美文网首页
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