目前,越来越多的Dapp开始采用链下签名技术。这种技术允许设计出多步骤、涉及多个私钥签署的过程,待所有同意签署完毕后,再将结果上链供智能合约验证并执行。其中,0x Protocol,一个被众多去中心化交易所采用的协议,堪称设计上的典范。个人认为,这是一个非常精妙的设计。以下,我将记录下自己尝试使用web3进行链下签名的心得。
web3签名尽管签名过程本质上是将一段信息与私钥结合进行ECDSA签名,但在Ethereum的世界中,还有一个额外的规则:在哈希签名信息之前,需要在其前面添加一小段前缀:
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以及r、s、v三个椭圆曲线签名结果。其中,message是原始的待签名内容(orderHash),而messageHash则是程序自动添加前缀并进行SHA3哈希的结果,也就是私钥真正签名的一段哈希值。
简而言之,web3已经为我们完成了所有工作,无需像我一样笨拙地手动添加prefix,最终发现多做了两次。
secp256k1签名如果我们想仅用私钥对一段信息进行签名,而不使用Ethereum定义的prefix,则需要直接调用secp256k1库。但在使用之前,要知道所有要给secp256k1签名的message,长度都必须是256 bits,即32 bytes。正如之前所述,web3的签名函数可以接受任何内容,因为它会在添加prefix并进行SHA3哈希后,最终将其转换为32 bytes。如果我们自己手动签名信息,也必须通过这个函数来调整input长度。以下是一个例子,展示了如何手动完成web3的签名流程,以方便我们验证结果:
首先,我们可以通过soliditySha3将prefix和orderHash混合在一起进行哈希,得到的结果将与前面产出的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函数即可。我们让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);您可以尝试在我的合约上直接调用这两个函数查看结果:Ropsten地址:0x209ce2886420b27e497ce343e59574166400f1ab
Ropsten Accounts, Address and Contracts The Ethereum BlockChain Explorer, API and Analytics Platform ropsten.etherscan.io 小结虽然总结起来很简单,但我确实花费了大量时间才恍然大悟,原来web3如此体贴。总之,这可能是很多人会遇到的问题,所以我整理了一些信息。
标签: 数字货币