web3, secp256k1签名与Solidity签名
现在个好像越来约多Dapp用到链下签名,可以设计多步骤、需要不同私钥签署同意之后一起上链给智能合约验证执行。其中有名的例子包含了许多去中心化交易所使用的0x Protocal,个人觉得是个非常聪明的设计,这里就记录一下自己试着用web3玩玩链下签名的心得。
web3 签名
其实虽说签名的过程就是把一段讯息加上私钥进行ECDSA签张,但其实在Ethereum世界里的签名还加了一个小规则,就是要在要签名的message在Hash之前,还要在前面加上一小段prefix:
message= "\x19Ethereum Signed Message:\n" + message.length + message
在web3提供好给我们的sign函式(web3.eth.accounts.sign)当中,就已经包含了上述步骤。直接看web3.eth.accounts.sign程式码比较好懂:
GitHub连结
简单的使用方法如下:把要进行签名的string (orderHash)直接连同privateKey丢进函示就好。
其实是丢进去签名的orderHash要不要先转换成为bytes都可以,出来的结果会是相同的。sign函数回传的结果会包含message、messageHash以及rsv三个椭圆签名结果。其中message是原来我想要签名的内容(orderHash),messageHash则是程式中自动帮我们加上prefix,并且进行Sha3 Hash的结果,也就是真正被拿去用私钥签名的一段Hash值。
简而言之web3什么都帮你做好了,不要像我一样傻傻的自己想办法加prefix最后才发现做了两次。
secp256k1 签名
那么如果我们想要单纯用私钥签名一段资料,不要有Ethereum定义的那些prefix的话,就必须要直接调用secp256k1这一包library了。不过在用之前要知道,所有要丢给secp256k1签名的message,长度都必须是256 bits,也就是32 bytes。刚刚我们说web3的签名函式丢什么都可以,是因为它会帮我加上prefix之后再做sha3 Hash (keccak),最后一定会变成一个32 bytes的东西。如果我们自己纯靠私要签名讯息的话,也势必要先通过这个函式来整理input长度。我在这里举个例子,手动作上面web3的所有要丢给secp256k1签名的message,长度都必须是256 bits帮我们包好的流程,也就是自己以符合Ethereum协议的方法做一遍,比较方便我们验证结果。也就是32 bytes。刚刚我们说web3的签名函式丢什么都可以,是因为它会帮我加上prefix之后再做sha3 Hash (keccak),最后一定会变成一个32 bytes的东西。如果我们自己纯靠私要签名信息的话,也势必要先通过这个函式来整理input长度。我在这里举个例子,手动作上面web3的sign帮我们包好的流程,也就是自己以符合Ethereum协议的方法做一遍,比较方便我们验证结果。
所以一开始我们可以透过soliditySha3来把prefix跟orderHash混在一起然后进行hash,这一段的结果会跟上面产出的messageHash相同,也等同于在Solidity里面使用keccak:
keccak256("\x19Ethereum Signed Message:\n32", hash)
得到这串「要签名的hash」之后,在丢进secp256k1之前,要先转成bytes (长度会为32),存入buffer,然后才能进行签名。若是直接用string的话,会发生message length is invalid的错误。同理,用来签名的privateKey也要转换成Buffer才行。
使用secp256k1回传的物件里还需要自己解析出r,s,v三个元素,不过我是直接复制贴上web3里面包的做法。
所以说,如果自己使用secp256k1来签名的话,可以略过加上prefix那一段,未来在智能合约上验章也可以少一段,不过还是需要使用到keccak来进行杂凑就是了。
Solidity 验章
好不容易签好章当然就是要来线上验章了。Solidity上面验章很简单,只要使用ercrecover这个function就可以了。我们让hash是一个bytes32的数,套用我们前面的例子,就是最原始的orderHash值。而v,r,s则是签名结果:
bytes32 hash uint8 v bytes32 r bytes32 s
那么下面这个函式就应该回传我们所用来签名的public Key。注意到这里会使用keccak256来把hash加上ETH规定的prefix ,我们很常可以在合约中看到这段文字,因为web3预设的签名就要这样来还原。当然,如果想要设计没有用prefix的,那么这一步就省了。
ecrecover( keccak256("\x19Ethereum Signed Message:\n32", hash), v, r, s );
可以试试看到我deploy的合约上直接call这两个函示玩玩看结果:
Ropsten地址:0x209ce2886420b27e497ce343e59574166400f1ab
Ropsten Accounts, Address and Contracts
The Ethereum BlockChain Explorer, API and Analytics Platform
ropsten.etherscan.io
小结
虽然整理下来很简单,但是我可是花了好多时间才豁然开朗原来web3这么鸡婆。总之感觉是很多人可能遇到的问题,就整理一波吧~
声明:本站所提供的资讯信息不代表任何投资暗示, 本站所发布文章仅代表个人观点,仅供参考。