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

feat: support l2 plus #1157

Merged
merged 41 commits into from
Jan 14, 2025
Merged

feat: support l2 plus #1157

merged 41 commits into from
Jan 14, 2025

Conversation

karlem
Copy link
Contributor

@karlem karlem commented Sep 26, 2024

Part of #1080

This PR focuses on:

Integration Tests in Solidity (L3-Level Testing)

  • Write and run integration tests to cover the following scenarios:
    • Happy Path Scenarios:
      • From root to L3. - DONE
      • From L3 to root. - DONE
      • Between sibling L3 nodes. - DONE
    • Failure Scenarios:
      • Errors at the destination. - DONE
      • Errors midway through execution. Not sure this is really an issue.
    • Edge Case Testing:
      • Supply source mismatches. - This should really not be an issue - as the only cross net messages allowed are Call kind. (not trasfers)
      • Non-existent destinations. - DONE
    • Ensure all error cases propagate correctly. - DONE

Improvements

  • Improved visibility by adding new events into the cross-message propagation process. A cross-message ID has been added to each event for better tracking and transparency:
    • CheckpointCommitted
    • NewBottomUpMessage
    • MessageStoredInPostbox
    • MessagePropagatedFromPostbox
  • Automated cross-message propagation from the postbox. Previously, this process required an external caller to propagate the message, but now the IPC automatically propagates them after a checkpoint has been committed or after top-down messages have been applied.
  • Added a check to verify that the destination address is indeed a contract and not an EOA (Externally Owned Account).
  • Changed the behavior when applying messages: instead of reverting when a message is invalid, a result receipt is returned. This ensures that one invalid message does not disrupt the entire checkpoint submission process.
  • Extracted message validation into a separate function to ensure validation is completed before committing the message. The validation function does not revert since reversion is only desirable when the message is directly triggered by a user. In scenarios where the message originates from a different subnet (e.g., in the form of a checkpoint), reversion is not appropriate. For the same reason, the commit functions (top-down, bottom-up) should also not revert, as they are triggered by automatic postbox propagation. Instead, validation is performed beforehand.

TODOs

  • Adding a doc on who, how is invoking cross messages calls. Because there were some assumption/requirements not properly documented yet, all the components needed to ensure correct functioning.
  • Currently, there is missing a debug tool for cross message calls. It would be really helpful to have a tool, maybe external, that tracks the lifecycle of the cross network messages, i.e. where the message submitted, which subnet is the message now, is the message committed into the postbox and etc... This would help debug cross network message execution.
  • Checking subnet.circSupply. Not sure if this variable is tracked correctly. This value is deducted by IPCEnvelop.value regardless message type? But only increased (after bootstrap) when message is Transfer. DONE: only deducted for Transfer
  • There should be a debug tool to check if the message execution is successful or failed. If failed, should be possible to show the error.
  • Consistent way to encode ret bytes. The way that encodes the error is using encodeWithSelector, but for L2+ error is just encode, it's kind of confusing for debugging to tell which decode method to use. DONE - now maps to custom error. 6b5901d
  • Emit event in test should be removed. Events should be emitted in actual code, not from test, otherwise the expect becomes useless. @cryptoAtwill https://book.getfoundry.sh/cheatcodes/expect-emit it is not emitted from test. This is how the testing framework works you emit what you expect to see in the next call.
  • Fix duplicate nonce issue. DONE - 34102cc
  • Do not transfer funds for call message DONE - 6a6c53f

@karlem karlem changed the title Support l2 plus feat: support l2 plus Oct 11, 2024
@karlem karlem marked this pull request as ready for review October 11, 2024 20:03
@karlem karlem requested a review from a team as a code owner October 11, 2024 20:03
Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments.

contracts/contracts/gateway/GatewayMessengerFacet.sol Outdated Show resolved Hide resolved
contracts/contracts/gateway/GatewayMessengerFacet.sol Outdated Show resolved Hide resolved
contracts/contracts/gateway/router/CheckpointingFacet.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/AssetHelper.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/CrossMsgHelper.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/SubnetIDHelper.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/LibGateway.sol Show resolved Hide resolved
contracts/contracts/lib/CrossMsgHelper.sol Outdated Show resolved Hide resolved
contracts/test/integration/L2PlusSubnet.t.sol Outdated Show resolved Hide resolved
contracts/test/integration/L2PlusSubnet.t.sol Show resolved Hide resolved
contracts/test/integration/L2PlusSubnet.t.sol Show resolved Hide resolved
contracts/test/integration/L2PlusSubnet.t.sol Show resolved Hide resolved
contracts/test/integration/L2PlusSubnet.t.sol Outdated Show resolved Hide resolved
@cryptoAtwill
Copy link
Contributor

@karlem Another question I have is regarding nonce. Say a bottom up message is created in L3, the nonce will be set by the local network. Then in L2, will the cross network message nonce change? If so, then the id (keccak) will change too?

Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments.

contracts/contracts/errors/IPCErrors.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/LibGateway.sol Show resolved Hide resolved
contracts/contracts/lib/LibGateway.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/LibGateway.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/LibGateway.sol Outdated Show resolved Hide resolved
contracts/test/helpers/TestUtils.sol Outdated Show resolved Hide resolved
contracts/test/helpers/TestUtils.sol Outdated Show resolved Hide resolved
contracts/test/integration/L2PlusSubnet.t.sol Outdated Show resolved Hide resolved
contracts/test/integration/L2PlusSubnet.t.sol Outdated Show resolved Hide resolved
@karlem
Copy link
Contributor Author

karlem commented Oct 24, 2024

@karlem Another question I have is regarding nonce. Say a bottom up message is created in L3, the nonce will be set by the local network. Then in L2, will the cross network message nonce change? If so, then the id (keccak) will change too?

Hmm that's a good point. I will think about how to mitigate this.

@karlem
Copy link
Contributor Author

karlem commented Oct 28, 2024

@cryptoAtwill should be ready for another review :)

Copy link
Contributor

@cryptoAtwill cryptoAtwill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. But I do have a few follow up questions though:

  1. Does it work in calibration with a live 3 level network? Is there a testnet we can try or planned?
  2. propagateAll is called in the parent as part of bottom up checkpoint. Bottom up checkpoint is already quite gas costly, not sure if this will burst the gas even more.

@@ -131,6 +135,22 @@ library CrossMsgHelper {
return keccak256(abi.encode(crossMsg));
}

/// @notice Returns a deterministic hash for the given cross message. The hash remains the same accross different networks
/// because it doesn't include the network-specific nonce.
function toDeterministicHash(IpcEnvelope memory crossMsg) internal pure returns (bytes32) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess most of the time this is ok, but will this cause conflict if say someone sends the same message twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might collide, but it is only for logging so it should not matter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, this is not great; I'd rather have the protocol generate a message ID at origin and propagate that even inside the IpcEnvelope, even if it costs a few little more gas for the extra bytes.

Also, if this is only used for tracing, it's best to signal that by calling this method: toTracingId?

contracts/contracts/lib/LibGateway.sol Show resolved Hide resolved
@karlem
Copy link
Contributor Author

karlem commented Nov 20, 2024

Does it work in calibration with a live 3 level network? Is there a testnet we can try or planned?

It should. Whether is Calibration or not.

propagateAll is called in the parent as part of bottom up checkpoint. Bottom up checkpoint is already quite gas costly, not sure if this will burst the gas even more.

We don't really have any other option here. I don't think we really optimise for gas efficiency at this stage. Moving forward we might re-write the contracts L2+ as native actors to make it cheaper.

Copy link
Contributor

@cryptoAtwill cryptoAtwill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is no longer correct: https://github.com/consensus-shipyard/ipc/pull/1157/files#diff-71559bfe927afaf1a3980c01ffe9f4d15c77d58650b2d850bbae821de9c009d0R49.

This implicitly assumes the token behind envelop.value is the native token in the origin subnet. If say I want to trigger transfer from address A to address B in L1 and I'm sending from L3, why should I attach that amount in msg.value?

It comes back to the definition of envelop.value, is it just the native token transfer in the target subnet. If this is the case, it does not make sense to have this check.

@karlem
Copy link
Contributor Author

karlem commented Nov 27, 2024

@cryptoAtwill The issue was resolved by not using or supporting native token transfers in the Call message.

@karlem karlem mentioned this pull request Dec 5, 2024
@cryptoAtwill
Copy link
Contributor

This looks good to me. Just one more thing regarding the tests, shall we also add nonce check to bottom up, just make sure it's correct?

Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial feedback. Removing the ability to send value in calls is a no-go. See inline comments for more context. It will be very common for subnets denominated in the same asset to want to interact with one another sending value and calling some logic in an atomic manner.

@karlem can you please detail the rationale for having removed this? Was it due to the risk of supply source mismatches across the hierarchy during a multi-hop? We had logic to prevent this here:

if (applyType == IPCMsgType.BottomUp) {
// We're traversing up, so if we're the first hop, we reject if the subnet was ERC20.
// If we're not the first hop, a child propagated this to us, they made a mistake and
// and we don't have enough info to evaluate.
reject = from.getParentSubnet().equals(s.networkName) && from.getActor().hasSupplyOfKind(AssetKind.ERC20);
} else if (applyType == IPCMsgType.TopDown) {
// We're traversing down.
// Check the next subnet (which can may be the destination subnet).
reject = to.down(s.networkName).getActor().hasSupplyOfKind(AssetKind.ERC20);
}
if (reject) {
if (crossMessage.kind == IpcMsgKind.Transfer) {
revert MethodNotAllowed("propagation of `Transfer` messages not suppported for subnets with ERC20 supply");
}
}

So curious to hear why that was insufficient, or why strengthening the validation to detect supply source mismatches was a no-go?

contracts/contracts/lib/CrossMsgHelper.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/CrossMsgHelper.sol Outdated Show resolved Hide resolved
@@ -131,6 +135,22 @@ library CrossMsgHelper {
return keccak256(abi.encode(crossMsg));
}

/// @notice Returns a deterministic hash for the given cross message. The hash remains the same accross different networks
/// because it doesn't include the network-specific nonce.
function toDeterministicHash(IpcEnvelope memory crossMsg) internal pure returns (bytes32) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, this is not great; I'd rather have the protocol generate a message ID at origin and propagate that even inside the IpcEnvelope, even if it costs a few little more gas for the extra bytes.

Also, if this is only used for tracing, it's best to signal that by calling this method: toTracingId?

contracts/contracts/lib/CrossMsgHelper.sol Outdated Show resolved Hide resolved
contracts/contracts/lib/LibGateway.sol Outdated Show resolved Hide resolved
@karlem karlem requested a review from raulk January 9, 2025 14:41
Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@karlem this seems like it's close to a merge. Any outstanding changes? My only nit is that now what we've introduced the "original nonce", we should consider renaming the nonce localNonce to disambiguate. WDYT?

@raulk raulk self-requested a review January 14, 2025 15:21
@karlem karlem merged commit 967829f into main Jan 14, 2025
15 checks passed
@karlem karlem deleted the support-l2-plus branch January 14, 2025 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants