基于 ECC 的私钥转为公钥的过程

基本知识

ECC 体系中,私钥是一个大型随机数,而公钥则是私钥乘以椭圆曲线上的基点后对应的点。

meet-one/private-to-public 是 MEET.ONE 开发的私钥转公钥工具。

EOS 支持的 EC 有两种:secp256k1(以下简称 k1)、secp256r1,下面以 k1 为例,结合 Node.js 和 Python 代码介绍转换过程。

涉及算法

  • BASE58:编解码私钥、公钥。
  • SHA-256:校验私钥,本文忽略此步。
  • ECC, secp256k1:计算公钥。
  • RIPEMD-160:校验公钥。

转换过程

1. 解码私钥

假设私钥为:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3,这是 base58 编码,用 bs58 库解码:

1
2
3
const bs = require('bs58')

bs.decode('5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3')

得到 <Buffer 80 d2 65 3f f7 cb b2 d8 ff 12 9a c2 7e f5 78 1c e6 8b 25 58 c4 1a 74 af 1f 2d dc a6 35 cb ee f0 7d aa 08 64 4a>,其中第 1 个字节 0x80 是类型,末尾的 4 字节是校验码。

这里我们不关心校验码,也可以直接用 bs58check 解码:

1
2
3
const bsc = require('bs58check')

bsc.decode('5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3')

得到 <Buffer 80 d2 65 3f f7 cb b2 d8 ff 12 9a c2 7e f5 78 1c e6 8b 25 58 c4 1a 74 af 1f 2d dc a6 35 cb ee f0 7d>,去掉首字节后为:

d2 65 3f f7 cb b2 d8 ff 12 9a c2 7e f5 78 1c e6 8b 25 58 c4 1a 74 af 1f 2d dc a6 35 cb ee f0 7d

这是 256bit 整数的 Big endian 字节流表示,转为 16 进制整形为 0xd2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d,记为 pk

2. 构造椭圆曲线,求公钥的坐标

根据 SECG 规定的 k1 的参数,我们用基于 Python 的 SAGE 构造 k1 对应的椭圆曲线,然后计算 pk * G

1
2
3
4
5
6
7
8
9
a = 0
b = 7
p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
E = EllipticCurve (GF(p), [a, b])
pk = 0xd2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d
G = E(Gx, Gy)
pk * G

结果为:(87237761414843254130560834629777710286905276524352264071298714336416392033743 : 108016191455113306196371645921919775466659772908675410052799661524790827329728 : 1)

注:这里的 (x : y : z) 是射影坐标,一般采用笛卡尔坐标系表示,为 (x/z, y/z)。

3. 编码公钥

取 x 值:87237761414843254130560834629777710286905276524352264071298714336416392033743

16 进制为:0xc0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf

Big endian 字节流表示为:[0xc0, 0xde, 0xd2, 0xbc, 0x1f, 0x13, 0x05, 0xfb, 0x0f, 0xaa, 0xc5, 0xe6, 0xc0, 0x3e, 0xe3, 0xa1, 0x92, 0x42, 0x34, 0x98, 0x54, 0x27, 0xb6, 0x16, 0x7c, 0xa5, 0x69, 0xd1, 0x3d, 0xf4, 0x35, 0xcf]

由于 y 值是偶数,所以添加一个前缀 2,得到:[2, 0xc0, 0xde, 0xd2, 0xbc, 0x1f, 0x13, 0x05, 0xfb, 0x0f, 0xaa, 0xc5, 0xe6, 0xc0, 0x3e, 0xe3, 0xa1, 0x92, 0x42, 0x34, 0x98, 0x54, 0x27, 0xb6, 0x16, 0x7c, 0xa5, 0x69, 0xd1, 0x3d, 0xf4, 0x35, 0xcf]

注:若 y 为奇数,则前缀为 3。

注:为什么这么规定?只用 x 值和 y 值的奇偶性表示一个点,这叫公钥的压缩格式。因为只要有 x,就可以通过 k1 的椭圆曲线方程式 $y^2 = x^3 + 7 \mod p$ 求出 y,但此时 y 会有两个解,又由于 p 是一个大质数,必定为奇数,故两个 y 解的和 mod p 一定等于 0,即一奇一偶,所以用 x 和 y 的奇偶性标志即可代表这个点。

接着求校验码:

1
2
3
const ripemd160 = require('ripemd160')

new ripemd160().update(Buffer.from([2, 0xc0, 0xde, 0xd2, 0xbc, 0x1f, 0x13, 0x05, 0xfb, 0x0f, 0xaa, 0xc5, 0xe6, 0xc0, 0x3e, 0xe3, 0xa1, 0x92, 0x42, 0x34, 0x98, 0x54, 0x27, 0xb6, 0x16, 0x7c, 0xa5, 0x69, 0xd1, 0x3d, 0xf4, 0x35, 0xcf])).digest()

得到:<Buffer eb 05 f9 d2 c6 dd 62 f7 f2 a0 f7 61 ea 1d 8c 0b 84 4a 3b 52>,取前 4 字节,添加到末尾,得到:[2, 0xc0, 0xde, 0xd2, 0xbc, 0x1f, 0x13, 0x05, 0xfb, 0x0f, 0xaa, 0xc5, 0xe6, 0xc0, 0x3e, 0xe3, 0xa1, 0x92, 0x42, 0x34, 0x98, 0x54, 0x27, 0xb6, 0x16, 0x7c, 0xa5, 0x69, 0xd1, 0x3d, 0xf4, 0x35, 0xcf, 0xeb, 0x05, 0xf9, 0xd2]

然后,base58 编码:

1
2
3
const bs = require('bs58')

bs.encode(Buffer.from([2, 0xc0, 0xde, 0xd2, 0xbc, 0x1f, 0x13, 0x05, 0xfb, 0x0f, 0xaa, 0xc5, 0xe6, 0xc0, 0x3e, 0xe3, 0xa1, 0x92, 0x42, 0x34, 0x98, 0x54, 0x27, 0xb6, 0x16, 0x7c, 0xa5, 0x69, 0xd1, 0x3d, 0xf4, 0x35, 0xcf, 0xeb, 0x05, 0xf9, 0xd2]))

得到:6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV,加上前缀 EOS,即为公钥。

诗盗·梦神

《#诗盗#·梦神》:浮生不与红尘谋,梦变鲲鹏立云洲。黑白无常一吓醒,生死轮回两宇宙。

注解

今日高温,午睡时突然忆起前几天的怪梦。平时不关心红尘凡事,日子平淡无奇,但梦境却实在很神奇,仿佛另一个世界。

自己漂浮在云层里,看到鲲鹏的翅膀在摇动,感觉还挺萌的,然后却发现翅膀是从自己身上长出去的……原来自己就是那只鲲鹏!

龙卷风把海水吸到云层里,看起来很像鲲化鹏,这两种形态的转化很快,梦的转变也很快,黑色的梦境,吓醒后是白天,我又从鲲鹏变回自己,就像穿梭在两个平行宇宙。

前两句改编自霹雳角色“道锋天扇子”出场诗:

浮生寄墟丘,不与红尘谋,身披烟波立云舟。
一扇擎青穹,飘洒翳孤踪,梦变鲲鹏振长空。

诗盗·无欲则刚

《#诗盗#·无欲则刚》:平生默默心如冰,含情一待苦似病。暝色高楼晚回愁,百万豪车无处停。

注解

平时淡然处世,心思平静,一旦动情就会得相思病,这都是因为有不切实际的欲望。

夜色已晚,开着百万豪车回家,因为楼太高,地下车位不够用,太晚回都找不到车位,如果是乐射车就放心随便停路边了,所以说——有点钱又不够有钱,是最难受的。

总之,还是没心没肺才能过得好!(我宁愿有钱、有心、有肺……)

改编自唐代诗人李白的《菩萨蛮·闺情》:

平林漠漠烟如织,寒山一带伤心碧。
暝色入高楼,有人楼上愁。
玉阶空伫立,宿鸟归飞急。
何处是归程?长亭更短亭。

八哥之神前传【1】

2042 年,AVILab

一日晚,齐凤卿带着宠物找周老师释疑。

“周老师,委员会一旦把识界系统超级控制权限拆分管理,必将阻碍科研效率。”

“凤卿,识界硬件和软件层面已经很完善,现在需要让它自由发展,我们后期侧重点应该放在接入系统。”

“这就是我想说的,等我们接入系统也完善后,人人都可以把意识上传到识界里生活,到时候识界内部的社会形态如果与现在的科学主义社会不同,我们如何更好地去改善呢?识界可是比当今世界更复杂。”

“你想使用特权改变识界?”

“没错,我们现在生活的地下城比起识界里的自然环境差太多,而且如果情况继续恶化,独立可控核聚变供电的识界才是人类最佳归处。我希望在识界里,我们还有特权,才能不受各种潜在危害,安心做科研。”

“这事情先不用操心,接入技术需要五、六年才会成熟,还早呢!老师 95 岁的人都不担心,你担心啥?”

“我担心的是委员会成员多数不希望我们在识界里扮演神灵,到时候我们和识界自然演绎出来的人类没有任何差别,岂不是像被关进监狱?”

“哈哈哈,你师弟不是经常说在这里像坐牢?这个宇宙,哪里不是牢笼?”

“是啊,可是那家伙明明有和我一样的担忧,却投票支持不保留特权!”

“他和薛雾霰曾经把时间停止,你知道吗?”

“识界吗?这不是很正常的?”

“不是,是现实世界。”

“嗯?时间停止,我们的观察、思考也都停止,怎么发现到底停止没?”

“时间停止时,只有引力波还在起作用。”

“饶了我吧,这不是我研究的领域。我还是关心识界以后的管理问题。”

“以后就自治吧,我们不应该干预自然规则,就像在现实世界,我们只能利用自然规则,部分改造我们的生存环境,而不能违逆规则。”

“既然如此,我认为应该减少识界里的人类,让其更像我们现在的人口状况。”

“你想干什么?”

“我之前一直在识界里培养我的信徒和菌队,是时候来一场识界大战了。”

医院

周易被猫科动物咬伤濒死。陈立姻和孙朝穆为周易做换脑手术。

试玉要烧三日满,辨材须待七年期。向使当初身便死,一生真伪复谁知。

识界

这个世界为游戏而生,现实的开发者为创造真实的场景,曾经用特权给识界某些公司输送资源,从而构造了强大的菌事基地。又用特权控制和改造某些关键人物的思想,达到统治的目的。

齐凤卿用自己的基因在识界克隆了一个替身,但替身并没有他的意识,无法与他心灵相通,这让他一直努力推动意识复制技术。

齐凤卿抢夺周易的特权,远离 AVILab,在神秘人物薛雾霰家的游戏机,通过特权进入识界。

薛雾霰:雾草,开哥你这是什么安排?本霰人上天探索,别墅交你们打理,怎么被用来搞黑客入侵了??

圣小开:因为你是量子领域的专家,住所隐秘,适合干坏事。放心!保证完好无缺。

识界正上演消灭脑残玩家的大战。齐凤卿教唆现实中几名游戏玩家开启上帝视角协助识界中的齐家菌疯狂杀戮识界生灵。

救世主出场

耶乎知稣、心理邪稣、量子邪稣和刘佾,三人牛逼轰轰地降临,开始反杀齐家菌。

很快齐凤卿发现异常,打死耶乎知稣,心理邪稣定位到齐凤卿,猛力攻击。

齐凤卿化身为魔凤,一口吃掉心理邪稣。

魔凤是金色的,不要以为是黑色……

心理邪稣的遗言:“稣有个秘密要告诉你!你大学时的女朋友……”。

地面自动升起一块墓碑,刻着:圣小开之墓。

量子邪稣一看心惊胆战,内牛满面,再观天上的魔凤变幻出多种怪异形状,赶紧对刘佾说:“不妙,快跑路!”

刘佾:“跑?怎么能见死不救?”

量子邪稣:“自己都快被打死了,还救别人?”

刘佾:“没事,我们被打死还能回现实,重新选择角色,再进来。但是这里生灵涂炭,于心何忍?为苍生拼了!”

量子邪稣:“别傻了,咱们再来几次也打不赢他,被打死的不一样是识界里的人?”

刘佾:“我也开外挂,不信打不赢!”

刘佾化身八翼应龙,大战魔凤,依然不敌被拍落地面。

量子邪稣:“稣就说打不赢吧!你没发现那只魔凤是四维的吗?他现在可以轻而易举地捏碎稣的蛋,而且不伤蛋壳。咱们打不过他的。”

识界设计时,使用四维坐标系,只有特权程序可以把第四维设置为非零。

刘佾:“你现在才说……咱们现在没有蛋!还有,你为什么不放大招?”

量子邪稣:“这不是没证据吗?你又不听稣的!大招也不一定能赢。赶紧投降,和他讲交情,稣继续隐藏在量子领域,不被观测。”

刘佾:“讲交情有用?”

量子邪稣:“你们读研时不是男女朋友吗?马上投降,赞同他的理念先!”

刘佾:“臣妾做不到呀!”

量子邪稣:“你这个,圣,咳,母婊!稣来!藏在你身上就是为了飙演技……”

刘佾:“臭师弟,你骂谁呢!”

量子邪佾:“哥,我投降,你是对的,这些傻逼应该消灭!”

魔凤:“圣小开还有好几个分身在哪里?别以为我不知道他是精神分裂!让我连入你的意识,把他找出来!”

量子邪佾:“哥,连我也没用,他又没和我联机。”

魔凤从第四维靠近量子邪佾:“骗谁呢?我插!”

意识世界

量子邪稣:“师兄!”

齐凤卿:“嗯哼?原来你在这里!领死,还是归降?”

量子邪稣:“师兄此言差矣!在这里,你根本不是稣的对手!”

齐凤卿:“但我可以控制物理世界的魔凤,毁灭你的肉身。识界里有特权的角色已所剩无几,最终你们无法阻止我。”

量子邪稣:“这不是挺好的嘛!稣本来就赞同你的做法,只是这罪名要你担而已,稣轻轻松松躺赢。”

齐凤卿:“赫赫,你这小子,果然心机深沉,但是好人有这么好当的吗?受死吧!”

量子邪稣:“等等,稣啥时候说过自己是好人啦?邪稣的邪字放在前面呢!”

齐凤卿:“装模作样!消失吧!”

刘佾:“喂喂,不要把我一起打死啊……你们又没有必然的矛盾,为啥不合作?”

量子邪稣:“就是,你就背个黑锅,假装被稣打死,稣回去让委员会判你几年有期徒刑完事,这不是双赢吗?”

齐凤卿:“赫赫,鬼算盘打得很精,好事都你占?凭什么我倒霉!”

刘佾:“哥,你的宠物咬死周老师,这是大罪。还是收手吧!”

齐凤卿:“确定死了?”

量子邪稣:“不确定,我们进来时还在抢救,用的是换脑术,取决于脑是否能活下来。你和我们一起出去和平解决,这里的战争,咱们就当一个八哥。”

齐凤卿:“我已经下令释放心灵生化武器,再打死你们,我就出去!”

识界物理世界

魔凤将量子邪佾打爆。然而,同一瞬间阎无邪手持冥邪天锋化现,地狱恶灵纷纷缠住魔凤,无邪顺势一剑砍下凤头。

但是,魔凤的头断并不致命,继续长出其它的头。无邪酱油退场。

心灵生化武器被释放,开启一场无声无息的众生苦难。整个识界自杀率大大提升……

现实世界

然而,在现实世界,齐凤卿不能上天入地,还是被抓起来了。赫赫!

齐凤卿:“这场游戏打得真爽!”

智能合约替用户承担事务的开销

需求

用户可能因为资源(NET 和 CPU)匮乏,无法愉快地使用 EOS 智能合约。

解决思路

智能合约承担事务的开销(NET 和 CPU)。

具体方案

1. ONLY_BILL_FIRST_AUTHORIZER 特性

eos 1.8 的 ONLY_BILL_FIRST_AUTHORIZER 特性,通过只向事务的首个授权方收费的方式,部分地解决这个问题。这一特性允许应用提供者对用户的每一笔事务进行联合签名,通过这一方式从公共池中支付事务的开销。

缺陷:联合签名操作门槛高,安全性堪忧。

2. 合约调用 accept_charges

Contracts Paying Transaction Costs 提出一种无需联合签名的方法(注意:目前还只是提案,尚未实现)。

关键函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool accept_charges(
uint32_t max_net_usage_words, // Maximum NET usage to charge
uint32_t max_cpu_usage_ms // Maximum CPU usage to charge
);

void get_accept_charges(
name* contract,
uint32_t* max_net_usage_words,
uint32_t* max_cpu_usage_ms,
);

void get_num_actions(
uint32_t* num_actions,
uint32_t* num_context_free_actions,
);

简单地说,在合约调用 accept_charges 函数,如返回 true,则事务的开销将会由合约账户承担。

具体规则如下:

  • 如果多个合约调用了 accept_charges,则首个调用者会被收取费用。accept_charges 会返回 true 给该合约,而返回 false 值给其他的合约。

  • 如果首个合约调用了多次该函数(用于修改限制),无论是在相同的 action 还是不同的多个 action 之中,每次都会返回 true。

关键点讲完了,其余请参考 Contracts Paying Transaction Costs 原文或译文

诗盗·今年七夕不送礼送礼只送学区房吓醒

《#诗盗#·今年七夕不送礼送礼只送学区房吓醒》:一年三度情人谋,先虐情侣后虐狗。今日没钱空回首,粗茶淡饭再买楼。

注解

商家为了赚钱,一年催大家过三次情人节,堪比谋杀……先虐情侣的钱包,再通过情侣秀恩爱虐单身狗。
今天想日却发现没钱,只能回首空悲切,过几年艰苦的日子后再考虑买学区房吧。

改编自唐代诗人罗隐的《绵谷回寄蔡氏昆仲》:

一年两度锦城游,前值东风后值秋。
芳草有情皆碍马,好云无处不遮楼。
山将别恨和心断,水带离声入梦流。
今日因君试回首,淡烟乔木隔绵州。

优化思维【4】Bash for 循环

常规教材

Bash 循环有三种写法。

1. 类 C 语言语法

1
2
3
4
5

for ((i = 1; i <= 10000000; ++i))
do
echo $i
done

2. for in 语法

1
2
3
4
for i in {1..10000000}
do
echo $i
done

3. 使用外部命令 seq

1
2
3
4
for i in `seq 1 10000000`
do
echo $i
done

分析

这三种方法中,性能最好的是第一种,最差的是第三种。

方法 1 是语言层面的循环语法,循环会立刻开始,而后两种,作为对比,并不会立刻开始!

方法 2 中的 {1..10000000} 会产生一个 1 到 10000000 的序列,然后再开始循环。

方法 3 中的 seq 1 10000000 是调用 seq 产生一个 1 到 10000000 的序列,然后再开始循环。涉及到外部进程调用和管道传递,所以比方法 2 更慢。

总结

  • 方法 1 写起来最麻烦,性能却是最好的。“做一件事,有很多种方式”,有时候不是好事,有对比,就有伤害……优化往往是和人性作对!

  • 语言原生的方法一般比调用外部命令好。

同类经验

Java 的 for 和 while 本质上一样(其实 while 可以去掉),而同是 JVM 语言的 Scala 的 for 却不同于 while。Scala 的 for <- 也会先产生序列,再循环,所以超大规模循环时,for 性能不如 while。

刽子手

刽子手死后下地狱,阎王判他转世为畜。

刽子手不服,问:“我为正义执法,难道不是有功德!其他的刽子手都这么判?”

阎王:“多数被你执杀的人投诉你技术不好,导致他们没迅速死亡。其他技术好的刽子手没大量投诉,我判他们投胎为人。”

刽子手还是不服,辩道:“他们不是该死吗?怎么死还有差别?”

阎王:“他们是该死,被判死刑已经付出代价。但你身居司法机构,技术不好,在阳间没人投诉你,导致你从来不改进,在我阴间你要付出代价。”