Skip to content

improve(Universal_SpokePool): Clarify comments + variable names #952

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

Merged
merged 2 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
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
25 changes: 14 additions & 11 deletions contracts/Universal_SpokePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ contract Universal_SpokePool is OwnableUpgradeable, SpokePool, CircleCCTPAdapter
/// set to a very high value, like 24 hours.
uint256 public immutable ADMIN_UPDATE_BUFFER;

/// @notice Stores all proofs verified to prevent replay attacks.
mapping(uint256 => bool) public verifiedProofs;
/// @notice Stores nonces of calldata stored in HubPoolStore that gets executed via executeMessage()
/// to prevent replay attacks.
mapping(uint256 => bool) public executedMessages;

// Warning: this variable should _never_ be touched outside of this contract. It is intentionally set to be
// private. Leaving it set to true can permanently disable admin calls.
Expand All @@ -51,7 +52,7 @@ contract Universal_SpokePool is OwnableUpgradeable, SpokePool, CircleCCTPAdapter
error NotImplemented();
error AdminUpdateTooCloseToLastHeliosUpdate();

// All calls that have admin privileges must be fired from within the receiveL1State method that validates that
// All calls that have admin privileges must be fired from within the executeMessage method that validates that
// the input data was published on L1 by the HubPool. This input data is then executed on this contract.
// This modifier sets the adminCallValidated variable so this condition can be checked in _requireAdminSender().
modifier validateInternalCalls() {
Expand Down Expand Up @@ -104,8 +105,8 @@ contract Universal_SpokePool is OwnableUpgradeable, SpokePool, CircleCCTPAdapter
* @notice Relays calldata stored by the HubPool on L1 into this contract.
* @dev Replay attacks are possible with this _message if this contract has the same address on another chain.
* @param _messageNonce Nonce of message stored in HubPoolStore.
* @param _message Message stored in HubPoolStore. Compared against hashed value in light client for slot key
* corresponding to _messageNonce at block number.
* @param _message Message stored in HubPoolStore's relayMessageCallData mapping. Compared against raw value
* in Helios light client for slot key corresponding to _messageNonce at block number.
* @param _blockNumber Block number in light client we use to check slot value of slot key
*/
function executeMessage(
Expand All @@ -114,11 +115,13 @@ contract Universal_SpokePool is OwnableUpgradeable, SpokePool, CircleCCTPAdapter
uint256 _blockNumber
) external validateInternalCalls {
bytes32 slotKey = getSlotKey(_messageNonce);
bytes32 expectedSlotValueHash = keccak256(_message);
// The expected slot value corresponds to the hash of the L2 calldata and its target,
// as originally stored in the HubPoolStore's relayMessageCallData mapping.
bytes32 expectedSlotValue = keccak256(_message);

// Verify Helios light client has expected slot value.
bytes32 slotValueHash = IHelios(helios).getStorageSlot(_blockNumber, hubPoolStore, slotKey);
if (expectedSlotValueHash != slotValueHash) {
bytes32 slotValue = IHelios(helios).getStorageSlot(_blockNumber, hubPoolStore, slotKey);
if (expectedSlotValue != slotValue) {
revert SlotValueMismatch();
}

Expand All @@ -131,10 +134,10 @@ contract Universal_SpokePool is OwnableUpgradeable, SpokePool, CircleCCTPAdapter

// Prevent replay attacks. The slot key should be a hash of the nonce associated with this calldata in the
// HubPoolStore, which maps the nonce to the _value.
if (verifiedProofs[_messageNonce]) {
if (executedMessages[_messageNonce]) {
revert AlreadyExecuted();
}
verifiedProofs[_messageNonce] = true;
executedMessages[_messageNonce] = true;
emit RelayedCallData(_messageNonce, msg.sender);

_executeCalldata(message);
Expand Down Expand Up @@ -181,7 +184,7 @@ contract Universal_SpokePool is OwnableUpgradeable, SpokePool, CircleCCTPAdapter
}
}

// Check that the admin call is only triggered by a receiveL1State() call.
// Check that the admin call is only triggered by a executeMessage() call.
function _requireAdminSender() internal view override {
if (!_adminCallValidated) {
revert AdminCallNotValidated();
Expand Down
2 changes: 1 addition & 1 deletion storage-layouts/Universal_SpokePool.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
},
{
"contract": "contracts/Universal_SpokePool.sol:Universal_SpokePool",
"label": "verifiedProofs",
"label": "executedMessages",
"offset": 0,
"slot": "3262"
},
Expand Down
6 changes: 3 additions & 3 deletions test/evm/foundry/local/Universal_SpokePool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ contract UniversalSpokePoolTest is Test {
spokePool.executeMessage(nonce, value, 101); // block number changes doesn't impact replay protection
}

function testVerifiedProofs() public {
function testExecutedMessages() public {
// Checks replay protection mapping is updated as expected.
bytes memory message = abi.encodeWithSignature(
"relayRootBundle(bytes32,bytes32)",
Expand All @@ -151,9 +151,9 @@ contract UniversalSpokePoolTest is Test {
);
bytes memory value = abi.encode(address(spokePool), message);
helios.updateStorageSlot(spokePool.getSlotKey(nonce), keccak256(value));
assertFalse(spokePool.verifiedProofs(nonce));
assertFalse(spokePool.executedMessages(nonce));
spokePool.executeMessage(nonce, value, 100);
assertTrue(spokePool.verifiedProofs(nonce));
assertTrue(spokePool.executedMessages(nonce));
}

function testHeliosMissingState() public {
Expand Down
Loading