八哥之神【番外篇7】

听说鲁豫要来采访稣

1. 稣,好久不见,最近都在忙什么?

项目忙,九九六一段时间,还趁机研究《孙子兵法》和密码学。由于一时忽视各位女朋友,现在她们都另寻新欢了。不过倒也清净,毕竟她们也老了,省得稣裁掉她们,还要 N+1,万一怀上,还得 2N……多烧钱呐!

2. 有读者问,为什么先写《八哥之神》正剧,再写前传?

其实这系列故事是好多年前就想好的,只是一直写代码,没空写故事。再说这个故事其实就是稣的梦,有很大跳跃性,哪有那么容易写好!

  • 你是说,这些故事已经存在很久,最近才写下来?

没错!这些真的都是梦,劣者的创作只是把每个梦连起来,让它们有逻辑而已。一个明显的套路就是剧中有很多吓醒故事,看似无关,其实都是营造气氛,预测故事走向。

3. 为什么写得这么难懂?

戒焦戒虑,勤思好学您就能看懂。主要因为这只是小说的草稿,就没打算让您懂,万一看懂,觉得故事太妙,到处被剽窃怎么办?稣的手稿在 9 年后将会十分值钱。哈。哦也。

4. 还会写后传吗?

那是必须的,等有经费就写!前传、后传,那都是商业套路,咳,现在主要工作还是写代码,多赚点钱。

5. 能简单介绍一下后传的框架吗?

19 年前佛祖转世谈恋爱谈恋爱去了,燃灯佛祖让位孙悟空。5 年前,玉帝也转世谈恋爱谈恋爱去了,太上道祖让位石敢当。9 年后,八哥之神转世谈恋爱谈恋爱谈恋爱去,八哥虫祖让位给稣。

  • 您是说……嗯哼??

没错!稣就是第八个无骨蠕虫。

  • 天呐!太震惊了!这是要改走神话路线??

没有!您不能先入为主呀!正剧已经说过“无神”,只是轮回而已。成神其实就是轮回转世,就是用嘴把人劝死,隐含作者一直推动安乐死合法化的决心。再次强调:没有神,只有神奇!

  • 您一再强调“无神”是怕年轻人迷信吗?

嗯,有这样的担心,另外还怕不明所以的读者对稣进行物理攻击!

  • 怎么会呢?难道您曾经……

举个例子吧!大学时,稣用“宇宙最大”做签名档,结果被很多人攻击,更有黑客跟踪稣多年,就是为了教育稣……其实“宇宙最大”表达的是“宇宙比人类的心大”,这是因为稣反对电视剧里说的“人心最大”,并非说自己是宇宙最大!

  • 哦,稍微有点明白啦!《八哥之神》也会有人理解为您自称是神?

yup!稣从来没说自己是神!只想表达“八哥”太特么神奇了!万一有神论者理解成“自称是神”,对稣痛下杀手,稣不是白死了?吓尿呀!

6. 您能再聊聊其它隐含意义吗?

明线一直都在强调“现实”从来不存在,这一切都是一个叫做“天道”的程序的运行结果。对意识来说,时间并非不可逆。

  • 这些太理论了,您能说说自然界或者社会方面的意义吗?

哦,也是有的!您看剧中大量六七十岁的角色和养老院的场景!

  • 嗯?您是说……

没错!中年危机不仅存在于程序员中!演员也是有的,老头老太能演青春偶像剧吗?

  • 所以,您是写了有很多老年人的剧本来拯救他们?

哦也,稣爱世人,包括老年人的!年轻人需要更多关爱老人,加强养老方面的物质以及精神建设。

7. 没想到稣有这么高尚的情怀!最后还是问点和剧情有关的吧!稣的前妻到底叫什么?

陈因提,外号砂砂。在天道程序里叫陈立姻和陈提姻是不同迭代的名字。

8. 据说《八哥之神前传》将要出现一名机器人美女?

嗯,堪称八哥系列最美最善解人意的。

  • 机器人最善解人意?

当然!人类有情绪,有三千烦恼。大道无情,但能创造有情的人类。有情未必真比无情好。

  • 好严肃呀!开个玩笑呗?

不好意思,笑不出来……写个诗吧!

圣人半个已操群,
耶稣佛祖我都信。
一生大坑避无数,
半脚踏入鬼门关。

  • 为什么半脚踏入鬼门关?

薛定谔的稣一直处于生与死、真与假的边缘。

  • 稣其实是个严肃的人嘛!

当然!如来佛祖、昊天上帝和八哥之神也都很严肃,严肃不妨碍我们谈恋爱。而且谈恋爱这事,还是严肃对待才好!结婚更好慎重!

  • 呵呵……是是是!不好意思,这么严肃的话题,我,我,居然,笑场了。

你开心就好。

ECDSA Node.js

前情

上篇《ECC Node.js》讲解椭圆曲线点的计算。本篇分析椭圆曲线签名算法。

代码

https://github.com/UMU618/secp256k1-tools

范例数据

已知,待签名数据为:

1
2
3
4
5
6
7
const data = Buffer.from(
// chainId
'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906'
// serializedTransaction
+ 'c0fbc75d000000000000000000000000'
// sha256 of serializedContextFreeData
+ '0000000000000000000000000000000000000000000000000000000000000000', 'hex')

运行 node ecc-sign.js,信息摘要为:

1
2
3
4
5
6
[
204, 24, 57, 178, 84, 129, 31, 104,
99, 30, 100, 210, 3, 38, 31, 168,
138, 248, 252, 131, 196, 14, 203, 152,
34, 152, 102, 149, 181, 94, 182, 148
]

签名为:

1
2
3
4
5
6
7
8
Uint8Array [
27, 36, 211, 214, 45, 20, 219, 85, 150, 70, 174,
229, 131, 173, 20, 61, 37, 129, 232, 80, 19, 164,
36, 249, 132, 56, 36, 74, 210, 34, 221, 98, 164,
68, 6, 237, 42, 240, 227, 212, 33, 105, 239, 200,
11, 59, 11, 148, 226, 85, 212, 106, 250, 155, 34,
25, 101, 69, 159, 138, 157, 114, 44, 38, 202
]

签名的字符串形式为:SIG_K1_Gg74ULRryVHxYZvMRLJgTrAZW6PZGC5SYfUiswtMJxBwfTTnGEnTejeWXopL2oSs8EZD7mqAC8mCps6VKq95Bgic9tGNHJ

分析

数值全部使用 16 进制表示。

  1. 范例使用的钥匙对

    • 签名私钥:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3

    • k = d2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d

    • 对应的公钥:EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

    • K = [c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf, eeceff7130fd352c698d2279967e2397f045479940bb4e7fb178fd9212fca8c0]

  2. 信息先用 sha256 算法计算摘要,范例中值为 h = cc1839b254811f68631e64d203261fa88af8fc83c40ecb9822986695b55eb694

  3. 签名数据一共 65 字节,第一个字节 [27] 是 recoveryParam,使用前要先减去 27,它的一个作用是区别 y 坐标值的奇偶性,后面是两个 256bit 数,分别记为 x、s,x 是随机私钥 r 在椭圆曲线上的点 rG 的 x 坐标值,s = (h + k * x) / r。

    • x = 24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4
    • s = 4406ed2af0e3d42169efc80b3b0b94e255d46afa9b221965459f8a9d722c26ca

    • 注意:elliptic 库把本文的 x 记为 r,为了和算法保持一致,UMU 没有采用 elliptic 的标识方式。

  4. 计算 rG = [24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4, bc336258d8f1789ad949773ef4abfe6a6e56c9dd77754e18869c7ab2801a4ae2]

1
2
3
4
5
6
7
8
9
10
11
const BN = require('bn.js')
const elliptic = require('elliptic')

const x = new BN('24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4', 16, 'be')
console.log('x =', x.toString(16))

// (27 - 27) & 1 是偶数,取偶数的 y
const p_even = elliptic.curves.secp256k1.curve.pointFromX(x, false)
console.log('y_even = ', p_even.getY().toString(16))
// const p_odd = elliptic.curves.secp256k1.curve.pointFromX(x, true)
// console.log('y_odd = ', p_odd.getY().toString(16))
  1. 计算 hG/s + x * K/s

    • u1 = h/s = b774bb6040cced0596626026679594b2b5478e6a5a8ba25b3411ed5360ea6bfa

    • u2 = x/s = 5697dfd4caab3caa0ed315a97f99f1ad7bce1ce85e0be32c63847d1dd4be327a

    • result = u1 G + u2 K = [24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4, bc336258d8f1789ad949773ef4abfe6a6e56c9dd77754e18869c7ab2801a4ae2],与 rG 一致,签名验证通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const BN = require('bn.js')
const elliptic = require('elliptic')

const k1 = elliptic.curves.secp256k1
const h = new BN('cc1839b254811f68631e64d203261fa88af8fc83c40ecb9822986695b55eb694', 16)
const s = new BN('4406ed2af0e3d42169efc80b3b0b94e255d46afa9b221965459f8a9d722c26ca', 16)
// u1 = h/s
const sinv = s.invm(k1.n)
const u1 = h.mul(sinv).umod(k1.n)
u1.toString(16)

const x = new BN('24d3d62d14db559646aee583ad143d2581e85013a424f98438244ad222dd62a4', 16)
// u2 = x/s
const u2 = x.mul(sinv).umod(k1.n)
u2.toString(16)

const k = new BN('d2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d', 16)
const K = k1.g.mul(k)
const result = k1.g.mulAdd(u1, K, u2) // k1.g.mul(u1).add(K.mul(u2))
result.getX().toString(16)
result.getY().toString(16)

数学原理

参考:椭圆曲线加密和签名算法

hG/s + xK/s = hG/s + x(kG)/s = (h + xk)G/s = r(h + xk)G / (h + kx) = rG

八哥之神前传【6】

2047 年,养老院

“死开,你知道大家为了你的毕生心愿付出多少努力吗?放弃抵抗吧!”

“砂砂,没想到你站他们那边!为了科研就可以草菅人命吗?这哪里是劣者的心愿!”

“长生不老,不是吗?你亲口和我说过,从小就有和孙悟空一样的长生想法,所以才喜欢看《西游记》!再说,我们也没有草菅人命,他们是自愿安乐死的。”

孙朝穆:“是呀,开哥,因提姐说的是事实,反正都是死,为科研贡献不是挺好的?”

“不!这有违契约精神……赫赫赫赫,在稣的平行宇宙中,没有人可以为非作歹,连稣自己都不行!”

贾力劣:“哦,那会怎么样?你都被我们抓起来了!”

“哼,一定会出现一个破局者!然后我们反败为胜,把你们统统抓起来关!”

“放开他!救世主来也!”

齐凤卿:“哇!真的有救兵?灯哥!”

“没错,就是我——督宇神照黄金灯大师是也!”

“大师!啊……”圣小开胸口中了一刀。

齐凤卿:“这……这,这难道是传说中的小灯飞刀?但是为什么射小开,有没有搞错?”

“是啊,为什么小灯飞刀是射稣?你有没有搞错?”

黄金灯:“稣,你心脏下方中刀,虽然一时不会毙命,但你再负隅顽抗,肯定很快失血而死。请积极配合治疗!”

稣:“好的,稣的命就交给大师了,快救命啊。”

2042 年,贾力劣别墅

贾力劣:“然后呢?你们怎么复活的,而且时间是怎么倒退五年的?”

圣小开:“这个劣者还没完全想起来。”

贾力劣:“可能只是你的梦而已,开哥别看太多科幻片,回头我送你一个美女机器人服侍你,包你满意!比你编这些狗血剧有意思多了!”

圣小开:“什么?你以为劣者老年痴呆呀?告诉你,劣者今天睡够八个小时的,比机器人还客观!你还想抵赖?”

贾力劣:“真不是我抵赖,就算你说的是真的,但是时间都倒退了,那也不算是我干的。你看我们现在这个世界不是一切都好好的?”

圣小开:“是哦!都撤销了……好吧,你的美女机器人赶紧送我,就当是赔礼!”

贾力劣:“一定一定!我的效率你放心,早就有所预备。”

圣小开:“不要耍花样,你要记住一句话!别人的记忆是连续的,而稣的,是离散的,量子化的!”

“什么?好深奥,不过我一定强行记忆……”贾力劣,心想:“好可怕呀,他好像快全部想起来了!”

学习 Rust【1】简化掉什么?

结论先行:从语法上说,Rust 基本无敌。

1. ++ 和 --

语言 有无 ++、-- 语法
C/C++/C#/Java
Go 只支持放变量后,不支持放变量前
Python/Rust/Scala

++、-- 一般是 +=、-= 的特例(除了 C++ 的迭代器),没有必要单独支持,新语言倾向于语法的单一性。

Python 的情况比较有意思,放后面是语法错误,放前面其实就是正负号,+ 写两次还是原来的数,- 写两次是负负得正,也还是原来的数。

2. 三目运算符(?:)

语言 有无 ?: 语法
C/C++/C#/Java/Swift
Go/Python/Rust/Scala

Rust 的 let = if else 就有 C 语言 ?: 的功能,即判断语句的子语句块可以有返回值。

3. 条件无需括号

语言 条件需不需要括号
C/C++/Java/Scala 需要
Go/Python/Rust/Swift 不需要

字符是能少打一个是一个,有效预防鼠标手。另外,Go 和 Rust 的语句块必须包含于 {}。

4. 异常处理

语言 异常处理机制
C/C++ 编译器扩展 try…except…finally, leave
C++/C#/Java/Scala/Swift throw, try…catch…finally
Python raise, try…except…else, try…finally
Go/Rust

5. 换行符(;)

语言 换行符
C/C++/C#/Java 必须
JavaScript/Scala/Swift 可选,有少数必须的情况
Python/Go
Rust 有是有,无是无(return),两者含义不同

Rust 有分号的是语句(statement),返回值是 (),即没有返回值。而没分号的是表达式(expression),返回值就是自身的值。

其实想说的是:有的 return 被简化掉了。省略 ; 就是省略 return,真香。但是,由于隐含 return,所以只能用于语句块的最后一行。

6. case 隐含 break

语言 case 是否隐含 break
C/C++/C#/Java 必须显式 break
Go/Rust/Swift 隐含 break

Rust 优秀在用 match 代替 switch,明确告诉大家这是新语法,而 Go/Swift 用 switch,却改变 case 行为,还多出一个 fallthrough 关键字,容易引起鲸神魂裂

诗盗·山坡羊·欠牛日生产基

《#诗盗#·山坡羊·欠牛日生产基》:房租两万,车租三千,研究花呗习了惯。宇督观,接国盘,学习不顾寒冬寒。一片天地景阑珊,穷,也是产,富,也是产。

注解

寒冬之下,同在一片天地,不管穷人富人,都要努力生产,不断奔跑才不会冻死。

欠牛日:软件园,字掉了。
生产基:生产者。
产:有时候也通“惨”。

诗盗·水龙头·无疑福报无尽天坑

《诗盗·水龙头·无疑福报》
鸡鸣风雨萧萧,断魂以翔,飘恨今宵。
极目乡关何处?几回好梦,不知昏晓。

《诗盗·水龙头·无尽天坑》
古来云海茫茫,八哥奇语,神明何在?
填尽九沟四坑,人间自有,挨踢风采。

注解

改编自“水龙吟”。

明,刘基,《水龙吟·鸡鸣风雨潇潇》

鸡鸣风雨萧萧,侧身天地无刘表。
啼鹃迸泪,落花飘恨,断魂飞绕。
月暗云霄,星沉烟水,角声清袅。
问登楼王粲、镜中白发,今宵又添都少?

极目乡关何处?渺青山,髻螺都校。
几回好梦,随风归去,被渠遮了。
宝瑟弦僵,玉笙簧冷,冥鸿天杪。
但侵阶落草,满庭绿树,不知昏晓。

宋,苏轼,《水龙吟·古来云海茫茫》

古来云海茫茫,道山绛阙知何处。
人间自有,赤城居士,龙蟠凤举。
清净无为,坐忘遗照,八篇奇语。
向玉霄东望,蓬莱暗霭,有云驾、骖风驭。

行尽九州四海,笑纷纷、落花飞絮。
临江一见,谪仙风采,无言心许。
八表神游,浩然相对,酒酣箕踞。
待垂天赋就,骑鲸路稳,约相将去。