通常, ECDSA(椭圆曲线数字签名算法)包含两个参数, r 和 s. 在以太坊中签名包含第三个参数 v,它可以用于验证哪一个账号的私钥签署了这个消息。 Solidity 提供了一个内建函数 ecrecover 它接受 r, s and v 作为参数并且返回签名这的地址。
我们如何进行测试签名和验签呢;
签名,在线下使用web3库,对信息进行签名;获取到hash和signature;
解析signature,提取r,s,v;
调用,ecrecover 函数,返回签名的地址;
比较地址是否正确;
1、对消息签名
线下我们使用自己的私钥对消息进行签名,其实就是映射我们对交易进行签名;这里我是用的python的web3库做演示;签名地址为0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E;
从输出信息中我们可以找到信息hash,以及签名;
fromweb3.autoimportw3
frometh_account.messagesimportencode_defunct
#address = 0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E
msg="blockchain"
private_key=b"\xb2\\}\xb3\x1f\xee\xd9\x12''\xbf\t9\xdcv\x9a\x96VK-\xe4\xc4rm\x03[6\xec\xf1\xe5\xb3d"
message=encode_defunct(text=msg)
signed_message=w3.eth.account.sign_message(message,private_key=private_key)
print(signed_message)
输出信息:
SignedMessage(messageHash=HexBytes('0x9d193b650938d0c77baf2b825d0fea75d923a0aaa84221c5fd847a6659a10c9a'), r=11654359695283894998368058620483652058172288253605713014912952658261322451562, s=31697021223068352279943535821783285805623410722626389131972335850026147729781, v=27, signature=HexBytes('0x19c42242a4054fdcf157e3103454f0fd90de5a4a327a0748b19d4bc19c774a6a4613e0d4ca6e80cd54990f8f42310638d1376fb24e1b494beed4df53b2e751751b'))
2、提取签名参数
签名 是由r, s 和 v 是连接在一起组成的,各部分分离出来。实现就仅仅需要一个参数而不是三个参数,我们在 splitSignature 函数里使用 内联汇编 来完成这个工作。
assembly{}为solidity设置的内联汇编语言,用于以一种底层方式访问EVM虚拟机; := 是内联汇编语言语法,且solidity支持return多个返回值,用括号括起来即可;mload()是内联汇编语言中的操作码,类似于封装好的函数直接调用即可,mload(p)表示mem[p...(p+32)];mem[a...b)表示将位置a到位置b的memory字节内容分离出来,add(a,b)表示a+b
function splitSignature(bytes memory sig) internal purereturns(uint8 v,bytes32 r,bytes32 s){
require(sig.length== 65);
assembly{
r:=mload(add(sig,32))
s:=mload(add(sig,64))
v:=byte(0,mload(add(sig,96)))
}
return (v,r,s);
}
3、提取签名地址
从椭圆曲线签名中恢复与公钥关联的地址,错误返回零,ecrecover(bytes32 hash,uint8 v,bytes32 r,bytes32 s)returns (address)
function recoverSigner(bytes32 message,bytes memory sig)internal pure returns(address){
(uint8v,bytes32 r,bytes32 s) = splitSignature(sig);
return ecrecover(message,v,r,s);
}
从recoverSigner的返回值中,我们可以看到返回的地址,就是我签名使用的地址;