GithubHelp home page GithubHelp logo

ava-labs / teleporter Goto Github PK

View Code? Open in Web Editor NEW
45.0 15.0 21.0 6.63 MB

EVM cross-chain messaging protocol built on top of Avalanche Warp Messaging

License: Other

Go 39.15% Solidity 55.96% Shell 4.89%

teleporter's Introduction

teleporter

To get started with building applications on top of Teleporter, refer to the avalanche-starter-kit repository. This README is focused on the development of the Teleporter protocol itself.

Teleporter is an EVM compatible cross-subnet communication protocol built on top of Avalanche Warp Messaging (AWM), and implemented as a Solidity smart contract. It provides a mechanism to asynchronously invoke smart contract functions on other EVM blockchains within Avalanche. Teleporter provides a handful of useful features on top of AWM, such as specifying relayer incentives for message delivery, replay protection, message delivery and execution retries, and a standard interface for sending and receiving messages within a dApp deployed across multiple subnets.

It's important to understand the distinction between Avalanche Warp Messaging and Teleporter. AWM allows subnets to communicate with each other via authenticated messages by providing signing and verification primitives in Avalanchego. These are used by the blockchain VMs to sign outgoing messages and verify incoming messages.

The Teleporter protocol, on the other hand, is implemented at the smart contract level, and is a user-friendly interface to AWM, aimed at dApp developers. All of the message signing and verification is abstracted away from developers. Instead, developers simply call sendCrossChainMessage on the TeleporterMessenger contract to send a message invoking a smart contract on another subnet, and implement the ITeleporterReceiver interface to receive messages on the destination subnet. Teleporter handles all of the Warp message construction and sending, as well as the message delivery and execution.

To get started with using Teleporter, see How to Deploy Teleporter Enabled Subnets on a Local Network

Deployed Addresses

Contract Address Chain
TeleporterMessenger 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf All chains, all networks
TeleporterRegistry 0x7C43605E14F391720e1b37E49C78C4b03A488d98 Mainnet C-Chain
TeleporterRegistry 0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228 Fuji C-Chain

A Note on Versioning

Release versions follow the semver convention of incompatible Major releases, and backwards-compatible Minor and Patch releases. A new Major version is released whenever the TeleporterMessenger bytecode is changed, due to the use of Nick's method to deploy the contract to the same address on all chains (see Teleporter Contract Deployment for details). Minor and Patch versions may pertain to contract changes that do not change the TeleporterMessenger bytecode, or to changes in the test frameworks.

Setup

Initialize the repository

  • Get all submodules: git submodule update --init --recursive

Dependencies

  • Ginkgo for running the end-to-end tests.
  • Docker and Docker Compose v2 for running the local test network.
    • The docker image installs the following:

Structure

  • contracts/ is a Foundry project that includes the implementation of the TeleporterMessenger contract and example dApps that demonstrate how to write contracts that interact with Teleporter.
  • abi-bindings/ includes Go ABI bindings for the contracts in contracts/.
  • tests/ includes integration tests for the contracts in contracts/, written using the Ginkgo testing framework.
  • utils/ includes Go utility functions for interacting with the contracts in contracts/. Included are Golang scripts to derive the expected EVM contract address deployed from a given EOA at a specific nonce, and also construct a transaction to deploy provided byte code to the same address on any EVM chain using Nick's method.
  • scripts/ includes bash scripts for interacting with Teleporter in various environments, as well as utility scripts.
    • abi_bindings.sh generates ABI bindings for the contracts in contracts/ and outputs them to abi-bindings/.
    • lint.sh performs Solidity and Golang linting.
    • scripts/local/ includes scripts for running Teleporter in Docker.
  • docker/ includes configurations for a local, containerized setup of Teleporter.

E2E tests

In addition to the docker setup, end-to-end integration tests written using Ginkgo are provided in the tests/ directory. E2E tests are run as part of CI, but can also be run locally. Any new features or cross-chain example applications checked into the repository should be accompanied by an end-to-end tests. See the Contribution Guide for additional details.

To run the E2E tests locally, you'll need to install Gingko following the instructions here.

Then run the following command from the root of the repository:

./scripts/local/e2e_test.sh

Run specific E2E tests

To run a specific E2E test, specify the environment variable GINKGO_FOCUS, which will then look for test descriptions that match the provided input. For example, to run the Calculate Teleporter message IDs test:

GINKGO_FOCUS="Calculate Teleporter message IDs" ./scripts/local/e2e_test.sh

A substring of the full test description can be used as well:

GINKGO_FOCUS="Calculate Teleporter" ./scripts/local/e2e_test.sh

The E2E tests also supports GINKGO_LABEL_FILTER, making it easy to group test cases and run them together. For example, to run all E2E tests for the example cross chain applications:

	ginkgo.It("Send native tokens from subnet A to B and back",
		ginkgo.Label("cross chain apps"),
		func() {
			flows.NativeTokenBridge(LocalNetworkInstance)
		})
GINKGO_LABEL_FILTER="cross chain apps" ./scripts/local/e2e_test.sh

Run the E2E tests on another network

The same E2E test flows can be executed against external network by setting the proper environment variables in .env.testnet and .env, and running the following commands:

cp .env.example .env # Set proper values after copying.
./scripts/testnet/run_testnet_e2e_flows.sh

The user wallet set in .env must have native tokens for each of the Subnets used in order for the test flows to be able to send transactions on those networks. The Avalanche Testnet Faucet can be used to obtain native tokens for certain public testnet Subnets.

Upgradability

The Teleporter contract is non-upgradeable and can not be changed once it is deployed. This provides immutability to the contracts, and ensures that the contract's behavior at each address is unchanging. However, to allow for new features and potential bug fixes, new versions of the Teleporter contract can be deployed to different addresses. The TeleporterRegistry is used to keep track of the deployed versions of Teleporter, and to provide a standard interface for dApps to interact with the different Teleporter versions.

TeleporterRegistry is not mandatory for dApps built on top of Teleporter, but dApp's are recommended to leverage the registry to ensure they use the latest Teleporter version available. Another recommendation standard is to have a single canonical TeleporterRegistry for each Subnet chain, and unlike the Teleporter contract, the registry does not need to be deployed to the same address on every chain. This means the registry does not need a Nick's method deployment, and can be at different contract addresses on different chains.

For more information on the registry and how to integrate with Teleporter dApps, see the Upgradability doc.

Deploy Teleporter to a Subnet

From the root of the repo, the TeleporterMessenger contract can be deployed by calling

./scripts/deploy_teleporter.sh --version <version> --rpc-url <url> [OPTIONS]

Required arguments:

  • --version <version> Specify the release version to deploy. These will all be of the form v1.X.0. Each Teleporter version can only send and receive messages from the same Teleporter version on another chain. You can see a list of released versions at https://github.com/ava-labs/teleporter/releases.
  • --rpc-url <url> Specify the rpc url of the node to use.

Options:

  • --private-key <private_key> Funds the deployer address with the account held by <private_key>

To ensure that Teleporter can be deployed to the same address on every EVM based chain, it uses Nick's Method to deploy from a static deployer address. Teleporter costs exactly 10eth in the subnet's native gas token to deploy, which must be sent to the deployer address.

deploy_teleporter.sh will send the necessary native tokens to the deployer address if it is provided with a private key for an account with sufficient funds. Alternatively, the deployer address can be funded externally. The deployer address for each version can be found by looking up the appropriate version at https://github.com/ava-labs/teleporter/releases and downloading TeleporterMessenger_Deployer_Address_<VERSION>.txt.

Alternatively for new Subnets, the TeleporterMessenger contract can be directly included in the genesis file as documented here.

Deploy TeleporterRegistry to a Subnet

There should only be one canonical TeleporterRegistry deployed for each chain, but if one does not exist, it is recommended to deploy the registry so Teleporter dApps can always use the most recent Teleporter version available. The registry does not need to be deployed to the same address on every chain, and therefore does not need a Nick's method transaction. To deploy, run the following command from the root of the repository:

./scripts/deploy_registry.sh --version <version> --rpc-url <url> --private-key <private_key> [OPTIONS]

Required arguments:

  • --version <version> Specify the release version to deploy. These will all be of the form v1.X.0.
  • --rpc-url <url> Specify the rpc url of the node to use.
  • --private-key <private_key> Funds the deployer address with the account held by <private_key>

deploy_registry.sh will deploy a new TeleporterRegistry contract for the intended release version, and will also have the corresponding TeleporterMessenger contract registered as the initial protocol version.

ABI Bindings

To generate Golang ABI bindings for the Solidity smart contracts, run:

./scripts/abi_go_bindings.sh

The auto-generated bindings should be written under the abi-bindings/ directory.

Docs

Resources

  • List of blockchain signing cryptography algorithms here.
  • Background on stateful precompiles here.
  • Background on BLS signature aggregation here.

teleporter's People

Contributors

geoff-vball avatar cam-schultz avatar michaelkaplan13 avatar gwen917 avatar minghinmatthewlam avatar feugenea avatar iansuvak avatar dependabot[bot] avatar meaghanfitzgerald avatar 0xscratch avatar martineckardt avatar nuttymoon avatar chand1012 avatar andyvargtz avatar darioush avatar

Stargazers

 avatar James Dobry avatar harry.avax avatar Perseverance Success avatar  avatar Laurent Knauss avatar looter_ avatar Claudio Hermida avatar AlwaysDumpling avatar  avatar SinTan1071 avatar  avatar Daniel Zarifpour avatar Bookland avatar moti avatar H.D.KIM avatar Benjamin Samuels avatar  avatar Arthur Guiot avatar GCP avatar Ashutosh Tripathi avatar tlentz avatar Yiğit Gürbulak avatar pacy avatar Konstantin avatar K0d3x avatar  avatar Kevin avatar Bryte Morio avatar  avatar illiYAAAlli avatar Patrick O'Grady avatar  avatar Shiven avatar İbrahim Sefa TUNA avatar Anderson avatar disco chuck avatar Vusal Ismayilov avatar Josh Hesketh avatar Onur Günduru avatar xinbenlv avatar  avatar Xander Dunn avatar  avatar Steven Gates avatar

Watchers

Gabriel Cardona avatar Arran Schlosberg avatar Richard Pringle avatar  avatar  avatar Saeid Mofrad avatar  avatar  avatar  avatar Steven Gates avatar  avatar Stephen Buttolph avatar Mickael avatar  avatar  avatar

teleporter's Issues

Document interaction of receiveTeleporterMessage and fallback functions

Context and scope
All contracts that receive from Teleporter need to implement ITeleporterReceiver.receiveTeleporterMessage, but if a contract doesn't implement this and has a fallback(), that will be called.

Discussion and alternatives
Confirm interaction of fallback with receiveTeleporterMessage call, does it always succeed? Consider adding unit test with example contract with a fallback.

OpenZeppelin Defender initial fixes

Context and scope
The repo just integrated the OpenZeppelin Defender that adds additional security checks, and the first report here has generated some low severity findings.

Discussion and alternatives
Certain alerts we purposely decided against, for example "Use custom errors". Also lots of alerts are from test files that we don't scrutinize as strictly as tests, but can also be addressed if wanted.

Contracts for fetching a value from another chain with an async callback

Context and scope
One possible use of Teleporter that could prove very helpful for dApp developers is the ability to query the state of a contract on another chain. The data being queried could be the current value of an oracle, the current state of an account, a random value from a VRF contract, etc.

The query would be sent to the other chain as a Teleporter message with a specific request ID. When the message is delivered to the destination, the specified data would be looked up on chain, and then submitted in a subsequent Teleporter message sent back to the origin chain. The delivery of that second message would invoke a callback providing the value to be used by the original requesting contract.

In order to make this pattern as easy as possible, create contracts that demonstrate this flow on top of Teleporter. Ideally, the interface can be generic enough such that dApps could inherit a CrossChainRequester or CrossChainProvider contract that handles most of the logic.

Open questions

  • Is it possible to make the interface generic of the type of the value being queried?
  • What's the best pattern for registering callbacks? Could either store the callback for a given request ID on the source chain contract, or pass it through the messages.
  • How to set the relayer incentive fees for each message? To start, a working demo that doesn't use any relayer fees is sufficient to demonstrate the use case.

Generalize installing avalanchego and subnet-evm releases

Context and scope
We have separate scripts of installing avalanchego and subnet-evm releases. The former was taken from the subnet-evm repo, and the latter was adapted to install a different release, but has similarities between the two.

Discussion and alternatives
The url format for the two repo's releases are different, and the default download paths are different.

Add Teleporter E2E test cases

Context and scope
There are a number of Teleporter-specific edge cases that we should add end-to-end tests for. Many of these are implicitly covered by unit tests already, but we should add integration level tests as well. Below are the test cases to add. As they are implemented, we should check the box and link the corresponding PR.

(Check next to test case means it's in progress; a check next to the PR means it's been merged)

  • Message execution runs out of gas, and execution needs to be retried
  • Adding an extra fee amount after a message is submitted, checking relayer gets total amount as reward
  • Relayer manually retries sending of specific receipts.
  • Test receiving a receipt multiple times has no effect
  • Submit a message, large validator set turn over before the message is delivered. Message can be resubmitted on the sending chain to be retried
  • Try resubmitting a message that has been slightly altered, ensure it doesn't work.
  • Try receiving the same message multiple times, ensure only the first one succeeds.
  • Try delivering a message from a relayer that isn't allowed, ensure it doesn't work.
  • Try re-executing a message that has previously succeeded, ensure it doesn't work.
  • Deliver message destined for contract that doesn't exist, should fail. Then deploy the contract to the intended address and retry message execution, should succeed.
  • Try delivering message to a different chain than it was meant for, should fail.
  • Relayer modifies a message in flight. This should fail warp verification

All of the above PRs merge into #93. Once that is merged, this ticket will be marked as complete.

Consider converting ReceiptQueue to library

Context and scope
ReceiptQueue is a contract deployed alongside the TeleporterMessenger contract. Logically, it makes more sense for this to be a library than a standalone contract. In addition to saving on deployment cost, there may be runtime gas savings as well.

Discussion and alternatives
We should also take a general pass of ReceiptQueue to identify any opportunities to save gas within the implementation itself.

Consider using assembly for low level call to avoid return bombs

Context and scope
When a message is delivered, an external call is made to the destinationAddress specified by the message sender where the amount of gas the call can use is specified in the message by the requiredGasLimit value. The return data from this call is not used and only the success of the call is recorded. Although a gas limit is set for the call, the external contract could force the transaction to use gas in excess of the specified limit due to an open issue with the Solidity compiler. This is possible because the TeleporterMessenger contract pays for memory expansion to accommodate the return data, but the external contract controls the size of that return data. Consider using assembly to explicitly prevent any memory from being allocated for the return data from the external call.

See reference here.

For instance, the change could look like:

        // Call the destination address of the message with the formatted call data.
        // Only provide the required gas limit to the sub-call so that the end application
        // cannot consume an arbitrary amount gas.
        bytes memory payload = abi.encodeCall(
            ITeleporterReceiver.receiveTeleporterMessage,
            (originChainID, message.senderAddress, message.message)
        );
        address target = message.destinationAddress;
        uint256 requiredGas = message.requiredGasLimit;
        bool success;
        assembly ("memory-safe") {
            success := call(
               requiredGas, // gas provided to the call
                target, // call target
                0, // zero value
                add(payload, 0x20), // input data
                mload(payload), // input data length
                0, // output
                0 // output size
            )
        }

        // If the execution failed, store a hash of the message in state such that its
        // execution can be retried again in the future with a higher gas limit (paid by whoever
        // retries).Either way, the message will now be considered "delivered" since the relayer
        // provided enough gas to meet the required gas limit.
        if (!success) {
            _storeFailedMessageExecution(originChainID, message);
            return;
        }

Open questions
Does call in assembly return false if the contract being call hits a revert statement?

Initial Teleporter runbook

Context and scope
We want to have a runbook for handling incident response and debugging Teleporter incidents.

Discussion and alternatives
The runbook should include helpful links, logs, etc. Also it can outline setps to debugging common problems, for example decoding Teleporter message bytes into Teleporter message fields, querying contract state, etc.

Publish audits

Context and scope
Publicize Teleporter audits in the repo.

Discussion and alternatives
Separate audits folder, decide on naming structure. Review how other open source projects publicize audits.

Upgrade Strategy

Context and scope
Currently Teleporter is a non upgradeable contract, but in this case any bugs are found, or new features would like to be added, we would need some form of upgrade strategy.

Discussion and alternatives

  1. Implementing a pass-through proxy that forwards Teleporter calls on to the correct implementation contract, where the individual implementations maintain their own state. - @minghinmatthewlam points out that this would make it difficult to handle the msg.sender value when receiving teleporter messages, since it would change with each new implementation.
  2. Using the standard upgradable proxy smart contract pattern. - In this case, we would need to be careful about the contract state and variable ordering. This would mean that the proxy contract needs to be deployed to the same contract on each chain using Nick's method, and that it would be possible for different Teleporter versions to send messages to one another. This is a non-starter as Teleporter depends on knowing that the messages it receives are from the same contract deployed bytecode deployed on another chain.
  3. Using a upgradable protocol registry. - Instead of using a proxy, applications will simply lookup the current address to be used from a registry. We will provide a small intuitive library that wraps the necessary calls with the proper look up.

Add TeleporterMessenger Go binding

Context and scope
We should add Go bindings for the TeleporterMessenger ABI. For example, the awm-relayer implements this separately, but that is decoupled from the contract itself. By hosting those bindings in this repo, we could tightly couple them by adding scripts or CI jobs to autogenerate the binding or verify that the checked in binding matches the contract.

Consider adding failure reason information to MessageExecutionFailed event

Context and scope
When a message fails to be executed in a call to receiveCrossChainMessage, the contract emits MessageExecutionFailed. It does not, however include any information as to why execution failed. The reasons for failure are numerous, but would generally be due to logical or format errors in the encoded calldata, or insufficient gas allocated in the Teleporter message.

Discussion and alternatives
We should consider adding the failure reason to MessageExecutionFailed

Open questions
We need to determine what the best way to encode a general failure reason in the log.

Monitor Teleporter messages received to have corresponding sends

Context and scope
TeleporterMessenger receive messages should always have a corresponding send message from the origin chain of the message. Looking to create a monitor that alerts us if a Teleporter message was received without a corresponding send message.

Discussion and alternatives
When receiving Teleporter message, we should have information about which source chain sent the message, as well as the message ID. With that information, can we find the corresponding sendCrossChainMessage on source chain?

Open questions
What if the send was sent many blocks ago? How many blocks should/can we look back?
How about chains that have enough stake weight and want to just randomly add receive messages without a corresponding send? (like the incident drill)

Teleporter Decoder

Context and scope
We want to provide decoding functionalities that will help make debugging Teleporter transactions easier.

Discussion and alternatives

  • Given a transaction to Teleporter address, there should be function selector, and data. Being able to decoding the selector to which Teleporter function, and decode data to function arguments
  • Decode Teleporter emitted log events to which event was emitted, and decode into event fields (topics and data)
  • Given a Teleporter message's bytes decode into Teleporter message with corresponding fields

Open questions
GIven a dapp contract's abi, can we decode the Teleporter message payload into the call arguments, i.e. figure out which function in the dapp is being called, and what function arguments are passed.

Use custom errors for reverts

Context and scope
Right now in Teleporter we do require checks and pass in a revert message. Replace these with custom errors, and remove the solhint.json config that has custom-errors warning set to be off so we do lint for the rule

Discussion and alternatives
Change require checks to using revert CustomError

Standalone application for running E2E test against live testnets

Context and scope
#93 creates Teleporter integration tests that are run against local containerized subnets. Having an application for running the same tests against live testnets will make it easy to test the Teleporter deployment and integration on new networks as they are created.

The networks used for the tests should be configurable.

Discussion and alternatives
We could try to make the networks used by the Ginkgo containers configurable to achieve the same effective result, but that may add unnecessary complexity to the test set up, and certain tests (such as validator set churn) aren't able to be run against live testnets in any event. Thanks to @cam-schultz for calling that out.

Update to subnet-evm v0.5.9

Context and scope
Updates to the latest subnet-evm and avalanchego versions. There are no breaking changes to the Warp message format, so this change should be straightforward.

Remove subnet-evm as submodule

Context and scope
subnet-evm was originally added a submodule over the telelporter repo because Teleporter required features of subnet-evm that were under long-running development branches and not yet released.

Now, we should be able to remove the submodule and instead install the compatible release version of subnet-evm.

Author integration tests using Ginkgo

Action items
The Ginkgo test framework has already been implemented in other tickets, for example #94. Much of the discussion in the following sections have already been resolved and implemented, but I'll leave the text there for context.

The main remaining action item to close this ticket out is to port the existing bash integration tests to our Ginkgo framework. Below are the test cases we need to port.

  • example_messenger.sh
  • basic_send_receive.sh
  • erc20_bridge_multihop.sh
  • send_specified_receipts.sh
  • block_hash_publisher_receiver.sh
  • Remove bash integration test setup

Context and scope
The current integration test setup consists of a set of docker containers that:

  1. Spin up a local avalanche network and deploys subnets and Teleporter contracts
  2. Runs an awm-relayer instance
  3. Executes integration tests

These steps are performed within the containers using a set of bash scripts. For example, https://github.com/ava-labs/teleporter/blob/main/docker/run_setup.sh starts the networks, and https://github.com/ava-labs/teleporter/blob/main/scripts/local/test.sh runs the integration tests.

Discussion and alternatives
Ginkgo is a test framework written in Go that performs very similar steps to our current setup, but in a much more user friendly and less error prone way. Ginkgo:

  • handles container setup and teardown
  • is easier to avoid bugs when authoring tests due to Go's static typing
  • produces much more readable tests with clearly defined expected behavior
  • is under active development

To convert our bash-based integration tests to Ginkgo, the following development strategy is proposed:

  1. Port awm-relayer E2E tests written in Ginkgo to teleporter. The high-level changes here are:
    a. Implement basic send and receive integration test.
    b. Modify message relaying to either build and run awm-relayer directly, or manually aggregate signatures and deliver the message from within the E2E test. subnet-evm Warp E2E tests take the latter approach.
  2. Consolidate awm-relayer and teleporter E2E tests to reduce code duplication.
  3. Iteratively expand teleporter Ginkgo E2E test suite to achieve parity with the existing bash integration tests.
  4. Remove bash integration test CI jobs, and optionally remove all docker and bash code.

Open questions

  • One nice feature of the docker setup is that we can start up the system, but not execute tests, (see run.sh) allowing the user to open a shell inside one of the containers and interact with the system in real time. Is this possible with Ginkgo?

Create examples of moving AVAX and ERC20s into a subnet as the native token

Context and scope
One possible use case of Teleporter is to move an token from an existing EVM chain into a new EVM chain as the new chains native token. For instance, you could create a subnet that uses AVAX from the C-chain as its native token used to pay for transaction fees in that subnet. Similarly you could use USDC/USDT/any ERC20 from the C-chain as a subnets native token.

Currently, this is only possible using third-party bridges, but Teleporter should enable this use case using Avalanche Warp Messaging.

Discussion and alternatives
The native minter precompile in subnet-evm enables EVM subnets to specify addresses that are allowed to mint more of the native token of that EVM instance. Moving native tokens into a subnet-evm instance should be very similar model to bridging an ERC20 token in the existing ERC20Bridge example, but instead of creating and minting a BridgeToken the contract receiving the Teleporter message should call the native minter precompile to mint native tokens on that chain. Then, to move the tokens back out to the external chain, the native tokens in the subnet should be sent to a predefined "black hole" address, and a Teleporter message sent to the external chain specify the amount of tokens to be released, and to which address.

There still exists a bootstrapping problem for the subnet because some native tokens will need to exist for contracts to be deployed and the initial Teleporter messages to be delivered. As a work around, a small "pre-minted" token supply can be specified in the subnet's genesis, and the initial amount of tokens moved into the subnet equaling this supply can be ignored such that no new native tokens will be minted until the original supply becomes fully collateralized. Alternatively, the initial "pre-minted" supply could be simply ignored since it should be burned over time as fees as transactions are sent. The subnet will only ever be able to claim the amount of tokens that have been moved into it on the existing chain, so it does not pose a security risk the external chain.

A possible setup and deployment order of this design would be:

  1. Create a new EOA to be used to deploy the Native Token Receiver on the new Subnet and Native Token Sender on the external chain. Calculate what the resulting contract address is for the 0 nonce of that EOA; this will be the address of both the Sender and Receiver contracts on their respective chains.
  2. Create Subnet XYZ with native token ABC.
    • In the genesis, pre-mined ABC is allotted to:
      • Teleporter Deployer Address (this is predefined since it is deployed using Nick's method)
      • Native Token Receiver Deployer EOA (created in step 1)
      • EOA used by the AWM relayer
    • Native Minter precompile is configured to allow the Native Token Receiver contract address to mint native tokens.
  3. Deploy Teleporter contract on Subnet XYZ.
  4. Deploy Native Token Receiver contract on Subnet XYZ. This needs to be deployed from the first transaction (nonce 0) of the EOA created in step 1.
    • Constructor of the Native Token Receiver takes as parameters the origin chain ID and the origin sender address that is allowed to send Teleporter messages to mint native tokens.
  5. Deploy Native Token Sender contract on the external chain. This needs to be deployed from the first transaction (nonce 0) of the EOA created in step 1.
    • Constructor of the Native Token Sender takes as parameters the destination chain ID and the Native Token Receiver address where it should send messages.
    • The Native Token Sender contract also defines which asset on the external chain is to be used as the token being bridged.
      Relayer deployment is configured to use the EOA that was specified in the genesis and has pre-mined tokens.

As an example, it would be great to have a "Native Token Sender" contract implementation that supports each another chain's native token (i.e. moving AVAX from the C-chain into a subnet as its native token) and ERC20 tokens on other chains. One benefit of only specifying the "Native Token Sender" address in the "Native Token Receiver" contract is that the receiver does not need to be aware of the the collateral asset is, so it can be re-used no matter as-is no matter the collateral asset type.

Open questions

  • Should the Native Token Receiver contract include a multiplier to allow for assets with a decimal count other than 18 to be converted correctly?
  • Should the Native Token Receiver contract be aware of the the pre-mined supply amount to correct for initially?
  • (Longer Term) Can subnets track how much of their native token has been burned, and report that amount back to the origin chain to burn the collateral? This would likely require changes to the VM to track the amount burned in blocks, but would help prevent the locked native token collateral from strictly growing overtime, making the subnet validators more and more of a security target.

Consider hosting "stable" foundry releases to avoid purging

Context and scope
We currently install a specific nightly foundry release in our integration test flow. Foundry may prune this version in the future such that it's no longer available as a pre-built release.

Discussion and alternatives
Consider either:

  1. Building from source
  2. Hosting the specific foundry release we'd like to install in an image repository like Dockerhub to ensure that it can't be removed from underneath unexpectedly.

Remove WarpProtocolRegistry and refactor TeleporterRegistry

Context and scope
Current implementation has WarpProtocolRegistry, which was meant to be inherited by other protocols on top of Warp, but Teleporter repo wouldn't be the appropriate place to leave the contract, also we don't have other protocols built on top of Warp right now.

Discussion and alternatives
Remove the WarpProtocolRegistry and move logic into TeleporterRegistry.

Open questions
Do we want to keep an external->internal matching pattern for functions, or just public?
Would Teleporter anycasting need a separate registry?

Consider introspection for checking if a contract implements ITeleporterReceiver

Context and scope
A contract that would like to receive messages from the TeleporterMessenger contract must implement the receiveTeleporterMessage function from the ITeleporterReceiver interface. This function is called when the message is being executed. If a contract does not implement the receiveTeleporterMessage function, the call will fail and be stored for a future execution. However, if the receiving contract does not implement this function and instead implements a fallback , the call could be successful. This opens up the possibility whereby even though the message is being forwarded to the contract, the destination contract does not handle it but there is no reversion of the transaction.

In favor of restricting the possibility of a message being delivered into a non-compatible contract on the destination subnet, consider using an EIP meant for introspection, such as EIP-1820, instead of assuming that contracts that do not possess a receiveTeleporterMessage hook cannot be called.

Open questions
Is this worth the additional complexity in the TeleporterMessenger contract, given that it is the responsibility of the application contracts using Teleporter to ensure they properly implement the required interface and are able to process intended message properly?

Revert back to require instead of custom errors

Context and scope
We previously moved from using require statements to using custom errors, that is a newer feature in Solidity versions. We want to revert back to require because it allows for better debugging, certain tools like snowtrace do not propagate the custom errors. When running our integration tests a returned error from foundry command would look like a hash that'd need to be decoded with the abi.

Discussion and alternatives
Custom errors were lighter in gas, but this should only matter for cases that fail. Might want to check the gas report to make sure overall gas does not take a bigger than expected increase.

Consider how Teleporter apps can stop receiving messages from vulnerable version

Context and scope
Right now for receiving Teleporter messages there is a minTeleporterVersion, and when a new version gets published to the registry, the app should wait for all previous version messages to be delivered, then call updateMinTeleporterVersion.

What if the latest version has a vulnerability and shouldn't be used for sending/receiving? Currently the only way is to add a new version to the registry with a Warp off-chain message, and perform an update to minTeleporterVersion.

The registry allows adding old protocol addresses as a new version, so the chain can the latest version that is not vulnerable back to the registry, apps call updateMinTeleporterVersion, and they can still receive previous Teleporter messages from that version. What if the chain adds a new protocol address for the new version? Then when apps call updateMinTeleporterVersion to stop receiving/sending from a vulnerable version, they'll no longer be able to receive Teleporter messages of the previous valid version that haven't been delivered yet.

We don't want to require chains to add the latest valid Teleporter address to the registry, before adding a new Teleporter address. We also don't want apps to have to rely on the registry update, which requires a Warp off-chain message so needs validator consensus that can take long, to stop receiving/sending from a vulnerable Teleporter version. So need to implement some mechanism in TeleporterUpgradeable that allows apps to stop receiving/sending from a specific Teleporter version.

Discussion and alternatives
There are two approaches currently in mind, blocking a specific Teleporter version, or pausing Teleporter interaction altogether (all versions).

With pausing the app would be pausing all versions, so would need to figure out how apps can still receive Teleporter messages from the previous valid versions. If this can only done by relying on a registry update with a previous valid protocol address but new version, then this is less viable.

For blocking a version, should the app be allowed to unblock a version. Definitely not unblock a version < minTeleporterVersion, but can it unblock version > minTeleporterVersion in case apps block the wrong version? But blocking is already having the same trust assumption as updateMinTeleporterVersion, since if the admins for the app call updateMinTeleporterVersion incorrectly they'd also stop receiving Teleporter messages.

For updateMinTeleporterVersion we now have _checkTeleporterUpgradeAccess, that should be overriden for different access pattern checks, and both approaches should use the same check.

Open questions
How do we handle pausing/blocking for sending Teleporter messages? Currently we by default recommend always sending with the latestVersion, but what if the latest version has a vulnerability? We wouldn't want to send anymore either.

This might only be an extra consideration for if latestVersion (recommended sending version) is vulnerable. For example, lets say there's version 1,2,3, we are sending on version 3, but still receiving all versions. Version 2 found a bug, we could pause receives, or block version 2, but keep sending with 3.

However if version 3 had a bug, we'd pause receives, or block version 3, and need to no longer send with version 3. I think we can add a check in the helper to check whether sending version is blocked, if it is, no sending until new vesrion is added.

function _getTeleporterMessenger() internal virtual returns(ITeleporterMessenger) {
    uint256 latestVersion = teleporterRegistry.latestVersion();
    // require latsetVersion is not blocked
   return teleporterRegistry.getTeleporterFromVersion(latestVersion);
}

Investigate nil tx in E2E test

In the test RetrySuccessfulExecution, the call to RetryMessageExecution after the message has already been successfully executed is expected to fail. It fails, however, with a nil transaction that is never mined, which is unexpected. We should investigate the cause.

Originally posted by @minghinmatthewlam in #93 (comment)

Add multiplier to `NativeTokenDestination`

Context and scope
Some ERC20 contracts have decimals of something other than 18. Currently, sending 1 full token from an ERC20 contract with 24 decimals, will result in 10^6 full tokens being minted on the destination. We should make this configurable.

Discussion and alternatives
We decided that this should be handled on the destination chain only, as the source chain does not need to know the implementation specifics of the destination, and with this implementation, we can unify the interfaces among all the different token bridge contracts.

Open questions

[Bug]: Validator churn test case flakiness

Describe the bug
Validator churn test flow is flaky and fails sometimes

To Reproduce
See failed CI runs like https://github.com/ava-labs/teleporter/actions/runs/7107020416/job/19347656883. Or try to run e2e tests locally many times

Expected behavior
Test case should always pass

Logs

[12-05|22:03:09.549] ERROR server/server.go:686 failed to add subnet validators {"error": "P-Wallet Tx Error IssueAddPermissionlessValidatorTx context deadline exceeded, node ID NodeID-J3yt4RJY9ZZfgARkP8J6CYebwqwR2Q127"}
  [FAILED] Expected
      <*status.Error | 0xc000119e70>: 
      rpc error: code = DeadlineExceeded desc = P-Wallet Tx Error IssueAddPermissionlessValidatorTx context deadline exceeded, node ID NodeID-J3yt4RJY9ZZfgARkP8J6CYebwqwR2Q127
INFO [12-05|22:03:09.549] Tearing down network 
INFO [12-05|22:03:09.549] Shutting down cluster 
      {
          s: {
              s: {
                  state: {
                      NoUnkeyedLiterals: {},
                      DoNotCompare: [],
                      DoNotCopy: [],
                      atomicMessageInfo: nil,
                  },
                  sizeCache: 0,
                  unknownFields: nil,
                  Code: 4,
                  Message: "P-Wallet Tx Error IssueAddPermissionlessValidatorTx context deadline exceeded, node ID NodeID-J3yt4RJY9ZZfgARkP8J6CYebwqwR2Q127",
                  Details: nil,
              },
          },
      }
  to be nil
  In [It] at: /home/runner/work/teleporter/teleporter/tests/utils/local-network-utils/local_network_setup.go:512 @ 12/05/23 22:03:09.549

  Full Stack Trace
    github.com/ava-labs/teleporter/tests/utils/local-network-utils.AddSubnetValidators({0x1e77d60, 0xc000126000}, {0x6a, 0xd6, 0x8c, 0x66, 0xec, 0x40, 0x2e, 0xb5, ...}, ...)
    	/home/runner/work/teleporter/teleporter/tests/utils/local-network-utils/local_network_setup.go:512 +0x18d
    github.com/ava-labs/teleporter/tests.ValidatorChurn()
    	/home/runner/work/teleporter/teleporter/tests/validator_churn.go:76 +0x865
  < Exit [It] Validator churn - /home/runner/work/teleporter/teleporter/tests/e2e_test.go:95 @ 12/05/23 22:03:09.549 (1m21.674s)
• [FAILED] [81.674 seconds]
[Teleporter integration tests]
/home/runner/work/teleporter/teleporter/tests/e2e_test.go:53
  [It] Validator churn
  /home/runner/work/teleporter/teleporter/tests/e2e_test.go:95

Operating System
On Github CI

Additional context
Add any other context about the problem here.

To best protect the Avalanche community security bugs should be reported in accordance to our Security Policy

Integrate Ginkgo testing framework

Context and scope
We are currently using bash scripts for all our integration tests in Teleporter, we'd like to start moving towards ginkgo testing framework so that we can write a majority of the tests in go. Similar relayer PR: ava-labs/awm-relayer#26

Consider emitting TeleporterMessage hash in events

Context and scope
We currently emit the full (unbounded size) TeleporterMessage in certain event logs in TeleporterMessenger.sol. We should consider emitting the hash of the TeleporterMessage instead to save on gas. We would need to consider if that would affect indexers at all.

Discussion and alternatives
One idea is to continue to emit the full TeleporterMessage on the sending side (perhaps along with the hash). Since the sender pays for gas anyway, they have a small incentive to make their TeleporterMessage payload less verbose. Then we emit the hash on the receiving side.

Implement TeleporterOwnerUpgradeable

Context and scope
TeleporterUpgradeable is a utility contract dapps can inherit to help integrate with the TeleporterRegistry, but dapps have to implement TeleporterUpgradeable.updateMinTeleporterVersion, we want to provide a contract that helps implement the function for Ownable contracts.

Discussion and alternatives
Create an abstract contract TeleporterOwnerUpgradeable that inherits TeleporterUpgradeable, and implements the updateMinTeleporterVersion. Update the cross chain apps to use this contract and update README and examples.

Open questions
Consider other access control patterns

Finding corresponding Teleporter messages across chains

Context and scope
When we send a message through Teleporter, we expect to see the receive of that Teleporter message on destination chain. We should document these steps or have some form of script to check for it.

Discussion and alternatives
Ideally with the source tx that sends out the Teleporter message, we'd have the Teleporter message ID, then we want to be able to easily find the destination chain's tx that has the receive of the Teleporter message. Block numbers between chains don't match, and we have a match number of blocks we can call to FilterLogs.

Support Message Anycasting

Context and scope
The design should support submitting a message on one subnet that can be posted to any other subnet at most once.

There are plenty of use cases for this, one being broadcasting price feed updates from the c-chain into all subnets.

Discussion and alternatives
Things to consider:

  • What is the "any cast" chain ID and where is it defined. Possibly a fixed chain id like 1111...1111 next to primary network + P chain id.
  • Handling for overlapping message IDs for any cast vs direct messages.
  • Incentives for delivering any cast messages.

Update docs to be compatible with the documentation site

Context and scope
The docs website (https://docs.avax.network/) converts markdown into a beautified website. In order to do this, we need to make the following changes to https://github.com/ava-labs/teleporter/blob/main/contracts/src/Teleporter/README.md and https://github.com/ava-labs/teleporter/tree/main/contracts/src/CrossChainApplications#getting-started-with-an-example-application

  • Put all documentation intended for the site in standalone markdown files (for example, separate the Getting Started section into its own file)
  • Convert all relative paths in links to absolute paths
  • Close all html tags

Allow changing fee asset of existing message

Context and scope
If a user address(0) for the fee contract address, or 0 fee amount, then the current solution for the message to still be delivered would be to relay their own message. Some users might find this troublesome, and might not be the best UX. Consider adding functionality to allow users to replace their feeInfo in addFeeAmount when fee amount is set to 0.

Discussion and alternatives
If a user specifies address(0) as the fee contract address, the fee amount must be zero.

Open questions
Whether we want to support this use case at all. Possibly only allow changing fee asset is the current set contract addrses is address(0).

Simplify getting contract's state variable values

Context and scope
Contracts liek TeleporterMessenger have public state variables that by default have getter functions, we want to make it simple to query for these state variable values.

Discussion and alternatives
With a rpc endpoint and contract address, we can create an instance of the contract through the generated abi bindings, and in the bindings we can query the state variable getter functions.

Open questions
How can we simplify this process, will it be through some form of script or through documentation?

Consider allowed relayers wildcard case

Context and scope
Currently Teleporter messages allow any relayers when the allowedRelayers array is empty, we could possibly look at having a special address value such as 0x00...00.

Consider TeleporterRegistry deployment through Nick's method

Context and scope
TeleporterRegistry is currently implemented without Nick's method, so will end up at different addresses across different subnet chains. The registry needs Warp out-of-band messages to update, and do so validators need to add the Warp out-of-band message bytes, and we want to streamline this process as much as possible.

Discussion and alternatives
We would need to remove the calls in the registry' constructor, which include registry initialization and getting warp blockchain ID, out of constructor, to prevent the deployment from failing and using up the nonce.

Open questions
The destination chain ID between Warp messages would be different already anyways, how can we streamline this too?

Upgradeability audit fixes

Context and scope
There were multiple suggestions for fixes from a recent upgradeability audit. We want to address the issues found including:

  • Teleporter version can be overwritten to a lower value
  • The constructor initializes the _lastestVersion to 0 unnecessarily
  • Messages could become permanently stuck if minTeleporterVersion is wrongly set
  • The TeleporterUpgradeable lacks helper functions
  • The updateMinTeleporterVersion function is marked as external
  • Potential race condition between new Teleporter version and updateMinTeleporterVersion, block/pausable will be addresed in #172
  • The MinTeleporterVersionUpdated event might not be emitted during specific updates of the minTeleporterVersion.
  • The minTeleporterVersion value could be cached to save gas
  • The blockchainID variable could be cached to save gas

Do not call Warp precompile in TeleporterMessenger constructor

Context and scope
TeleporterMessenger must be deployed using Nick's method, which requires that the deployment transaction be sent from an account with a zero nonce. If the deployment transaction reverts, then the account nonce will increment and TeleporterMessenger cannot be deployed at the intended address on that chain.

TeleporterMessenger calls the Warp precompile in its constructor to fetch the blockchain ID. If the Warp precompile has not been enabled on that chain, then the TeleporterMessenger constructor will revert, and the deployment will fail, precluding TeleporterMessenger from being deployed at the intended address on that chain. If that happens, then all chains would have to redeploy TeleporterMessenger at a new address in order to interact with the chain on which deployment failed.

Discussion and alternatives
We should avoid calling the Warp precompile in the TeleporterMessenger constructor, and instead fetch the blockchain ID in a separate step that cannot impact deployment.

Implement strict ordering example app

Context and scope
Teleporter by default does not enforce strict ordering, and messages can be delivered in any order. Some dapps may like the strict ordering functionality, and can be done so on chain, this ticket tackles one as an example.

Discussion and alternatives
Likely on chain through the dapp's receiveTeleporterMessage or similar to enforce strict ordering. Alternative would be offchain where the dapp can specify its own allowedRelayerAddresses and the relayer can enforce ordering of delivering messages. This ticket is tackling an example of the former.

Open questions
What happens if the receiving Teleporter receives a message out of order, does it simply reject, and require a retry later?
What is the best UX to make it easier for the dapp?

Add to CLI unit tests

Context and scope
There are some basic unit tests in PR, but we can also add unit tests with basic inputs. There are unit tests on branch cli-unit-tests, which run successfully individually, but ran altogether in go test ./... they fail. This is likely due to some shared state, and the commands not reinitializing between the test cases.

Discussion and alternatives
In the unit tests might need to make copies of the commands to initialize between test cases.

Clarify CrossChainApplications as examples

Context and scope
The CrossChainApplications directory currently has example applications built on top of Teleporter, for example ERC20Bridge. They mostly serve as examples for developers who are looking to build applications on top of Teleporter, and to demonstrate how to use/integrate with Teleporter, but are meant to serve as examples.

Discussion and alternatives
Change directory name to include example
Clarify in any relevant documentation

Solhint workflow not catching lint errors

Describe the bug
The github workflow that runs solhint is not catching lint errors.

To Reproduce
On the main branch the Linter check passes, even though there are meant to be lint errors. Checking raw logs solhint never outputs anything after its version.

Expected behavior
Github workflow linter check outputs solhint lint errors and fails the check.

Screenshots
If ran locally:
image

Operating System
Mac M1 Ventura 13.5.1

Monitoring of Teleporter contract usage

Context and scope
We want to collect different kinds of telemetry to tell us how Teleporter is performing, as well as areas that need to be improved on.

Open questions

  • How many people are interacting with Teleporter at all?
  • Are users using all the ITelepoterMessenger functions like retries, adding fee amount, etc?
  • Do users tend to specify allowed relayers?
  • Are people trending towards relaying their messages?
  • What is the throughput like, how long does it take to relay the message to destination?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.