Skip to content

Commit

Permalink
reintroduce delegateCall; ofac/audit; merge tips + callbreaker; [wip]…
Browse files Browse the repository at this point in the history
… existing examples + tests improvements (#37)

* reintroduce delegateCall; ofac/audit; merge tips + callbreaker

* completed PnP exmaple
  • Loading branch information
xBalbinus authored Nov 22, 2023
1 parent a67ecc9 commit c0dad2e
Show file tree
Hide file tree
Showing 21 changed files with 575 additions and 98 deletions.
1 change: 1 addition & 0 deletions src/TimeTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct CallObject {
uint256 gas;
address addr;
bytes callvalue;
bool delegate;
}

/// @dev Struct for holding a CallObject with an associated index
Expand Down
9 changes: 7 additions & 2 deletions src/lamination/LaminatedProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,13 @@ contract LaminatedProxy is LaminatedStorage, ReentrancyGuard {
bool success;
bytes memory returnvalue;

(success, returnvalue) =
callToMake.addr.call{gas: callToMake.gas, value: callToMake.amount}(callToMake.callvalue);
if (callToMake.delegate) {
(success, returnvalue) = callToMake.addr.delegatecall(callToMake.callvalue);
} else {
(success, returnvalue) =
callToMake.addr.call{gas: callToMake.gas, value: callToMake.amount}(callToMake.callvalue);
}

if (!success) {
revert CallFailed();
}
Expand Down
23 changes: 22 additions & 1 deletion src/timetravel/CallBreaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ contract CallBreaker is CallBreakerStorage {
/// @param index The index of the return value in the returnStore
event EnterPortal(CallObject callObj, ReturnObject returnvalue, uint256 index);

event Tip(address indexed from, address indexed to, uint256 amount);

/// @notice Emitted when the verifyStxn function is called
event VerifyStxn();

Expand All @@ -55,6 +57,15 @@ contract CallBreaker is CallBreakerStorage {
_setPortalClosed();
}

/// @dev Tips should be transferred from each LaminatorProxy to the solver via msg.value
receive() external payable {
bytes32 tipAddrKey = keccak256(abi.encodePacked("tipYourBartender"));
bytes memory tipAddrBytes = fetchFromAssociatedDataStore(tipAddrKey);
address tipAddr = abi.decode(tipAddrBytes, (address));
emit Tip(msg.sender, tipAddr, msg.value);
payable(tipAddr).transfer(msg.value);
}

/// @dev Modifier to make a function callable only when the portal is open.
/// Reverts if the portal is closed. Portal is opened by `verify`.
modifier ensureTurnerOpen() {
Expand Down Expand Up @@ -103,7 +114,7 @@ contract CallBreaker is CallBreakerStorage {
emit VerifyStxn();
}

/// @notice Executes a call and returns a value from the record of return values.
/// @notice Returns a value from the record of return values from the callObject.
/// @dev This function also does some accounting to track the occurrence of a given pair of call and return values.
/// @param input The call to be executed, structured as a CallObjectWithIndex.
/// @return The return value from the record of return values.
Expand All @@ -114,6 +125,16 @@ contract CallBreaker is CallBreakerStorage {
return thisReturn.returnvalue;
}

/// @notice Gets a return value from the record of return values from the index number.
/// @dev This function also does some accounting to track the occurrence of a given pair of call and return values.
/// @param index The call to be executed, structured as a CallObjectWithIndex.
/// @return The return value from the record of return values.
function getReturnValue(uint256 index) external view returns (bytes memory) {
// Decode the input to obtain the CallObject and calculate a unique ID representing the call-return pair
ReturnObject memory thisReturn = _getReturn(index);
return thisReturn.returnvalue;
}

/// @notice Fetches the value associated with a given key from the associatedDataStore
/// @param key The key whose associated value is to be fetched
/// @return The value associated with the given key
Expand Down
53 changes: 51 additions & 2 deletions src/timetravel/SmarterContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import "../timetravel/CallBreaker.sol";

contract SmarterContract {
CallBreaker public callbreaker;
mapping(address => bool) public auditedContracts;
mapping(address => bool) public ofacCensoredAddresses;

/// @dev Selector 0xab63c583
error FutureCallExpected();
Expand All @@ -26,6 +28,12 @@ contract SmarterContract {
/// @dev Selector 0xd1cb360d
error IllegalBackrun();

/// @dev Selector 0xed32fe28
error Unaudited();

/// @dev Selector 0xc19f17a9
error NotApproved();

/// @dev Constructs a new SmarterContract instance
/// @param _callbreaker The address of the CallBreaker contract
constructor(address _callbreaker) {
Expand Down Expand Up @@ -53,6 +61,47 @@ contract SmarterContract {
_;
}

modifier onlyAudited(address _address) {
auditedBlocker(_address);
_;
}

modifier onlyOFACApproved(address _address) {
OFAC_censoredBlocker(_address);

This comment has been minimized.

Copy link
@laudiacay

laudiacay Nov 23, 2023

Contributor

this is not how this should work- we need to pair on this to fix it

basically the concept is "scan the entire call stack to make sure it's entirely composed of calls to functions of contracts that are explicitly white/black listed"

_;
}

/// @notice Prevents execution by un-audited contracts
/// @dev This function checks whether the provided address is in the list of audited contracts
/// @param _address The address to check
function auditedBlocker(address _address) public view {
if (!auditedContracts[_address]) {
revert Unaudited();
}
}

/// @notice Prevents calls to all addresses in the list
/// @dev This function checks whether the provided address is in the list of OFAC censored addresses
function OFAC_censoredBlocker(address _address) public view {
if (ofacCensoredAddresses[_address]) {
revert NotApproved();
}
}

/// @notice Sets a contract as OFAC blocked
/// @dev This function adds the provided address to the list of OFAC censored addresses
/// @param _address The address to be added to the list of OFAC censored addresses
function setOFACBlocked(address _address) public {
ofacCensoredAddresses[_address] = true;

This comment has been minimized.

Copy link
@laudiacay

laudiacay Nov 23, 2023

Contributor

this should not be public- this info should be pulled from an external contract maintained by some theoretical third party, address provided in the constructor

}

/// @notice Sets a contract as audited
/// @dev This function adds the provided address to the list of audited contracts
/// @param _address The address to be added to the list of audited contracts
function setAuditedContract(address _address) public {
auditedContracts[_address] = true;

This comment has been minimized.

Copy link
@laudiacay

laudiacay Nov 23, 2023

Contributor

same situation

}

/// @notice Returns the call index, callobj, and returnobj of the currently executing call
/// @dev This function allows for time travel by returning the returnobj of the currently executing call
/// @return A pair consisting of the CallObject and ReturnObject of the currently executing call
Expand Down Expand Up @@ -90,7 +139,7 @@ contract SmarterContract {
/// @notice Ensures that there is a future call to the specified callobject after the current call
/// @dev This iterates over all call indices and ensures there's one after the current call.
/// Adding a hintdex makes this cheaper.
/// @param callObj The callobject to check for future calls
/// @param callObj The callobject to check for. This callObject should strictly be a future call
function assertFutureCallTo(CallObject memory callObj) public view {
uint256[] memory cinds = callbreaker.getCallIndex(callObj);
uint256 currentlyExecuting = callbreaker.getCurrentlyExecuting();
Expand All @@ -103,7 +152,7 @@ contract SmarterContract {
}

/// @notice Ensures that there is a future call to the specified callobject after the current call
/// @param callObj The callobject to check for future calls
/// @param callObj The callobject to check for. This callObject should strictly be a future call
/// @param hintdex The hint index to start checking for future calls
/// @custom:reverts FutureCallExpected() Hintdexes should always be in the future of the current executing call
/// @custom:reverts CallMismatch() The callobject at the hintdex should match the specified callObject
Expand Down
28 changes: 0 additions & 28 deletions src/tips/Tips.sol

This file was deleted.

4 changes: 0 additions & 4 deletions test/CallBreaker.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ contract CallBreakerHarness is CallBreaker {
function expectCallAtHarness(CallObject memory callObj, uint256 index) public view {
_expectCallAt(callObj, index);
}

function getReturnHarness(uint256 index) public view returns (ReturnObject memory) {
return _getReturn(index);
}
}

contract CallBreakerTest is Test {
Expand Down
1 change: 1 addition & 0 deletions test/GasSnapshot.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {LaminatorHarness} from "./Laminator.t.sol";
contract GasSnapshot is Test {
LaminatorHarness laminator;

// TODO: Finish gas snapshots
function setUp() public {
laminator = new LaminatorHarness();
}
Expand Down
42 changes: 28 additions & 14 deletions test/Laminator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", val1)
callvalue: abi.encodeWithSignature("emitArg(uint256)", val1),
delegate: false
});
bytes memory cData = abi.encode(callObj1);
vm.expectEmit(true, true, true, true);
Expand All @@ -74,7 +75,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", val2)
callvalue: abi.encodeWithSignature("emitArg(uint256)", val2),
delegate: false
});
cData = abi.encode(callObj2);
vm.expectEmit(true, true, true, true);
Expand Down Expand Up @@ -111,7 +113,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", val)
callvalue: abi.encodeWithSignature("emitArg(uint256)", val),
delegate: false
});
bytes memory cData = abi.encode(callObj);
uint256 sequenceNumber = laminator.pushToProxy(cData, 0);
Expand All @@ -134,7 +137,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", val)
callvalue: abi.encodeWithSignature("emitArg(uint256)", val),
delegate: false
});
bytes memory cData = abi.encode(callObj);
uint256 sequenceNumber = laminator.pushToProxy(cData, 1);
Expand All @@ -158,7 +162,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", val)
callvalue: abi.encodeWithSignature("emitArg(uint256)", val),
delegate: false
});
bytes memory cData = abi.encode(callObj);
uint256 sequenceNumber = laminator.pushToProxy(cData, 3);
Expand All @@ -184,7 +189,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", val)
callvalue: abi.encodeWithSignature("emitArg(uint256)", val),
delegate: false
});
bytes memory cData = abi.encode(callObj);
vm.prank(randomFriendAddress);
Expand All @@ -206,7 +212,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", val)
callvalue: abi.encodeWithSignature("emitArg(uint256)", val),
delegate: false
});
bytes memory cData = abi.encode(callObj);
vm.prank(address(laminator));
Expand All @@ -227,7 +234,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", val)
callvalue: abi.encodeWithSignature("emitArg(uint256)", val),
delegate: false
});
bytes memory cData = abi.encode(callObj);
uint256 sequenceNumber = laminator.pushToProxy(cData, 0);
Expand All @@ -254,7 +262,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("reverter()")
callvalue: abi.encodeWithSignature("reverter()"),
delegate: false
});
bytes memory cData = abi.encode(callObj);
uint256 sequenceNumber = laminator.pushToProxy(cData, 1);
Expand All @@ -277,7 +286,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42)
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42),
delegate: false
});
bytes memory cData = abi.encode(callObj);

Expand All @@ -298,7 +308,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42)
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42),
delegate: false
});
bytes memory cData = abi.encode(callObjs);

Expand All @@ -320,7 +331,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42)
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42),
delegate: false
});
bytes memory cData = abi.encode(callObj);

Expand Down Expand Up @@ -358,7 +370,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42)
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42),
delegate: false
});
bytes memory cData = abi.encode(callObj);

Expand All @@ -378,7 +391,8 @@ contract LaminatorTest is Test {
amount: 0,
addr: address(dummy),
gas: gasleft(),
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42)
callvalue: abi.encodeWithSignature("emitArg(uint256)", 42),
delegate: false
});
bytes memory cData = abi.encode(callObj);

Expand Down
3 changes: 2 additions & 1 deletion test/NoopTurnerTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ contract NoopTurnerTest is Test {
amount: 0,
addr: address(noopturner),
gas: 1000000,
callvalue: abi.encodeWithSignature("const_loop(uint16)", uint16(42))
callvalue: abi.encodeWithSignature("const_loop()"),
delegate: false
});

ReturnObject[] memory returnObjs = new ReturnObject[](1);
Expand Down
Loading

0 comments on commit c0dad2e

Please sign in to comment.