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),
	)
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM