Skip to content

Commit

Permalink
added more context about state updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Zodomo committed Dec 11, 2024
1 parent 3178579 commit c0b82f9
Show file tree
Hide file tree
Showing 13 changed files with 546 additions and 61 deletions.
93 changes: 81 additions & 12 deletions contracts/bindings/solveinbox.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/solveoutbox.go

Large diffs are not rendered by default.

60 changes: 30 additions & 30 deletions contracts/solve/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
SolveInbox_accept_Test:test_accept_one_request() (gas: 395276)
SolveInbox_accept_Test:test_accept_reverts() (gas: 1047031)
SolveInbox_accept_Test:test_accept_skip_first() (gas: 703186)
SolveInbox_accept_Test:test_accept_two_requests() (gas: 727312)
SolveInbox_cancel_Test:test_cancel_multiToken() (gas: 548512)
SolveInbox_cancel_Test:test_cancel_nativeMultiToken() (gas: 637552)
SolveInbox_cancel_Test:test_cancel_oldest_request() (gas: 693948)
SolveInbox_cancel_Test:test_cancel_one_request() (gas: 396173)
SolveInbox_cancel_Test:test_cancel_rejected_nativeMultiToken_request() (gas: 647058)
SolveInbox_cancel_Test:test_cancel_rejected_nativeToken_request() (gas: 405192)
SolveInbox_cancel_Test:test_cancel_reverts() (gas: 1068135)
SolveInbox_cancel_Test:test_cancel_singleToken() (gas: 427717)
SolveInbox_cancel_Test:test_cancel_two_requests() (gas: 705726)
SolveInbox_claim_Test:test_claim_multiDeposit() (gas: 719118)
SolveInbox_claim_Test:test_claim_reverts() (gas: 448858)
SolveInbox_claim_Test:test_claim_singleNative() (gas: 471334)
SolveInbox_claim_Test:test_claim_singleToken() (gas: 502256)
SolveInbox_markFulfilled_Test:test_markFulfilled_reverts() (gas: 506937)
SolveInbox_markFulfilled_Test:test_markFulfilled_success() (gas: 417277)
SolveInbox_reject_Test:test_reject_nativeMultiToken() (gas: 601341)
SolveInbox_reject_Test:test_reject_oldest_request() (gas: 666353)
SolveInbox_reject_Test:test_reject_one_request() (gas: 368071)
SolveInbox_reject_Test:test_reject_reverts() (gas: 749056)
SolveInbox_reject_Test:test_reject_two_requests() (gas: 670691)
SolveInbox_request_Test:test_request_multiToken() (gas: 551270)
SolveInbox_request_Test:test_request_nativeMultiToken() (gas: 609471)
SolveInbox_request_Test:test_request_reverts() (gas: 929951)
SolveInbox_request_Test:test_request_singleNative() (gas: 369998)
SolveInbox_request_Test:test_request_singleToken() (gas: 431512)
SolveInbox_request_Test:test_request_two() (gas: 678931)
SolveInbox_accept_Test:test_accept_one_request() (gas: 537030)
SolveInbox_accept_Test:test_accept_reverts() (gas: 1339215)
SolveInbox_accept_Test:test_accept_skip_first() (gas: 902646)
SolveInbox_accept_Test:test_accept_two_requests() (gas: 953825)
SolveInbox_cancel_Test:test_cancel_multiToken() (gas: 688533)
SolveInbox_cancel_Test:test_cancel_nativeMultiToken() (gas: 778693)
SolveInbox_cancel_Test:test_cancel_oldest_request() (gas: 900582)
SolveInbox_cancel_Test:test_cancel_one_request() (gas: 535091)
SolveInbox_cancel_Test:test_cancel_rejected_nativeMultiToken_request() (gas: 860066)
SolveInbox_cancel_Test:test_cancel_rejected_nativeToken_request() (gas: 613808)
SolveInbox_cancel_Test:test_cancel_reverts() (gas: 1384001)
SolveInbox_cancel_Test:test_cancel_singleToken() (gas: 566623)
SolveInbox_cancel_Test:test_cancel_two_requests() (gas: 926513)
SolveInbox_claim_Test:test_claim_multiDeposit() (gas: 963296)
SolveInbox_claim_Test:test_claim_reverts() (gas: 651008)
SolveInbox_claim_Test:test_claim_singleNative() (gas: 713232)
SolveInbox_claim_Test:test_claim_singleToken() (gas: 744158)
SolveInbox_markFulfilled_Test:test_markFulfilled_reverts() (gas: 619200)
SolveInbox_markFulfilled_Test:test_markFulfilled_success() (gas: 605634)
SolveInbox_reject_Test:test_reject_nativeMultiToken() (gas: 742344)
SolveInbox_reject_Test:test_reject_oldest_request() (gas: 872875)
SolveInbox_reject_Test:test_reject_one_request() (gas: 506887)
SolveInbox_reject_Test:test_reject_reverts() (gas: 951608)
SolveInbox_reject_Test:test_reject_two_requests() (gas: 891281)
SolveInbox_request_Test:test_request_multiToken() (gas: 642443)
SolveInbox_request_Test:test_request_nativeMultiToken() (gas: 701762)
SolveInbox_request_Test:test_request_reverts() (gas: 1199281)
SolveInbox_request_Test:test_request_singleNative() (gas: 460051)
SolveInbox_request_Test:test_request_singleToken() (gas: 521569)
SolveInbox_request_Test:test_request_two() (gas: 824884)
SolveOutbox_fulfill_test:test_fulfillFee() (gas: 27996)
SolveOutbox_fulfill_test:test_fulfill_reverts() (gas: 673377)
SolveOutbox_fulfill_test:test_fulfill_succeeds() (gas: 274856)
7 changes: 7 additions & 0 deletions contracts/solve/src/Solve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ library Solve {
Claimed
}

struct StatusUpdate {
Status status;
uint40 timestamp;
}

/**
* @notice Reason for rejecting a request.
*/
Expand All @@ -34,6 +39,7 @@ library Solve {
* @param status Request status (open, accepted, cancelled, rejected, fulfilled, paid).
* @param call Details of the call to be executed on another chain.
* @param deposits Array of deposits backing the request.
* @param updateHistory Array of status updates including timestamps.
*/
struct Request {
bytes32 id;
Expand All @@ -43,6 +49,7 @@ library Solve {
address acceptedBy;
Call call;
Deposit[] deposits;
StatusUpdate[] updateHistory;
}

/**
Expand Down
103 changes: 88 additions & 15 deletions contracts/solve/src/SolveInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,23 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
*/
mapping(bytes32 id => Solve.Request) internal _requests;

/**
* @notice Map status to latest request ID.
*/
mapping(Solve.Status => bytes32 id) internal _latestReqByStatus;

constructor() {
// Must get Arbitrum block number from ArbSys precompile, block.number returns L1 block number on Arbitrum.
// This is a temporary fix, we need a robust way of properly setting this value when on any Arbitrum chain.
if (block.chainid != 42_161 && block.chainid != 421_614) deployedAt = block.number;
else deployedAt = IArbSys(ARB_SYS).arbBlockNumber();
if (_isContract(ARB_SYS)) {
try IArbSys(ARB_SYS).arbBlockNumber() returns (uint256 arbBlockNumber) {
deployedAt = arbBlockNumber;
} catch {
deployedAt = block.number;
}
} else {
deployedAt = block.number;
}

_disableInitializers();
}

Expand All @@ -97,6 +109,20 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
return _requests[id];
}

/**
* @notice Returns the update history for the request with the given ID.
*/
function getRequestUpdateHistory(bytes32 id) external view returns (Solve.StatusUpdate[] memory) {
return _requests[id].updateHistory;
}

/**
* @notice Returns the latest request with the given status.
*/
function getLatestRequestByStatus(Solve.Status status) external view returns (Solve.Request memory) {
return _requests[_latestReqByStatus[status]];
}

/**
* @notice Open a request to execute a call on another chain, backed by deposits.
* Token deposits are transferred from msg.sender to this inbox.
Expand Down Expand Up @@ -130,9 +156,15 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
Solve.Request storage req = _requests[id];
if (req.status != Solve.Status.Pending) revert NotPending();

req.updatedAt = uint40(block.timestamp);
req.status = Solve.Status.Accepted;
Solve.StatusUpdate memory update =
Solve.StatusUpdate({ status: Solve.Status.Accepted, timestamp: uint40(block.timestamp) });

req.updatedAt = update.timestamp;
req.status = update.status;
req.acceptedBy = msg.sender;
req.updateHistory.push(update);

_latestReqByStatus[Solve.Status.Accepted] = id;

emit Accepted(id, msg.sender);
}
Expand All @@ -146,8 +178,14 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
Solve.Request storage req = _requests[id];
if (req.status != Solve.Status.Pending) revert NotPending();

req.updatedAt = uint40(block.timestamp);
req.status = Solve.Status.Rejected;
Solve.StatusUpdate memory update =
Solve.StatusUpdate({ status: Solve.Status.Rejected, timestamp: uint40(block.timestamp) });

req.updatedAt = update.timestamp;
req.status = update.status;
req.updateHistory.push(update);

_latestReqByStatus[Solve.Status.Rejected] = id;

emit Rejected(id, msg.sender, reason);
}
Expand All @@ -162,8 +200,14 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
if (req.status != Solve.Status.Pending && req.status != Solve.Status.Rejected) revert NotPendingOrRejected();
if (req.from != msg.sender) revert Unauthorized();

req.updatedAt = uint40(block.timestamp);
req.status = Solve.Status.Reverted;
Solve.StatusUpdate memory update =
Solve.StatusUpdate({ status: Solve.Status.Reverted, timestamp: uint40(block.timestamp) });

req.updatedAt = update.timestamp;
req.status = update.status;
req.updateHistory.push(update);

_latestReqByStatus[Solve.Status.Reverted] = id;

_transferDeposits(req.from, req.deposits);

Expand All @@ -183,8 +227,14 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
// Ensure reported call hash matches requested call hash
if (callHash != _callHash(id, uint64(block.chainid), req.call)) revert WrongCallHash();

req.updatedAt = uint40(block.timestamp);
req.status = Solve.Status.Fulfilled;
Solve.StatusUpdate memory update =
Solve.StatusUpdate({ status: Solve.Status.Fulfilled, timestamp: uint40(block.timestamp) });

req.updatedAt = update.timestamp;
req.status = update.status;
req.updateHistory.push(update);

_latestReqByStatus[Solve.Status.Fulfilled] = id;

emit Fulfilled(id, callHash, req.acceptedBy);
}
Expand All @@ -199,8 +249,14 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
if (req.status != Solve.Status.Fulfilled) revert NotFulfilled();
if (req.acceptedBy != msg.sender) revert Unauthorized();

req.updatedAt = uint40(block.timestamp);
req.status = Solve.Status.Claimed;
Solve.StatusUpdate memory update =
Solve.StatusUpdate({ status: Solve.Status.Claimed, timestamp: uint40(block.timestamp) });

req.updatedAt = update.timestamp;
req.status = update.status;
req.updateHistory.push(update);

_latestReqByStatus[Solve.Status.Claimed] = id;

_transferDeposits(to, req.deposits);

Expand Down Expand Up @@ -234,12 +290,18 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
{
bytes32 id = _nextId();

Solve.StatusUpdate memory update =
Solve.StatusUpdate({ status: Solve.Status.Pending, timestamp: uint40(block.timestamp) });

req = _requests[id];
req.id = id;
req.updatedAt = uint40(block.timestamp);
req.status = Solve.Status.Pending;
req.updatedAt = update.timestamp;
req.status = update.status;
req.from = from;
req.call = call;
req.updateHistory.push(update);

_latestReqByStatus[Solve.Status.Pending] = id;

if (msg.value > 0) {
req.deposits.push(Solve.Deposit({ isNative: true, token: address(0), amount: msg.value }));
Expand Down Expand Up @@ -271,4 +333,15 @@ contract SolveInbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase, I
function _callHash(bytes32 id, uint64 sourceChainId, Solve.Call storage call) internal pure returns (bytes32) {
return keccak256(abi.encode(id, sourceChainId, call));
}

/**
* @dev Returns true if the address is a contract.
*/
function _isContract(address addr) internal view returns (bool) {
uint32 size;
assembly {
size := extcodesize(addr)
}
return (size > 0);
}
}
24 changes: 21 additions & 3 deletions contracts/solve/src/SolveOutbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,16 @@ contract SolveOutbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase {

constructor() {
// Must get Arbitrum block number from ArbSys precompile, block.number returns L1 block number on Arbitrum.
// This is a temporary fix, we need a robust way of properly setting this value when on any Arbitrum chain.
if (block.chainid != 42_161 && block.chainid != 421_614) deployedAt = block.number;
else deployedAt = IArbSys(ARB_SYS).arbBlockNumber();
if (_isContract(ARB_SYS)) {
try IArbSys(ARB_SYS).arbBlockNumber() returns (uint256 arbBlockNumber) {
deployedAt = arbBlockNumber;
} catch {
deployedAt = block.number;
}
} else {
deployedAt = block.number;
}

_disableInitializers();
}

Expand Down Expand Up @@ -185,4 +192,15 @@ contract SolveOutbox is OwnableRoles, ReentrancyGuard, Initializable, XAppBase {
function _callHash(bytes32 srcReqId, uint64 srcChainId, Solve.Call calldata call) internal pure returns (bytes32) {
return keccak256(abi.encode(srcReqId, srcChainId, call));
}

/**
* @dev Returns true if the address is a contract.
*/
function _isContract(address addr) internal view returns (bool) {
uint32 size;
assembly {
size := extcodesize(addr)
}
return (size > 0);
}
}
10 changes: 10 additions & 0 deletions contracts/solve/src/interfaces/ISolveInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ interface ISolveInbox {
*/
function getRequest(bytes32 id) external view returns (Solve.Request memory);

/**
* @notice Returns the update history for the request with the given ID.
*/
function getRequestUpdateHistory(bytes32 id) external view returns (Solve.StatusUpdate[] memory);

/**
* @notice Returns the latest request with the given status.
*/
function getLatestRequestByStatus(Solve.Status status) external view returns (Solve.Request memory);

/**
* @notice Open a request to execute a call on another chain, backed by deposits.
* Token deposits are transferred from msg.sender to this inbox.
Expand Down
45 changes: 45 additions & 0 deletions contracts/solve/test/Inbox_accept.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ contract SolveInbox_accept_Test is InboxBase {

assertEq(inbox.getRequest(id).acceptedBy, solver, "inbox.getRequest(id).acceptedBy");
assertEq(uint8(inbox.getRequest(id).status), uint8(Solve.Status.Accepted), "inbox.getRequest(id).status");
assertEq(
id,
inbox.getLatestRequestByStatus(Solve.Status.Accepted).id,
"inbox.getLatestRequestByStatus(Solve.Status.Accepted)"
);
assertEq(
uint8(inbox.getRequestUpdateHistory(id)[1].status),
uint8(Solve.Status.Accepted),
"inbox.getRequestUpdateHistory(id)[1].status"
);
assertEq(inbox.getRequestUpdateHistory(id).length, 2, "inbox.getRequestUpdateHistory(id).length");
}

/// @dev Test accepting two requests
Expand All @@ -104,6 +115,23 @@ contract SolveInbox_accept_Test is InboxBase {
assertEq(inbox.getRequest(id2).acceptedBy, solver, "inbox.getRequest(id2).acceptedBy");
assertEq(uint8(inbox.getRequest(id1).status), uint8(Solve.Status.Accepted), "inbox.getRequest(id1).status");
assertEq(uint8(inbox.getRequest(id2).status), uint8(Solve.Status.Accepted), "inbox.getRequest(id2).status");
assertEq(
id2,
inbox.getLatestRequestByStatus(Solve.Status.Accepted).id,
"inbox.getLatestRequestByStatus(Solve.Status.Accepted)"
);
assertEq(
uint8(inbox.getRequestUpdateHistory(id1)[1].status),
uint8(Solve.Status.Accepted),
"inbox.getRequestUpdateHistory(id1)[1].status"
);
assertEq(
uint8(inbox.getRequestUpdateHistory(id2)[1].status),
uint8(Solve.Status.Accepted),
"inbox.getRequestUpdateHistory(id2)[1].status"
);
assertEq(inbox.getRequestUpdateHistory(id1).length, 2, "inbox.getRequestUpdateHistory(id1).length");
assertEq(inbox.getRequestUpdateHistory(id2).length, 2, "inbox.getRequestUpdateHistory(id2).length");
}

/// @dev Test accepting requests out of order
Expand All @@ -126,5 +154,22 @@ contract SolveInbox_accept_Test is InboxBase {
assertEq(inbox.getRequest(id2).acceptedBy, solver, "inbox.getRequest(id2).acceptedBy");
assertEq(uint8(inbox.getRequest(id1).status), uint8(Solve.Status.Pending), "inbox.getRequest(id1).status");
assertEq(uint8(inbox.getRequest(id2).status), uint8(Solve.Status.Accepted), "inbox.getRequest(id2).status");
assertEq(
id2,
inbox.getLatestRequestByStatus(Solve.Status.Accepted).id,
"inbox.getLatestRequestByStatus(Solve.Status.Accepted)"
);
assertEq(
uint8(inbox.getRequestUpdateHistory(id1)[0].status),
uint8(Solve.Status.Pending),
"inbox.getRequestUpdateHistory(id1)[0].status"
);
assertEq(
uint8(inbox.getRequestUpdateHistory(id2)[1].status),
uint8(Solve.Status.Accepted),
"inbox.getRequestUpdateHistory(id2)[1].status"
);
assertEq(inbox.getRequestUpdateHistory(id1).length, 1, "inbox.getRequestUpdateHistory(id1).length");
assertEq(inbox.getRequestUpdateHistory(id2).length, 2, "inbox.getRequestUpdateHistory(id2).length");
}
}
Loading

0 comments on commit c0b82f9

Please sign in to comment.