Commit a4c66440 authored by Maurelian's avatar Maurelian

fix(ctb): Properly handle return data

Also adds a test that checks the return data.
parent 0a1ffdc4
...@@ -47,12 +47,12 @@ contract DelayedVetoable is ISemver { ...@@ -47,12 +47,12 @@ contract DelayedVetoable is ISemver {
/// @notice The address that can initiate a call. /// @notice The address that can initiate a call.
address internal immutable INITIATOR; address internal immutable INITIATOR;
/// @notice The current amount of time to wait before forwarding a call.
uint256 internal _delay;
/// @notice The delay which will be set after the initial system deployment is completed. /// @notice The delay which will be set after the initial system deployment is completed.
uint256 internal immutable OPERATING_DELAY; uint256 internal immutable OPERATING_DELAY;
/// @notice The current amount of time to wait before forwarding a call.
uint256 internal _delay;
/// @notice The time that a call was initiated. /// @notice The time that a call was initiated.
mapping(bytes32 => uint256) internal _queuedAt; mapping(bytes32 => uint256) internal _queuedAt;
...@@ -130,7 +130,6 @@ contract DelayedVetoable is ISemver { ...@@ -130,7 +130,6 @@ contract DelayedVetoable is ISemver {
// The initiator and vetoer activate the delay by passing in null data. // The initiator and vetoer activate the delay by passing in null data.
if (msg.data.length == 0) { if (msg.data.length == 0) {
if (msg.sender != INITIATOR && msg.sender != VETOER) { if (msg.sender != INITIATOR && msg.sender != VETOER) {
// todo(maurelian): make this error have an expected array.
revert Unauthorized(INITIATOR, msg.sender); revert Unauthorized(INITIATOR, msg.sender);
} }
_activateDelay(); _activateDelay();
...@@ -180,13 +179,15 @@ contract DelayedVetoable is ISemver { ...@@ -180,13 +179,15 @@ contract DelayedVetoable is ISemver {
function _forwardAndHalt(bytes32 callHash) internal { function _forwardAndHalt(bytes32 callHash) internal {
// Forward the call // Forward the call
emit Forwarded(callHash, msg.data); emit Forwarded(callHash, msg.data);
(bool success,) = TARGET.call(msg.data); (bool success, bytes memory returndata) = TARGET.call(msg.data);
assembly { if (success == true) {
// Success == 0 means a revert. We'll revert too and pass the data up. assembly {
if iszero(success) { revert(0x0, returndatasize()) } return(add(returndata, 0x20), mload(returndata))
}
// Otherwise we'll just return and pass the data up. } else {
return(0x0, returndatasize()) assembly {
revert(add(returndata, 0x20), mload(returndata))
}
} }
} }
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { CommonTest, Reverter } from "./CommonTest.t.sol"; import { CommonTest } from "./CommonTest.t.sol";
import { DelayedVetoable } from "../src/L1/DelayedVetoable.sol"; import { DelayedVetoable } from "../src/L1/DelayedVetoable.sol";
contract DelayedVetoable_Init is CommonTest { contract DelayedVetoable_Init is CommonTest {
...@@ -17,7 +17,6 @@ contract DelayedVetoable_Init is CommonTest { ...@@ -17,7 +17,6 @@ contract DelayedVetoable_Init is CommonTest {
address vetoer = bob; address vetoer = bob;
uint256 operatingDelay = 14 days; uint256 operatingDelay = 14 days;
DelayedVetoable delayedVetoable; DelayedVetoable delayedVetoable;
Reverter reverter;
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
...@@ -27,13 +26,12 @@ contract DelayedVetoable_Init is CommonTest { ...@@ -27,13 +26,12 @@ contract DelayedVetoable_Init is CommonTest {
target_: address(target), target_: address(target),
operatingDelay_: operatingDelay operatingDelay_: operatingDelay
}); });
// Most tests will use the operating delay, so we call as the initiator with null data // Most tests will use the operating delay, so we call as the initiator with null data
// to set the delay. For tests that need to use the initial zero delay, we'll modify the // to set the delay. For tests that need to use the initial zero delay, we'll modify the
// value in storage. // value in storage.
vm.prank(initiator); vm.prank(initiator);
(bool success,) = address(delayedVetoable).call(hex""); (bool success,) = address(delayedVetoable).call(hex"");
reverter = new Reverter();
} }
/// @dev This function is used to prevent initiating the delay unintentionally.. /// @dev This function is used to prevent initiating the delay unintentionally..
...@@ -78,22 +76,29 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init { ...@@ -78,22 +76,29 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
vm.prank(initiator); vm.prank(initiator);
(bool success,) = address(delayedVetoable).call(data); (bool success,) = address(delayedVetoable).call(data);
assert(success); assertTrue(success);
} }
/// @dev The delay is inititially set to zero and the call is immediately forwarded. /// @dev The delay is inititially set to zero and the call is immediately forwarded.
function testFuzz_handleCall_initialForwardingImmediately_succeeds(bytes memory data) external { function testFuzz_handleCall_initialForwardingImmediately_succeeds(
assumeNonzeroData(data); bytes memory inData,
bytes memory outData
)
external
{
assumeNonzeroData(inData);
// Reset the delay to zero // Reset the delay to zero
vm.store(address(delayedVetoable), bytes32(uint256(0)), bytes32(uint256(0))); vm.store(address(delayedVetoable), bytes32(uint256(0)), bytes32(uint256(0)));
vm.prank(initiator); vm.mockCall(target, inData, outData);
vm.expectEmit(true, false, false, true, address(delayedVetoable)); vm.expectEmit(true, false, false, true, address(delayedVetoable));
vm.expectCall({ callee: target, data: data }); vm.expectCall({ callee: target, data: inData });
emit Forwarded(keccak256(data), data); emit Forwarded(keccak256(inData), inData);
(bool success,) = address(delayedVetoable).call(data); vm.prank(initiator);
assert(success); (bool success, bytes memory returnData) = address(delayedVetoable).call(inData);
assertTrue(success);
assertEq(returnData, outData);
} }
/// @dev The delay can be activated by the vetoer or initiator, and are not forwarded until the delay has passed /// @dev The delay can be activated by the vetoer or initiator, and are not forwarded until the delay has passed
...@@ -102,7 +107,6 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init { ...@@ -102,7 +107,6 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
assumeNonzeroData(data); assumeNonzeroData(data);
vm.prank(initiator); vm.prank(initiator);
// it's immediately forwarding for some reason.
(bool success,) = address(delayedVetoable).call(data); (bool success,) = address(delayedVetoable).call(data);
vm.warp(block.timestamp + operatingDelay); vm.warp(block.timestamp + operatingDelay);
...@@ -111,7 +115,7 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init { ...@@ -111,7 +115,7 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
vm.expectCall({ callee: target, data: data }); vm.expectCall({ callee: target, data: data });
(success,) = address(delayedVetoable).call(data); (success,) = address(delayedVetoable).call(data);
assert(success); assertTrue(success);
} }
} }
...@@ -120,7 +124,7 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init { ...@@ -120,7 +124,7 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
function test_handleCall_unauthorizedInitiation_reverts() external { function test_handleCall_unauthorizedInitiation_reverts() external {
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this))); vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
(bool success,) = address(delayedVetoable).call(NON_ZERO_DATA); (bool success,) = address(delayedVetoable).call(NON_ZERO_DATA);
assert(success); assertTrue(success);
} }
/// @dev The call cannot be forewarded until the delay has passed. /// @dev The call cannot be forewarded until the delay has passed.
...@@ -148,28 +152,30 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init { ...@@ -148,28 +152,30 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
// Forward the call // Forward the call
vm.expectCall({ callee: target, data: data }); vm.expectCall({ callee: target, data: data });
(success,) = address(delayedVetoable).call(data); (success,) = address(delayedVetoable).call(data);
assert(success); assertTrue(success);
// Attempt to foward the same call again. // Attempt to foward the same call again.
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this))); vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
(success,) = address(delayedVetoable).call(data); (success,) = address(delayedVetoable).call(data);
assert(success); assertTrue(success);
} }
/// @dev If the target reverts, it is bubbled up. /// @dev If the target reverts, it is bubbled up.
function testFuzz_handleCall_forwardingTargetReverts_reverts(bytes memory data) external { function testFuzz_handleCall_forwardingTargetReverts_reverts(bytes memory inData, bytes memory outData) external {
assumeNonzeroData(data); assumeNonzeroData(inData);
vm.etch(target, address(reverter).code);
// Initiate the call
vm.prank(initiator); vm.prank(initiator);
(bool success,) = address(delayedVetoable).call(data); (bool success,) = address(delayedVetoable).call(inData);
vm.warp(block.timestamp + operatingDelay); vm.warp(block.timestamp + operatingDelay);
vm.expectEmit(true, false, false, true, address(delayedVetoable)); vm.expectEmit(true, false, false, true, address(delayedVetoable));
emit Forwarded(keccak256(data), data); emit Forwarded(keccak256(inData), inData);
(success,) = address(delayedVetoable).call(data); vm.mockCallRevert(target, inData, outData);
assertFalse(success);
// Forward the call
vm.expectRevert(outData);
(bool success2,) = address(delayedVetoable).call(inData);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment