solidity“abi.encode/abi.encodePacked”使用golang编码
uniswap v2 erc20合约中有一个预授权功能,也就是链下签名链上验证,授权方法如下:
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
_approve(owner, spender, value);
}
里面在对签名数据进行编码时用到了abi.encode/abi.encodePacked两种方法,查阅solitidy文档得知两种方法区别在于:abi.encode 编码的数据需要32字节对齐,abi.encodePacked对小于32字节的数据不补0,以及其他一些区别:https://solidity.readthedocs.io/en/v0.7.3/abi-spec.html#abi-packed-mode
使用golang实现如下
// hash of packed byte array with arguments
hash := crypto.Keccak256Hash(
common.HexToAddress("0x0000000000000000000000000000000000000000").Bytes(),
[32]byte{'I','D','1'},
common.LeftPadBytes(big.NewInt(42).Bytes(), 32),// 不足32字节的补齐
[]byte("Some other string value"),//最后的参数不需要补齐
)
// normally we sign prefixed hash
// as in solidity with `ECDSA.toEthSignedMessageHash`
prefixedHash := crypto.Keccak256Hash(
[]byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%v", len(hash))),//不足32字节 不补齐
hash.Bytes(),
)
最后,使用golang写了一个UNISWAP合约中相关计算的例子。
const (
// keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)')
EIP712DomainHash = "0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f"
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"
)
func calcDigest(verifyingContract, owner, spender ethcmn.Address, value *big.Int, nonce, deadline int64) ethcmn.Hash {
return crypto.Keccak256Hash(
[]byte("\x19\x01"),
calcDomainSeparatorHash(verifyingContract).Bytes(),
calcDigestPermitFuncHash(owner, spender, value, nonce, deadline).Bytes(),
)
}
func TestCalcDigest(t *testing.T) {
digest := calcDigest(ethcmn.HexToAddress("0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"),
ethcmn.HexToAddress("0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"),
ethcmn.HexToAddress("0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"),
big.NewInt(123),
1,
1600000000)
fmt.Println(digest.Hex())
}
func calcDomainSeparatorHash(verifyingContract ethcmn.Address) ethcmn.Hash {
nameHash := crypto.Keccak256Hash([]byte("Uniswap V2")) //0xbfcc8ef98ffbf7b6c3fec7bf5185b566b9863e35a9d83acd49ad6824b5969738
versionHash := crypto.Keccak256Hash([]byte("1")) //0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6
chainId := big.NewInt(1).Bytes()
return crypto.Keccak256Hash(
ethcmn.HexToHash(EIP712DomainHash).Bytes(),
nameHash.Bytes(),
versionHash.Bytes(),
ethcmn.LeftPadBytes(chainId, 32),
ethcmn.LeftPadBytes(verifyingContract.Bytes(), 32),
)
}
func calcDigestPermitFuncHash(owner, spender ethcmn.Address, value *big.Int, nonce int64, deadline int64) ethcmn.Hash {
return crypto.Keccak256Hash(
ethcmn.HexToHash(PERMIT_TYPEHASH).Bytes(),
ethcmn.LeftPadBytes(owner.Bytes(), 32),
ethcmn.LeftPadBytes(spender.Bytes(), 32),
ethcmn.LeftPadBytes(value.Bytes(), 32),
ethcmn.LeftPadBytes(big.NewInt(nonce).Bytes(), 32),
ethcmn.LeftPadBytes(big.NewInt(deadline).Bytes(), 32),
)
}
posted on 2020-10-21 15:40 angry-baby 阅读(6244) 评论(0) 编辑 收藏 举报