这两天调试从事务签名中抽取公钥的程序,经过不断努力,总算可以实现这个功能了,但是当我测试以前的事务,却出现了奇怪的结果,绞尽脑汁也想不明白。
(图源 :pixabay)
从事务中的签名中恢复私钥的大致步骤如下:
- 读取事务JSON
- 保存事务的签名,并清空事务的签名
- 获取事务的摘要(不包含签名部分)
- 然后用签名(r,s)和摘要信息来恢复公钥
核心代码如下:
Sig = ecdsa.ecdsa.Signature(r, s)
digest_number = ecdsa.util.string_to_number(digest)
pubks = Sig.recover_public_keys(digest_number,ecdsa.SECP256k1.generator)
然后我们随便找个Transaction测试一下,比如这个:
调用程序后输出如下结果:
看了一下我的权限列表:
然而,别高兴得太早,当我尝试恢复如下事务的公钥时,却发现恢复出来的死活都不对
虽然也计算出来一个公钥,然而却不是我这个账户的任何一个公钥:
问题出在哪里呢?我首先想到的是不是我修改过密码或者私钥,拍脑袋无数次以及翻看历史记录后,我可以肯定说我没有修改过密码或者私钥!
再有就是是不是整个流程中有什么和时间有关的参数,我看了半天代码,实在看不出来和时间有什么关联。然而事实却真的是用在近期的事务上好用,用在久远一些的事务上就会算出错误的公钥。
想啊想啊,百思不得其解,昨天一直思索刀半夜也没想明白,然后关机睡觉。不过躺在床上的时候,我突然想到会不会是硬分叉改了什么东西影响到结果呢?
硬分叉改什么会影响结果呢?然后想了一下计算公钥的过程中用到的内容:事务本身、事务签名、事务的摘要(digest),而事务本身以及签名都是确定的,唯一有可能变化的就是摘要了,可是摘要为什么会变化呢?对了chain_id!
因为摘要首先是将去掉签名的事务序列话,然后在和chain_id串接后计算摘要信息:
hex = chain_id + hex[0:-2]
self.digest = hashlib.sha256(unhexlify(hex)).digest()
那么chain_id的变化当然会引起摘要的变化了,换句话说HF24之前的事务签名是用之前的chain_id来计算摘要并签名的,之后的则是用新的chain_id来计算摘要并签名,所以恢复时也要使用对应的chain_id。
再来用旧的chain_id计算一下上述事务的公钥:
这次算出来的没错,就是那么Active KEY:
也就是从程序实现在来讲,只要我们找到HF24发生的区块,那么之前块中的事务用旧的chain_id,之后用新的chain_id就好。不过我懒得改程序了,其实我只是想搞明白发生了什么而已。