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

MultiOffRamp - family-agnostic messages #1117

Merged
merged 22 commits into from
Jul 5, 2024

Conversation

elatoskinas
Copy link
Collaborator

@elatoskinas elatoskinas commented Jun 28, 2024

Motivation

The Multi-OffRamp can be converted to be family agnostic by using more generic Any2EVM structures and breaking the reliance on abi hashing

Solution

  • Remove metadataHash and use an implicit metadata hash composed of (prefix, srcChainSelector, destChainSelector, onRampAddress)
  • Convert EVM2EVMMessage to Any2EVMRampMessage, which uses a generic bytes sender
  • Decouple messageId from hash(message)
  • Out-of-scope: EVM2Any OnRamp changes

Sample on why hashes between Any2X and X2Any are no longer equivalent:

Solana:
  - messageId: hash(Sol2AnyMessage msg)
  - (Sol2Any message could contain some extra fields that are SOL-specific)

Off-chain:
  - Sol2AnyMessage -> Any2AnyMessage -> Any2EVMMessage
  - Route SOL -> Ethereum

Ethereum:
  - messageId: hash(Any2EVMMessage msg)
  - (Any2EVMMessage will contain some EVM-specific fields, but not the SOL specific fields)

hash(Sol2AnyMessage) != hash(Any2EVMMessage)

_metadataHash(sourceChainSelector, sourceConfigUpdate.onRamp, Internal.EVM_2_EVM_MESSAGE_HASH);
currentConfig.onRamp = sourceConfigUpdate.onRamp;
if (currentOnRamp.length == 0) {
// TODO: is this check sufficient / is an all-0 check needed?
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thoughts on this? Do we need a more complex validation that checks that at least 1 byte is non-zero? I'm thinking it would add much more complexity and contract size, maybe we could do this validation off-chain instead

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah I think this is fine. If the configured onramp is junk then messages will be unexecutable until we configure the correct one

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This validation adds about ~6.4k gas to applySourceChainConfigUpdates in the median case, and a 0.07KB contract size increase.

  function validateNonZeroBytesAddress(bytes memory data) public pure {
      uint256 length = data.length;
      uint256 chunkCount = length / 32;

      // Iterate over the `bytes` array in chunks of 32 bytes
      for (uint256 i = 0; i < chunkCount; i++) {
          bytes32 chunk;
          assembly {
              chunk := mload(add(data, add(32, mul(i, 32))))
          }
          if (chunk != 0) {
              return;
          }
      }

      // Check any remaining bytes that do not fit into a `bytes32` chunk
      for (uint256 i = chunkCount * 32; i < length; i++) {
          if (data[i] != 0) {
              return;
          }
      }

      revert ZeroAddressNotAllowed();
  }

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

until we configure the correct one

We are disallowing on ramp updates though (since we assume onramp change = offramp upgrade), so a misconfig would force us into a re-deployment

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't mean code change, I mean just like we accidentally configure say 0xa as the onramp when the real one is 0x123. Messages just will fail to execute, then we adjust offramp to point to 0x123 and they will succeed

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

then we adjust offramp to point to 0x123 and they will succeed

Yeah so this is not possible because of this check:

      if (currentOnRamp.length == 0) {
         // new chain selector
      } else if (
        keccak256(currentOnRamp) != keccak256(newOnRamp)
      ) {
        revert InvalidStaticConfig(sourceChainSelector);
      }

The offramp adjustment would be a complete redeployment of the offramp

Copy link
Collaborator

Choose a reason for hiding this comment

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

ah and that check is there because we want to prevent minSeqNr overwrites?

Copy link
Collaborator Author

@elatoskinas elatoskinas Jul 2, 2024

Choose a reason for hiding this comment

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

Yeah if we allowed overwriting the OnRamp, it would have some implications:

  • chain => seqNumbers => executionStates would still be kept even though the OnRamp has changed (maybe not an issue since it's off-ramp specific)
  • The same message could accidentally be included in the OffRamp and be replayed (since the _hash changes due to the different OnRamp, i.e. metadataHash)
  • Committed roots are not cleared, so we would have different commit roots with different metadata hashes

I was always going under the assumption that the metadataHash must remain immutable for each OffRamp, to preserve the OnRamp <-> OffRamp dependency

Copy link
Contributor

github-actions bot commented Jun 28, 2024

LCOV of commit eadb128 during Solidity Foundry #6226

Summary coverage rate:
  lines......: 98.6% (1816 of 1841 lines)
  functions..: 96.3% (337 of 350 functions)
  branches...: 90.5% (751 of 830 branches)

Files changed coverage rate: n/a

@elatoskinas elatoskinas force-pushed the feat/any2evm-offramp-structs branch from 0566ee7 to ffefa01 Compare July 2, 2024 12:48
@elatoskinas elatoskinas marked this pull request as ready for review July 2, 2024 12:50
@elatoskinas elatoskinas requested review from makramkd and a team as code owners July 2, 2024 12:50
## Motivation

Recent PR #1117 implemented Any2EVM message hashing, so we need to
update the MessageHasher for EVM to match the new onchain
implementation.

## Solution

Update the message hasher to match the `_hash(Any2EVMRampMessage)`
implementation in Internal.sol.
currentConfig.onRamp = sourceConfigUpdate.onRamp;
currentConfig.minSeqNr = 1;
if (currentOnRamp.length == 0) {
if (newOnRamp.length == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think will be more efficient to check the newOnRamp first. Most probably the currentOnRamp will already have an address.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Length checks are cheap so it wouldn't be substantial, but in this case this order makes sense because the onRamp can only be set if it was never set before (we treat it as immutable). newOnRamp.length == 0 checks are not required if the currentOnRamp is checked because we're checking equality directly

@elatoskinas elatoskinas enabled auto-merge (squash) July 5, 2024 16:04
@elatoskinas elatoskinas merged commit 5ca3364 into ccip-develop Jul 5, 2024
105 checks passed
@elatoskinas elatoskinas deleted the feat/any2evm-offramp-structs branch July 5, 2024 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants