美文网首页
17. Solidity:哈希运算、ABI编码解码

17. Solidity:哈希运算、ABI编码解码

作者: 泡泡龙吐泡泡 | 来源:发表于2023-10-08 08:10 被阅读0次

    17.1 哈希运算

    哈希函数(hash function)是一个密码学概念,它可以将任意长度的消息转换为一个固定长度的值,这个值也称作哈希(hash)。

    Hash的性质

    一个好的哈希函数应该具有以下几个特性:

    • 单向性:从输入的消息到它的哈希的正向运算简单且唯一确定,而反过来非常难,只能靠暴力枚举。
    • 灵敏性:输入的消息改变一点对它的哈希改变很大。
    • 高效性:从输入的消息到哈希的运算高效。
    • 均一性:每个哈希值被取到的概率应该基本相等。
    • 抗碰撞性:
      • 弱抗碰撞性:给定一个消息x,找到另一个消息x'使得hash(x) = hash(x')是困难的。
      • 强抗碰撞性:找到任意x和x',使得hash(x) = hash(x')是困难的。

    Hash的应用

    • 生成数据唯一标识
    • 加密签名
    • 安全加密

    Keccak256

    Keccak256函数是solidity中最常用的哈希函数,用法非常简单:

    哈希 = keccak256(数据);

    例子:

    contract HashContract {
        function hash(string memory text, uint num, address addr) external pure returns (bytes32) {
            return keccak256(abi.encodePacked(text, num, addr));
        }
    
        function collision(string memory text0, string memory text1) external pure returns (bytes32) {
            return keccak256(abi.encodePacked(text0, text1));
        }
    }
    

    代码使用encodePacked对输入的参数进行打包,encodePacked会对数据进行压缩,encode则会对参数进行补零。具体差异下一节进行介绍。
    需要注意的是,encodePacked由于是紧密拼接,可能会造成输入参数不同,输出hash值相同的情况,例如:

    对于collision()方法,输入参数为:AAA,BBB和输入参数为:AA,ABBB,哈希结果是相同的。本质上这并不是hash碰撞,而是由于encodePacked的编码特性造成的。但是在编写代码时要注意避免类似的错误发生。

    17.2 ABI编码解码

    Solidity中,ABI编码有4个函数:abi.encode, abi.encodePacked, abi.encodeWithSignature, abi.encodeWithSelector。而ABI解码有1个函数:abi.decode,用于解码abi.encode的数据。

    17.2.1 ABI编码

    定义以下数据,进行编码测试:

        uint public x;
        address public addr;
        uint[3] public arr;
    

    使用以下方法对上述参数进行初始化:

       function initialize(uint _x, address _addr, uint[3] memory _arr) external {
           x = _x;
           addr = _addr;
           arr = _arr;
       }
    

    在Remix中输入参数为:

    123,0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,[1,2,3]
    

    abi.encode

    将给定参数利用ABI规则编码。ABI被设计出来跟智能合约交互,他将每个参数填充为32字节的数据,并拼接在一起。如果你要和合约交互,你要用的就是abi.encode

        function encode() external view returns (bytes memory) {
            return abi.encode(x, addr, arr);
        }
    
    

    0x000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

    由于abi.encode将每个数据都填充为32字节,中间有很多0。

    abi.encodePacked

    将给定参数根据其所需最低空间编码。它类似 abi.encode,但是会把其中填充的很多0省略。当你想省空间,并且不与合约交互的时候,可以使用abi.encodePacked,例如算一些数据的hash时。

        function encodePacked() external view returns (bytes memory) {
            return abi.encodePacked(x, addr, arr);
        }
    

    注意:encodePacked不支持结构体类型。

    0x000000000000000000000000000000000000000000000000000000000000007b5b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

    abi.encodeWithSignature

    与abi.encode功能类似,只不过第一个参数为函数签名,比如"foo(uint256,address)"。当调用其他合约的时候可以使用。

        function encodeWithSignature() external view returns (bytes memory) {
            return abi.encodeWithSignature("foo(uint256,address,uint256[3])", x, addr, arr);
        }
    

    0xcde005b8000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

    等同于在abi.encode编码结果前加上了4字节的`函数选择器: 函数选择器就是通过函数名和参数进行签名处理(Keccak–Sha3)来标识函数,可以用于不同合约之间的函数调用。

    abi.encodeWithSelector

    与abi.encodeWithSignature功能类似,只不过第一个参数为函数选择器,为函数签名Keccak哈希的前4个字节。

        function encodeWithSelector() external view returns (bytes memory) {
            return abi.encodeWithSelector(bytes4(keccak256("foo(uint256,address,uint256[3])")), x, addr, arr);
        }
    

    0xcde005b8000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

    与abi.encodeWithSignature结果一样。

    17.2.2 ABI解码

    abi.decode

    abi.decode用于解码abi.encode生成的二进制编码,将它还原成原本的参数。

        function decode(bytes memory data) external pure returns (uint _x, address _addr, uint[3] memory _arr) {
            (_x, _addr, _arr) = abi.decode(data, (uint, address, uint[3]));
        }
    

    我们将abi.encode的二进制编码输入给decode,将解码出原来的参数:

    decode
    注意:
    1. 只能对encode编码的bytes进行解码。
    2. 必须知道数据结构类型才能进行解码。

    相关文章

      网友评论

          本文标题:17. Solidity:哈希运算、ABI编码解码

          本文链接:https://www.haomeiwen.com/subject/vjkcbdtx.html