似乎几乎所有外部调用的示例都是简单的代理,它们执行如下操作:
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()的参数:
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合同上调用这个函数:
myFunction(bytes4,uint256) => 0x7e965aeb使用适当的调用数据。
正如我所理解的(3) byte offset in the memory in bytes, the calldata of the sub context,这意味着我们需要用bytes4, bytes4, uint256打包内存的最后n字节(其中第一个bytes4是要调用的函数sig,后面两个是参数)。因此,我希望这样做:
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优化器才能使用它。
我认为我对如何将这些参数加载到内存中的理解可能是不正确的。我非常感谢这里的一些指导!
发布于 2022-03-30 10:40:06
您的代码有几个问题:
委托所有操作码将简单地调用implementation,将从argOffset开始的argSize字节数据作为被调用方的调用数据。此调用将在调用者的存储上下文中进行,但这与您的问题无关。
这意味着我们需要用bytes4、bytes4、uint256打包最后n个内存字节(其中第一个bytes4是要调用的函数sig,后面两个是参数)。
该数据必须包含函数标识符(4个字节)、abi编码的param1和abi编码的param2。函数标识符是一种识别应该在调用方调用哪个函数的方法,该数据的其余部分(param1和param2)是其参数。
在坚实的情况下,你可以用这样一种方式对它们进行编码:
// 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);提供一个字节数组,该数组具有:
总大小为68 (0x44)字节。
0x7e965aebc48d6d5e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc614e
其中:
此外,为了避免覆盖现有内存,我使用msize(),这似乎很糟糕,因为我需要禁用Yul优化器才能使用它。
这并不坏,您可以绕过它,使用一种只依赖地址0x40的自由内存指针的友好优化方法。
如果这有用的话,这里有一组注释过的契约,显示了3种主要方法:
目标契约将记录params,以便您可以看到正确的值已被转发。
// 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())
}
}
}
}https://ethereum.stackexchange.com/questions/124636
复制相似问题