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

node: Add Transfer Verifier mechanism #4169

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

johnsaigle
Copy link
Contributor

@johnsaigle johnsaigle commented Nov 22, 2024

Transfer Verifier

This PR is a joint effort between myself, @pleasew8t, and @mdulin2.

Overview

This PR introduces the Transfer Verifier, a mechanism to improve Token
Bridge security by validating transaction receipts that correspond to Publish
Message events emitted from the Core Bridge.

When the core bridge emits a Publish Message event, the Transfer Verifier does
the following:

  • Fetches the transaction artifacts corresponding to the Publish Message event
    (e.g. an Ethereum receipt transaction)
  • Checks whether the publish message corresponds to a token transfer and is
    emitted by the Token Bridge contract
  • Cross-references the data in the publish message against actual deposits or
    transfers into the token bridge
  • If there is publish message with no valid corresponding deposit or transfers
    into the Token Bridge, the program emits an error to signal to the node that
    there is a security incident.

This provides protection in the event that an attacker devises a way to emit
arbitrary publish message events from the core contract or else somehow tricks
the Token Bridge into making token transfer calls without any backing tokens.
While existing Integrity Checks (Governor and Accountant) would help mitigate
this attack, Transfer Verifier acts as another layer of protection.

What this PR contains

This PR contains two initial implementations for Ethereum and Sui.

Both implementations are bundled into a Transfer Verifier package. This package
is used by a new transfer-verifier binary that exists as a subcommand within
guardiand. This binary can be configured to monitor the mainnet token bridge and core
bridge contracts for this chain.

Testing

Both implementations contain a large number of unit tests. They can be invoked
as a normal part of testing the node via make test.

The Ethereum implementation has integration tests built into the Tilt set-up:

  • After the eth-devnet container is set-up, the transfer-verifier binary is
    invoked to monitor the Ethereum devnet
  • A shell script invokes a malicious call to the core contract via cast (part
    of the Foundry toolset)
  • The transfer-verifier binary logs an error when detecting the malicious call
  • A separate monitoring container looks for the presence of this error log and
    returns a success when it appears

In order to impersonate a call from the Token Bridge to the Core Bridge, the
--auto-impersonate flag was added to the anvil invocation in eth-devnet.

Future Work

The Transfer Verifier package is used here as a standalone binary. Its
fundamental output are logs that can be monitored and manually acted upon.

The next iteration of this project will be to integrate the package directly in
the Ethereum and Sui watchers

Integration into the Ethereum watcher

Ethereum has an advantage over the Sui implementation due to the robust logging
and tooling present in the EVM and its ecosystem. We can validate the relevant
information for Transfer Verification in a highly robust way and detect
violations of its invariants with certainty.

As a result, we can use the logic in the Transfer Verifier to block
transactions at the level of the Ethereum watcher so that malicious message
publications will not be processed by the network.

Integration into the Sui watcher

The nature of Sui makes it relatively difficult to provide guarantees about the
state of the core bridge relative to the token bridge. The Sui implementation
cannot provide the same guarantees as the Ethereum implementation due to limits
inherent in the chain.

It is still possible to integrate this protection into the Sui watcher but it
would be better to delay messages for e.g. 24h in this case rather than reject
them outright.

Additional implementations for other chains

This is an abstract algorithm that can in theory be applied to any chain supported by the Token Bridge.
Designing a Solana implementation of this script would be a fruitful choice
given that it is one of the chains that makes the most use of the Token Bridge.

@johnsaigle johnsaigle added enhancement New feature or request node evm sui labels Nov 22, 2024
@johnsaigle johnsaigle force-pushed the transfer-verifier-rebased branch from 0f118a9 to c711b03 Compare November 26, 2024 20:47
@johnsaigle johnsaigle marked this pull request as ready for review November 26, 2024 21:22
Copy link
Contributor

@bruce-riley bruce-riley left a comment

Choose a reason for hiding this comment

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

Not done reviewing yet, but I wanted to post the comments I have so far.

node/cmd/transfer-verifier/transfer-verifier-evm.go Outdated Show resolved Hide resolved
node/cmd/transfer-verifier/transfer-verifier-evm.go Outdated Show resolved Hide resolved
&txverifier.TVAddresses{
CoreBridgeAddr: common.HexToAddress(*evmCoreContract),
TokenBridgeAddr: common.HexToAddress(*evmTokenBridgeContract),
// TODO: should be a CLI parameter so that we could support other EVM chains
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you want to address this TODO now? Seems like would be easy to add.

Copy link
Contributor Author

@johnsaigle johnsaigle Dec 9, 2024

Choose a reason for hiding this comment

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

The code should hopefully be generic enough to work with other EVM chains but the only testing I've done is with Ethereum. For that reason my preference would be to merge this as effectively Ethereum-only for now, with the idea that transfer verification could be extended to other chains later if we need it.

Copy link
Contributor

@bruce-riley bruce-riley Dec 11, 2024

Choose a reason for hiding this comment

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

Well, this means this will only work on ETH mainnet. Users might have the impression that they can set the RPC and the core and token bridge address for either Sepolia or any other EVM, but things will not work correctly because the WETH address would be wrong, right?

node/cmd/transfer-verifier/transfer-verifier-sui.go Outdated Show resolved Hide resolved
node/cmd/transfer-verifier/transfer-verifier-sui.go Outdated Show resolved Hide resolved
node/cmd/transfer-verifier/transfer-verifier-sui.go Outdated Show resolved Hide resolved
@johnsaigle johnsaigle force-pushed the transfer-verifier-rebased branch from 8661aea to 6ae4219 Compare December 9, 2024 20:22
func (s *Subscription) Subscribe(ctx context.Context) {
go func() {
for {
select {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need to handle ctx.Done()?

&txverifier.TVAddresses{
CoreBridgeAddr: common.HexToAddress(*evmCoreContract),
TokenBridgeAddr: common.HexToAddress(*evmTokenBridgeContract),
// TODO: should be a CLI parameter so that we could support other EVM chains
Copy link
Contributor

@bruce-riley bruce-riley Dec 11, 2024

Choose a reason for hiding this comment

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

Well, this means this will only work on ETH mainnet. Users might have the impression that they can set the RPC and the core and token bridge address for either Sepolia or any other EVM, but things will not work correctly because the WETH address would be wrong, right?

}

// Get the full transaction receipt for this log.
receipt, txReceiptErr := tv.evmConnector.TransactionReceipt(ctx, vLog.Raw.TxHash)
Copy link
Contributor

Choose a reason for hiding this comment

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

I know it's not applicable in this standalone app, but it will matter when this is bolted into the watcher. The watcher will have already called TransactionReceipt, so you will want to restructure this code so that can be passed in.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I've modified this in the latest commit

@johnsaigle johnsaigle force-pushed the transfer-verifier-rebased branch from 82ed1c1 to 9cce4ec Compare December 16, 2024 21:19
@johnsaigle
Copy link
Contributor Author

@bruce-riley The hard-coding around the NATIVE_CHAIN_ID and the WETH address should be fixed in the latest commit. Now the chain ID is read from the connector directly and the WETH_equivalent is a mandatory parameter.

In order to validate the chain ID, I added 1337 to the SDK's list of supported chains as this is the ID used by CI/Tilt. Without this line, parsing the chain ID 1337 from the connector and converting it to a vaa.ChainID seemed fragile as 1337 wasn't a valid ID.

Let me know if you think this is an acceptable solution. I think the alternative approach would be to store simply a uint16 for the chain ID in the TransferVerifier, which would open the risk of invalid chain IDs being stored within it and requiring conversions between uint16s and vaa.ChainIDs in many places.

@johnsaigle
Copy link
Contributor Author

After discussing with Bruce, I've added a mapping from EVM --> Wormhole chain IDs where appropriate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request evm node sui
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants