首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >abi.encodePacked()产生与abi.encodeWithSignature()不同的结果

abi.encodePacked()产生与abi.encodeWithSignature()不同的结果
EN

Ethereum用户
提问于 2022-07-23 10:07:56
回答 1查看 562关注 0票数 0

我在扮演伊瑟诺守门二号:

代码语言:javascript
复制
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6.0;

import "forge-std/Test.sol";

contract GatekeeperTwo is Test {
    address public entrant;

    modifier gateOne() {
        require(msg.sender != tx.origin, "err1");
        _;
    }

    modifier gateTwo() {
        uint x;
        assembly {
            x := extcodesize(caller())
        }
        require(x == 0, "err2");
        _;
    }

    modifier gateThree(bytes8 _gateKey) {
        emit log_named_uint("part1", uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))));

        require(
            uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^
                uint64(_gateKey) ==
                uint64(0) - 1, "err3"
        );
        _;
    }

    function enter(bytes8 _gateKey)
        public
        gateOne
        gateTwo
        gateThree(_gateKey)
        returns (bool)
    {
        entrant = tx.origin;
        return true;
    }
}

contract Attack is Test {
    constructor() public {
        GatekeeperTwo gk = new GatekeeperTwo();
        address g = address(gk);
        
        bytes4 sig = bytes4(keccak256("enter(bytes8)"));
        log_named_bytes("first method",  abi.encode(sig, key()));
        log_named_bytes("second method", abi.encodePacked(sig, key()));
        log_named_bytes("third method",  abi.encodeWithSignature("enter(bytes8)", key()));

        (bool success, ) = g.call(abi.encodePacked(sig, key()));
        require(success);
    }

    function key() private returns (bytes8) {
        uint64 val = uint64(bytes8(keccak256(abi.encodePacked(address(this))))) ^
            (type(uint64).max);
        return bytes8(val);
    }
}

contract ContractTest is Test {
    function setUp() public {}

    function testExample() public {
        new Attack();
    }
}

在我的测试代码中,这三行

代码语言:javascript
复制
log_named_bytes("first method",  abi.encode(sig, key()));
log_named_bytes("second method", abi.encodePacked(sig, key()));
log_named_bytes("third method",  abi.encodeWithSignature("enter(bytes8)", key()));

产生不同的输出:

代码语言:javascript
复制
Logs:
  first method: 0x3370204e00000000000000000000000000000000000000000000000000000000f719f96375b9c994000000000000000000000000000000000000000000000000
  second method: 0x3370204ef719f96375b9c994
  third method: 0x3370204ef719f96375b9c994000000000000000000000000000000000000000000000000

而只有第三个生成正确的调用数据,并打破了水平。为什么其他两个人不能工作?

EN

回答 1

Ethereum用户

回答已采纳

发布于 2022-07-23 10:21:12

契约函数调用需要一个bytes4函数选择器以及abi编码的参数(每个填充为32个字节)。

代码语言:javascript
复制
abi.encode(sig, key());

pads,sig (bytes4)和key() (bytes8)都是32字节。因为sig被视为bytes32,因此会产生不正确的编码。

代码语言:javascript
复制
abi.encodePacked(sig, key());

sig (bytes4)和key() (bytes8)紧密地打包,而不需要任何额外的填充。因为key()格式不正确(bytes32),所以这是不正确的编码。

代码语言:javascript
复制
abi.encodeWithSelector(sig, key());

返回正确的编码,bytes4 sig,以及32字节的填充值key()

以下内容相当于:

代码语言:javascript
复制
abi.encodeWithSelector(sig, key());
abi.encodeWithSignature("enter(bytes8)", key());
abi.encodePacked(sig, abi.encode(key()));
票数 2
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/132270

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档