Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a delegateCall test regarding memory #25

Merged
merged 3 commits into from
Aug 8, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 112 additions & 1 deletion test/TestDelegateCall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 {
Expand All @@ -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();
}
Expand Down Expand Up @@ -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 {
Expand Down