hyperlane-xyz / hyperlane-monorepo

The home for Hyperlane core contracts, sdk packages, and other infrastructure
https://hyperlane.xyz
Other
239 stars 270 forks source link

Epic: VMs without keccak should be supported by Hyperlane #2399

Open nambrot opened 1 year ago

nambrot commented 1 year ago

Context

The EVM is the most popular and widespread VM for blockchains to offer smart contracting abilities as Ethereum led the ways. Many alt-L1s and rollups specifically emphasize their EVM compatibility to attract developers as solidity/EVM has the best resources for developers to build their applications. Additionally, existing applications and code for Ethereum can be reused mostly out of the box on other EVM chains. Hyperlane's architecture was conceived to be able to support many diverse environments and the Sealevel and Fuel implementations in progress show that the surface area is rather doable.

However, one of the big assumptions that the Hyperlane protocol makes is the usage of keccak as a hashing function to construct 1) message IDs (by hashing the message metadata + body) and 2) build merkle trees on-chain.
3) digest that validators sign

The main use of keccak is to support a merkle tree which commits to all messages in the tree for both censorship resistance and fraud proof purposes. On the destination chain, the merkle tree can be used with signatures from validators over the root of the tree alongside a merkle proof to prove inclusion of the message. For processing, the message ID is used to do replay protection. Invalid signatures can be given as input to prove their lack of validity against.

This ticket tries to capture what supporting VMs without keccak requires or could look like (referred to as alt-VMs in this ticket)

Option 1: Status-quo, i.e. no/minimal changes to the protocol itself

Contracts

Agents

Pros:

Cons:

Open Questions:

Option/Step 2

Have mailboxes support multiple merkle trees (in environments that support multiple hashing functions)?

Examples

Related Issues

2388

2258

nambrot commented 1 year ago

@serejke @HariSeldon23 would love your read on this and see if that makes sense for y'all

HariSeldon23 commented 1 year ago

Thanks for the detailed response. The way I normally look at these types of issues are to determine what are my non negotiables and then work from there.

For me, it would be:

With that being said from my interpretation of the above solution there are the following issues:

So then it comes down to the age old question of a short term fix for today vs planning years in advance. The Bridge Wars are a competitive space and while market share is important today, it's going to be a battle that occurs over, at a minimum, the next decade. Keccak will eventually be replaced. Quantum computing will arrive. Lock/Burn and Mint bridge use cases will eventually be viewed as quaint. So a short term solution is just kicking the can down the road. Which tbf is what most competitors will do.

My preferred approach would be:

There are quite a few short term negatives to this approach:

nambrot commented 1 year ago

Thanks for your input @HariSeldon23 ! Your non-negotiable unfortunately runs into another non-negotiable, in that the message ID needs to commit to the contents of the message. If you just do it around the metadata, then you lose a whole host of benefits from that commitment unless you write the whole message to storage. If you didn't need the commitment, you could just use the nonce of the message for identification purposes, i.e. something like ${mailboxAddress}-${originDomain}-${nonce} For replay protection, you'd probably still want commitments of the message though, but maybe we just don't call that ID.

It's not clear to me that processing merkle trees is a non-negotiable though, especially since it feels impossible to support. Censorship resistance is a very nice property, but I don't think its a must have (and can always be added later)

HariSeldon23 commented 1 year ago

Ok, that's fair enough. I realize I made quite a few bad assumptions in my original reply, now that I've done a deeper dive, I believe I've got a better understanding.

Let's talk about a connection between Ethereum (Domain = 1) and BlakeChain (Domain = 987). We will use the multisigISM.

We want to send a message to BlakeChain which should be used to execute a contract Counter (Address = 12345) to increment a variable counter. On BlakeChain we have a modified Mailbox contract which approximates the Mailbox contract on Ethereum.

So let's break down into how that will be achieved:

So this covers sending a message to Ethereum from BlakeChain.

Now let's say after the successful sending of the message referenced above, we now want to send a message from BlakeChain to Ethereum. We just want to send a simple "Hello World" string.

Have I understood this correctly? If so, then I agree with your assessment and this provides a good starting point to start.

serejke commented 1 year ago

Thanks @nambrot for putting the ideas together. I confirm the proposed solution to support non-EVM chains should work.

keccak usages

First, I want to suggest a different angle of thinking about keccak and Hyperlane in general. We need to clearly separate different usages of keccak:

MerkleTree

Mailbox (outbox) is building MerkleTree with leaf = message ID and keccak256(left, right) used to hash non-leaf nodes.

Note: hashing function of the message ID may be different from the hashing function of the MerkleTree. The MerkleTree treats message IDs as just bytes32

Message ID

Calculated as

ID = keccak256([
    VERSION,
    count(),
    localDomain,
    sender,
    destinationDomain,
    recipientAddress,
    messageBody
])

ECDSA-signed digest of the validators' checkpoints

Validators use ECDSA signature over keccak256-hashed digest, calculated as:

hash = keccak256(keccak256(domainId, mailbox_address, "HYPERLANE") + root + nonce + message_id)

messageHash = keccak256("\x19Ethereum Signed Message:\n" + hash.length + message)
sig = ecdsa_sha256_sign(messageHash)
rsig = recoverable(sig) { with v = 0 | 1 }
ethsig = rsig_to_ethsig(rsig) { with v = 27 | 28 }
signature = ethsigEip155 { with v = 35 + chainId * 2 + 0 | 1 }

Note: validators can sign any checkpoints using any algorithm (ECDSA or EdDSA), depending on the destination chain's needs. The keccak256 is only a requirement of ECDSA (needs 32-byte input), but not of EdDSA (accepts arbitrary input).

Mailbox: Inbox and Outbox

Secondly, I prefer to think that Mailbox consists of 2 independent parts: Inbox and Outbox, which do not share a common state.

Inbox

Inbox is a stateless handler and router of incoming messages. It does not necessarily need to be a standalone contract.

In Solidity, the only Inbox's own state is delivered used to guarantee at-most-once processing.

In non-EVM chains, there may be a different way to achieve that:

Outbox

Outbox is the stateful contract building MerkleTrees.

Replay protection

Thirdly, I might be missing the meaning of "replay protection". I get it as a guarantee that the same message will not be processed on two different chains. I believe this is not a problem. Validator signatures depend on both origin and destination domain IDs, and the receiving Inbox checks self.domainId === destinationDomainId

Rehashing on off-chain agents

I think @HariSeldon23 had a great idea on the middle-wares. I think off-chain agents can actually re-hash both the message ID and MerkleTree for the destination chain's needs.

Considering EVM -> altVM and altVM -> EVM

Now let's consider these two use cases separately

EVM -> altVM

altVM -> EVM

Challenges

serejke commented 1 year ago

@HariSeldon23 I think the ordering of actions in the Ethereum -> BlakeChain and BlakeChain -> Ethereum are inaccurate.

For example, Ethereum -> BlakeChain goes as (extended):

The below diagram should help: image

nambrot commented 1 year ago

Thirdly, I might be missing the meaning of "replay protection". I get it as a guarantee that the same message will not be processed on two different chains. I believe this is not a problem. Validator signatures depend on both origin and destination domain IDs, and the receiving Inbox checks self.domainId === destinationDomainId

Replay protection we refer to as the guarantee that a message that was already processed, can't be replayed again, i.e. processed again (i.e. what you call at-most-once processing)

I think off-chain agents can actually re-hash both the message ID and MerkleTree for the destination chain's needs.

I'm not sure I would call this "re-hashing", but hashing a message for its ID differently depending on the destination chain is what I suggested I believe. My main point is that you could start without needing to actually build the alt-VM merkle tree if you just start with only verifying signatures over the message ID (instead of in addition to the merkle tree proof). Building the alt-VM merkle tree is a nice-to-have only IMO (since you can't slash for misbehavior on the origin chain cross-VM)

HariSeldon23 commented 1 year ago

@HariSeldon23 I think the ordering of actions in the Ethereum -> BlakeChain and BlakeChain -> Ethereum are inaccurate.

For example, Ethereum -> BlakeChain goes as (extended):

  • Ethereum

    • Ethereum.Mailbox.Dispatch(987, 12345, calldata)
    • The messageID is inserted into the Merkle tree as a new leaf node. Ethereum.Mailbox.Root is updated with Merkle tree
  • Validators

    • Listen to Mailbox.Dispatch messages
    • wait for chain finalization
    • build an off-chain MerkleTree
    • sign checkpoints and save them to S3
  • Relayers

    • Listen to Mailbox.Dispatch messages
    • Call BlakeChain.ISM(...) to get N validators that should achieve consensus
    • Wait for M/N validators to achieve consensus by periodically downloading their S3 checkpoint jsons
    • Try to deliver the message by calling Mailbox.process
  • BlakeChain

    • Duplicate check occurs on MessageID
    • MultisigISM is called with the provided signatures

The below diagram should help: image

This is fantastic. Thank you. This whole thread has been incredibly insightful. Going to work through a few small PoC's and will then track back here if anything comes out of it that hasn't been addressed here