首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Yul /内联程序集中为调用()、委托所有()等设置数据?

在Yul /内联程序集中为调用()、委托所有()等设置数据?
EN

Ethereum用户
提问于 2022-03-25 02:07:02
回答 1查看 1.4K关注 0票数 0

似乎几乎所有外部调用的示例都是简单的代理,它们执行如下操作:

代码语言:javascript
复制
    assembly {
        // Copy msg.data. We take full control of memory in this inline assembly
        // block because it will not return to Solidity code. We overwrite the
        // Solidity scratch pad at memory position 0.
        calldatacopy(0, 0, calldatasize())

        // Call the implementation.
        // out and outsize are 0 because we don't know the size yet.
        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

        // ...
    }

我真正纠结的是将任意数据发送到call() / delegatecall(),而这些示例实际上无助于我的理解,因为这些调用数据是使用方便的calldata函数自动转发到调用中的,而无需对内存或堆栈进行任何干扰。我还认为,如果以前分配了一些内存,则此方法无法工作,因为这将覆盖以前在偏移量0处的任何内容。

查看evm.codes,我们看到以下是delegatecall()的参数:

代码语言:javascript
复制
1. gas: amount of gas to send to the sub context to execute. The gas that is not used by the sub context is returned to this one.
2. address: the account which code to execute.
3. argsOffset: byte offset in the memory in bytes, the calldata of the sub context.
4. argsSize: byte size to copy (size of the calldata).
5. retOffset: byte offset in the memory in bytes, where to store the return data of the sub context.
6. retSize: byte size to copy (size of the return data).

(3)和(4)是我在这里关注的那些写入任意数据。

假设我想在implementation合同上调用这个函数:

代码语言:javascript
复制
myFunction(bytes4,uint256) => 0x7e965aeb

使用适当的调用数据。

正如我所理解的(3) byte offset in the memory in bytes, the calldata of the sub context,这意味着我们需要用bytes4, bytes4, uint256打包内存的最后n字节(其中第一个bytes4是要调用的函数sig,后面两个是参数)。因此,我希望这样做:

代码语言:javascript
复制
    bytes4 fnSig := bytes4("0x7e965aeb")
    bytes4 param1 := bytes4("0xc48d6d5e")

    assembly {
        let sig := fnSig
        let data1 := param1
        let data2 := 12345678

        let memStart := msize()
        mstore(sig)
        mstore(data1)
        mstore(data2)
        let result := delegatecall(gas(), implementation, memStart, add(4, add(4, 1), 0, 0)

        // ...
    }

然而,上述方法似乎不起作用,我也不完全确定原因。此外,为了避免覆盖现有内存,我使用了msize(),这似乎很糟糕,因为我需要禁用Yul优化器才能使用它。

我认为我对如何将这些参数加载到内存中的理解可能是不正确的。我非常感谢这里的一些指导!

EN

回答 1

Ethereum用户

发布于 2022-03-30 10:40:06

您的代码有几个问题:

  • 您没有正确处理内存。
  • 您的参数编码不正确。
  • 您没有正确计算数据大小。

委托所有操作码将简单地调用implementation,将从argOffset开始的argSize字节数据作为被调用方的调用数据。此调用将在调用者的存储上下文中进行,但这与您的问题无关。

这意味着我们需要用bytes4、bytes4、uint256打包最后n个内存字节(其中第一个bytes4是要调用的函数sig,后面两个是参数)。

该数据必须包含函数标识符(4个字节)、abi编码的param1和abi编码的param2。函数标识符是一种识别应该在调用方调用哪个函数的方法,该数据的其余部分(param1param2)是其参数。

在坚实的情况下,你可以用这样一种方式对它们进行编码:

代码语言:javascript
复制
// Manually 
bytes memory encoded = abi.encodePacked(fnSig, abi.encode(param1, param2));

// With encodeWithSelector
bytes memory encoded = abi.encodeWithSelector(fnSig, param1, param2);

// With encodeWithSignature
bytes memory encoded = abi.encodeWithSignature("myFunction(bytes4,uint256)", param1, param2);

提供一个字节数组,该数组具有:

  • 字节0-4:函数标识符
  • 字节4-36: abi编码的param1 (按照abi规格填充到32个字节)
  • 字节36 - 68 : abi编码的param 2

总大小为68 (0x44)字节。

0x7e965aebc48d6d5e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc614e

其中:

  • 0x7e965aeb是函数标识符。
  • 0xc48d6d5e00000000000000000000000000000000000000000000000000000000是param1
  • 0x0000000000000000000000000000000000000000000000000000000000bc614e是param 2

此外,为了避免覆盖现有内存,我使用msize(),这似乎很糟糕,因为我需要禁用Yul优化器才能使用它。

这并不坏,您可以绕过它,使用一种只依赖地址0x40的自由内存指针的友好优化方法。

如果这有用的话,这里有一组注释过的契约,显示了3种主要方法:

  • 可靠地编码(呼叫者)
  • 使用程序集/空闲内存指针(caller2)进行编码
  • 使用程序集/大小编码(caller3)

目标契约将记录params,以便您可以看到正确的值已被转发。

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

contract Delegate {

    event CalledSuccessfully(bytes4 param1, uint256 param2);

    function myFunction(bytes4 param1, uint256 param2) public {
        emit CalledSuccessfully(param1, param2);
    }
}

contract Delegator {

    Delegate delegate;

    constructor(Delegate _delegate) {
        delegate = _delegate;
    }

    function caller() public {
        bytes4 fnSig = hex"7e965aeb";
        bytes4 param1 = hex"c48d6d5e";
        uint256 param2 = 12345678;

        //bytes memory encoded = abi.encodePacked(fnSig, abi.encode(param1, param2));
        //bytes memory encoded = abi.encodeWithSelector(fnSig, param1, param2);
        bytes memory encoded = abi.encodeWithSignature("myFunction(bytes4,uint256)", param1, param2);

        assembly {

            // Get the value at slot `delegate.slot` to get the address of our target contract
            let implementation := sload(delegate.slot)

            // gas : gas()
            // address : implementation -> sload(delegate.slot)
            // argsOffset : add(encoded, 0x20) -> skip the size of the array
            // argsSize : mload(encoded) -> first element of the array is its size
            // retOffset : 0
            // retSize : 0
            let result := delegatecall(gas(), implementation, add(encoded, 0x20), mload(encoded), 0, 0)

            if eq(result, 0) {
                revert(0, returndatasize())
            }
        }
    }

     function caller2() public {
        bytes4 fnSig = hex"7e965aeb";
        bytes4 param1 = hex"c48d6d5e";
        uint256 param2 = 12345678;

        assembly {

            // Get the free memory address with the free memory pointer
            let params := mload(0x40)

            // We store 0x44 bytes, so we increment the free memory pointer
            // by that exact amount to keep things in order
            mstore(0x40, add(params, 0x44))

            // Store fnSig at params : here we store 32 bytes : 4 bytes of fnSig and 28 bytes of RIGHT padding
            mstore(params, fnSig)

            // Store param1 at params + 0x4 : overwriting the 28 bytes of RIGHT padding included before
            mstore(add(params, 0x4), param1)

            // Store param2 at params + 0x4 + 0x20 = 0x24 (36 bytes) : store 32 bytes following fnSig (4 bytes) and param1 (32 bytes)
            mstore(add(params, 0x24), param2)

            // Get the value at slot `delegate.slot` to get the address of our target contract
            let implementation := sload(delegate.slot)
            
            // gas : gas()
            // address : implementation -> sload(delegate.slot)
            // argsOffset : encoded : we are not dealing with a solidity byte array
            // argsSize : 0x44
            // retOffset : 0
            // retSize : 0
            let result := delegatecall(gas(), implementation, params, 0x44, 0, 0)

            if eq(result, 0) {
                revert(0, returndatasize())
            }

        }
    }

    function caller3() public {
        bytes4 fnSig = hex"7e965aeb";
        bytes4 param1 = hex"c48d6d5e";
        uint256 param2 = 12345678;

        assembly {
             // Get the free memory address with msize
            let params := msize()

            // We store 0x44 bytes, so we increment the free memory pointer
            // by that exact amount to keep things in order
            mstore(0x40, add(params, 0x44))

            // Store fnSig at params : here we store 32 bytes : 4 bytes of fnSig and 28 bytes of RIGHT padding
            mstore(params, fnSig)

            // Store param1 at params + 0x4 : overwriting the 28 bytes of RIGHT padding included before
            mstore(add(params, 0x4), param1)

             // Store param2 at params + 0x4 + 0x20 = 0x24 (36 bytes) : store 32 bytes following fnSig (4 bytes) and param1 (32 bytes)
            mstore(add(params, 0x24), param2)

            // Get the value at slot `delegate.slot` to get the address of our target contract
            let implementation := sload(delegate.slot)

            // gas : gas()
            // address : implementation -> sload(delegate.slot)
            // argsOffset : encoded : we are not dealing with a solidity byte array
            // argsSize : 0x44
            // retOffset : 0
            // retSize : 0
            let result := delegatecall(gas(), implementation, params, 0x44, 0, 0)

           if eq(result, 0) {
                revert(0, returndatasize())
            }
        }
    }
}
票数 2
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

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

复制
相关文章

相似问题

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