From e98e6d97d8a1a6b26a2600f25f58d3ffd73af424 Mon Sep 17 00:00:00 2001 From: AtomicAzzaz <75758585+AtomicAzzaz@users.noreply.github.com> Date: Fri, 29 Jul 2022 03:58:12 +0200 Subject: [PATCH 1/3] Add test comparing memory state between two equivalent delegateCall functions --- test/TestDelegateCall.sol | 113 +++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/test/TestDelegateCall.sol b/test/TestDelegateCall.sol index 3ab1106..4f0fccd 100644 --- a/test/TestDelegateCall.sol +++ b/test/TestDelegateCall.sol @@ -20,11 +20,40 @@ contract Caller { bytes memory data = called.functionDelegateCall(abi.encodeWithSelector(_selector, "")); return abi.decode(data, (uint256)); } + + + function delegateCallAndReturnMemoryAndPointer(bytes4 _selector) external returns (bytes memory, bytes32) { + bytes memory allMem; + bytes32 ptr; + assembly{ + ptr := mload(0x40) + //We write a slot in the memory before the call + mstore(ptr, 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) + mstore(0x40, add(ptr, 0x20)) + } + + called.functionDelegateCall(abi.encodeWithSelector(_selector, "")); + + assembly{ + ptr := mload(0x40) + //We write a slot in the memory after the call + mstore(ptr, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) + mstore(0x40, add(ptr, 0x20)) + } + //We store the memory in allMem + assembly{ + ptr := mload(0x40) + allMem := 0x00 + mstore(allMem, mload(0x40)) + } + + return (allMem, ptr); + } } contract CallerRef { using Address for address; - + error LowLevelDelegateCallFailed(); address public immutable called; uint256 public x = 2; @@ -36,6 +65,47 @@ contract CallerRef { bytes memory data = called.functionDelegateCall(abi.encodeWithSelector(_selector, "")); return abi.decode(data, (uint256)); } + + function targetDelegateCallBehaviour(address _target, bytes memory _data) internal returns (bytes memory) { + (bool success, bytes memory returndata) = _target.delegatecall(_data); + if (success) return returndata; + else { + // Look for revert reason and bubble it up if present. + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly. + assembly { + revert(add(32, returndata), mload(returndata)) + } + } else revert LowLevelDelegateCallFailed(); + } + } + + function targetDelegateCallBehaviourAndReturnMemoryAndPointer(bytes4 _selector) external returns (bytes memory, bytes32) { + bytes memory allMem; + bytes32 ptr; + assembly{ + ptr := mload(0x40) + mstore(ptr, 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) + mstore(0x40, add(ptr, 0x20)) + } + + targetDelegateCallBehaviour(address(called),abi.encodeWithSelector(_selector, "")); + + assembly{ + ptr := mload(0x40) + mstore(ptr, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) + mstore(0x40, add(ptr, 0x20)) + } + + assembly{ + ptr := mload(0x40) + allMem := 0x00 + mstore(allMem, mload(0x40)) + } + + return (allMem, ptr); + } + } contract Called { @@ -47,6 +117,18 @@ contract Called { return x; } + function returnBytes32() external pure returns (bytes32){ + return "33333333333333333333333333333333"; + } + + function return40Bytes() external pure returns (bytes memory) { + return "3333333333333333333333333333333333333333"; + } + + function return32Bytes() external pure returns (bytes memory) { + return "33333333333333333333333333333333"; + } + function revertWithoutError() external pure { revert(); } @@ -92,6 +174,35 @@ contract TestDelegateCall is Test { caller.delegateCall(Called.revertWithCustomError.selector); } +function testMemoryUncorruptedWithRef() public{ + //This test makes sure that the delegateCall function of our library acts on the memory in the exact same way + //as a targetBehaviour function corresponding to the desired behavior. + bytes memory memoryReturned; + bytes32 pointerReturned; + bytes memory memoryReturnedRef; + bytes32 pointerReturnedRef; + + (memoryReturned, pointerReturned) = caller.delegateCallAndReturnMemoryAndPointer(Called.number.selector); + (memoryReturnedRef, pointerReturnedRef) = callerRef.targetDelegateCallBehaviourAndReturnMemoryAndPointer(Called.number.selector); + assert(keccak256(memoryReturned) == keccak256(memoryReturnedRef)); + assert(pointerReturned == pointerReturnedRef); + + (memoryReturned, pointerReturned) = caller.delegateCallAndReturnMemoryAndPointer(Called.return40Bytes.selector); + (memoryReturnedRef, pointerReturnedRef) = callerRef.targetDelegateCallBehaviourAndReturnMemoryAndPointer(Called.return40Bytes.selector); + assert(keccak256(memoryReturned) == keccak256(memoryReturnedRef)); + assert(pointerReturned == pointerReturnedRef); + + (memoryReturned, pointerReturned) = caller.delegateCallAndReturnMemoryAndPointer(Called.return32Bytes.selector); + (memoryReturnedRef, pointerReturnedRef) = callerRef.targetDelegateCallBehaviourAndReturnMemoryAndPointer(Called.return32Bytes.selector); + assert(keccak256(memoryReturned) == keccak256(memoryReturnedRef)); + assert(pointerReturned == pointerReturnedRef); + + (memoryReturned, pointerReturned) = caller.delegateCallAndReturnMemoryAndPointer(Called.returnBytes32.selector); + (memoryReturnedRef, pointerReturnedRef) = callerRef.targetDelegateCallBehaviourAndReturnMemoryAndPointer(Called.returnBytes32.selector); + assert(keccak256(memoryReturned) == keccak256(memoryReturnedRef)); + assert(pointerReturned == pointerReturnedRef); + } + /// GAS COMPARISONS /// function testGasDelegateCall() public { From 68467b6121e8a28ba07b0239cdf3e4d214d75a8f Mon Sep 17 00:00:00 2001 From: AtomicAzzaz <75758585+AtomicAzzaz@users.noreply.github.com> Date: Fri, 29 Jul 2022 13:43:57 +0200 Subject: [PATCH 2/3] Formatting --- test/TestDelegateCall.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestDelegateCall.sol b/test/TestDelegateCall.sol index 4f0fccd..56d0d8a 100644 --- a/test/TestDelegateCall.sol +++ b/test/TestDelegateCall.sol @@ -174,7 +174,7 @@ contract TestDelegateCall is Test { caller.delegateCall(Called.revertWithCustomError.selector); } -function testMemoryUncorruptedWithRef() public{ + function testMemoryUncorruptedWithRef() public{ //This test makes sure that the delegateCall function of our library acts on the memory in the exact same way //as a targetBehaviour function corresponding to the desired behavior. bytes memory memoryReturned; From 6aae2b6f1ce52cb2dc29603e318faa788a79d9f5 Mon Sep 17 00:00:00 2001 From: Merlin <44097430+MerlinEgalite@users.noreply.github.com> Date: Mon, 8 Aug 2022 10:22:18 +0100 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=93=9D=20Clean=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Merlin <44097430+MerlinEgalite@users.noreply.github.com> --- test/TestDelegateCall.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/TestDelegateCall.sol b/test/TestDelegateCall.sol index 56d0d8a..ad98127 100644 --- a/test/TestDelegateCall.sol +++ b/test/TestDelegateCall.sol @@ -27,7 +27,7 @@ contract Caller { bytes32 ptr; assembly{ ptr := mload(0x40) - //We write a slot in the memory before the call + // We write a slot in the memory before the call. mstore(ptr, 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) mstore(0x40, add(ptr, 0x20)) } @@ -36,11 +36,11 @@ contract Caller { assembly{ ptr := mload(0x40) - //We write a slot in the memory after the call + // We write a slot in the memory after the call. mstore(ptr, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) mstore(0x40, add(ptr, 0x20)) } - //We store the memory in allMem + // We store the memory in allMem. assembly{ ptr := mload(0x40) allMem := 0x00 @@ -175,8 +175,8 @@ contract TestDelegateCall is Test { } function testMemoryUncorruptedWithRef() public{ - //This test makes sure that the delegateCall function of our library acts on the memory in the exact same way - //as a targetBehaviour function corresponding to the desired behavior. + // This test makes sure that the delegateCall function of our library acts on the memory in the exact same way + // as a targetBehaviour function corresponding to the desired behavior. bytes memory memoryReturned; bytes32 pointerReturned; bytes memory memoryReturnedRef;