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

Add teleporter messenger to genesis #474

Merged
merged 14 commits into from
Aug 9, 2024
Merged
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ To ensure that Teleporter can be deployed to the same address on every EVM based

`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](./contracts/teleporter/README.md#teleporter-messenger-contract-deployment).

## 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:
Expand Down
10 changes: 3 additions & 7 deletions contracts/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
## Teleporter Messenger Contract
This directory contains Solidity contracts implementing the Teleporter messaging protocol.
## Contracts
This directory contains Solidity contracts that leverage Avalanche Warp Messaging to implement unique cross-chain functions.

This directory is set up as a [Foundry](https://github.com/foundry-rs/foundry) project. Use the `scripts/install_foundry.sh` to install the correct version of the ava-labs fork of foundry. Further documentation about given contracts can be found in`src/teleporter/`.
This directory is set up as a [Foundry](https://github.com/foundry-rs/foundry) project. Use the `scripts/install_foundry.sh` to install the correct version of the ava-labs fork of foundry.

## Building and Running
- To compile the contracts run `forge build` from this directory.
- Similarly, to run unit tests, run `forge test`.
- See additional testing and deployment options [here](https://book.getfoundry.sh/forge/).

## Deployment
**Do not deploy using `forge create`**. See [this guide](https://github.com/ava-labs/teleporter/blob/main/utils/contract-deployment/README.md) on how to properly deploy the `TeleporterMessenger` contract to your Subnet.

## Generate documentation
- Documentation can be generated by running `forge doc --build` from this repository. By default, this will generate documentation to `contracts/docs/`, and an HTML book to `contracts/docs/book/`. It's also possible to serve this book locally by running `forge doc --serve <PORT>`.

29 changes: 25 additions & 4 deletions contracts/teleporter/README.md

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions tests/interfaces/local_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ type LocalNetwork interface {
GetAllNodeIDs() []ids.NodeID
SetChainConfigs(chainConfigs map[string]string)
RestartNodes(ctx context.Context, nodeIDs []ids.NodeID)
DeployTeleporterContracts(
DeployTeleporterContractToCChain(
transactionBytes []byte,
deployerAddress common.Address,
contractAddress common.Address,
fundedKey *ecdsa.PrivateKey,
)
DeployTeleporterContractToAllChains(
transactionBytes []byte,
deployerAddress common.Address,
contractAddress common.Address,
fundedKey *ecdsa.PrivateKey,
updateNetworkTeleporter bool,
)
GetNetworkID() uint32
Dir() string
Expand Down
49 changes: 29 additions & 20 deletions tests/local/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import (
)

const (
teleporterByteCodeFile = "./out/TeleporterMessenger.sol/TeleporterMessenger.json"

teleporterByteCodeFile = "./out/TeleporterMessenger.sol/TeleporterMessenger.json"
warpGenesisTemplateFile = "./tests/utils/warp-genesis-template.json"

teleporterMessengerLabel = "TeleporterMessenger"
Expand All @@ -40,51 +39,61 @@ func TestE2E(t *testing.T) {

// Define the Teleporter before and after suite functions.
var _ = ginkgo.BeforeSuite(func() {
// Generate the Teleporter deployment values
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be helpful in some debugging scenarios to provide an easy way to deploy Teleporter to all subnets via eth_sendRawTransaction versus genesis. Some ideas that come to mind:

  • An explicit command line flag. I'm not sure if being able to make this switch at runtime is worth the added complexity.
  • Add a bool param deployViaGenesis to NewLocalNetwork. Then the only changes required are true->false and DeployTeleporterContractToCChain -> DeployTeleporterContractToAllSubnets`
    • Can be further simplified if DeployTeleporterContractToAllSubnets can be made idempotent so that it can be called in all scenarios.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What are the benefits of having those two different options for the E2E test execution that you were thinking of?

For local E2E tests, I think this covers both deployment flows sufficiently.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was just anticipating any issues arising from the different deployment methods. In such a scenario, being able to toggle between the two could be helpful. For instance, if a future change comes with state initialization in the same way as ReentrancyGuards, switching to Nick's method would help narrow down any differences.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think we can punt on this for now personally. Have the default E2E cover both possible deployment flows should hopefully ensure we keep them both up-to-date and compatible with any future changes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good. Any test failures along these lines would require root cause analysis anyway, something the suggested change would likely not help much with.

teleporterDeployerTransaction, teleporterDeployedBytecode, teleporterDeployerAddress, teleporterContractAddress, err :=
deploymentUtils.ConstructKeylessTransaction(
teleporterByteCodeFile,
false,
deploymentUtils.GetDefaultContractCreationGasPrice(),
)
Expect(err).Should(BeNil())

// Create the local network instance
LocalNetworkInstance = NewLocalNetwork(
"teleporter-test-local-network",
warpGenesisTemplateFile,
[]SubnetSpec{
{
Name: "A",
EVMChainID: 12345,
NodeCount: 2,
Name: "A",
EVMChainID: 12345,
TeleporterContractAddress: teleporterContractAddress,
TeleporterDeployedBytecode: teleporterDeployedBytecode,
TeleporterDeployerAddress: teleporterDeployerAddress,
NodeCount: 2,
},
{
Name: "B",
EVMChainID: 54321,
NodeCount: 2,
Name: "B",
EVMChainID: 54321,
TeleporterContractAddress: teleporterContractAddress,
TeleporterDeployedBytecode: teleporterDeployedBytecode,
TeleporterDeployerAddress: teleporterDeployerAddress,
NodeCount: 2,
},
},
2,
)
log.Info("Started local network")

// Generate the Teleporter deployment values
teleporterDeployerTransaction, teleporterDeployerAddress, teleporterContractAddress, err :=
deploymentUtils.ConstructKeylessTransaction(
teleporterByteCodeFile,
false,
deploymentUtils.GetDefaultContractCreationGasPrice(),
)
Expect(err).Should(BeNil())

// Only need to deploy Teleporter on the C-Chain since it is included in the genesis of the subnet chains.
_, fundedKey := LocalNetworkInstance.GetFundedAccountInfo()
LocalNetworkInstance.DeployTeleporterContracts(
LocalNetworkInstance.DeployTeleporterContractToCChain(
teleporterDeployerTransaction,
teleporterDeployerAddress,
teleporterContractAddress,
fundedKey,
true,
)
LocalNetworkInstance.SetTeleporterContractAddress(teleporterContractAddress)
Copy link
Contributor

Choose a reason for hiding this comment

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

Previously this was encapsulated in the call to DeployTeleporterContracts. Can we similarly do so in DeployTeleporterContractToCChain and/or NewLocalNetwork?

Copy link
Collaborator Author

@michaelkaplan13 michaelkaplan13 Aug 6, 2024

Choose a reason for hiding this comment

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

I think it was a little odd previously actually. There was a boolean parameter in one exported function for whether or not another exported function should be called from it. Since they are both exported, I think it's cleaner to make them independent and left up to the caller.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I had forgotten about that switch. Agreed, makes sense to keep them separate.


// Deploy the Teleporter registry contracts to all subnets and the C-Chain.
LocalNetworkInstance.DeployTeleporterRegistryContracts(teleporterContractAddress, fundedKey)
log.Info("Set up ginkgo before suite")

ginkgo.AddReportEntry(
"network directory with node logs & configs; useful in the case of failures",
LocalNetworkInstance.tmpnet.Dir,
ginkgo.ReportEntryVisibilityFailureOrVerbose,
)

log.Info("Set up ginkgo before suite")
})

var _ = ginkgo.AfterSuite(func() {
Expand Down
118 changes: 75 additions & 43 deletions tests/local/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,12 @@ const (
)

type SubnetSpec struct {
Name string
EVMChainID uint64
NodeCount int
Name string
EVMChainID uint64
TeleporterContractAddress common.Address
TeleporterDeployedBytecode string
TeleporterDeployerAddress common.Address
NodeCount int
}

func NewLocalNetwork(
Expand Down Expand Up @@ -104,6 +107,9 @@ func NewLocalNetwork(
utils.InstantiateGenesisTemplate(
warpGenesisTemplateFile,
subnetSpec.EVMChainID,
subnetSpec.TeleporterContractAddress,
subnetSpec.TeleporterDeployedBytecode,
subnetSpec.TeleporterDeployerAddress,
),
subnetEvmTestUtils.DefaultChainConfig,
nodes...,
Expand Down Expand Up @@ -197,7 +203,7 @@ func (n *LocalNetwork) setPrimaryNetworkValues() {
n.primaryNetworkInfo.RPCClient = chainRPCClient
n.primaryNetworkInfo.EVMChainID = chainIDInt

// TeleporterMessenger is set in DeployTeleporterContracts
// TeleporterMessenger is set in SetTeleporterContractAddress
// TeleporterRegistryAddress is set in DeployTeleporterRegistryContracts
}

Expand Down Expand Up @@ -242,60 +248,86 @@ func (n *LocalNetwork) setSubnetValues(subnet *tmpnet.Subnet) {
n.subnetsInfo[subnetID].RPCClient = chainRPCClient
n.subnetsInfo[subnetID].EVMChainID = chainIDInt

// TeleporterMessenger is set in DeployTeleporterContracts
// TeleporterMessenger is set in SetTeleporterContractAddress
// TeleporterRegistryAddress is set in DeployTeleporterRegistryContracts
}

// DeployTeleporterContracts deploys the Teleporter contract to all subnets.
func (n *LocalNetwork) deployTeleporterToChain(
ctx context.Context,
subnetInfo interfaces.SubnetTestInfo,
transactionBytes []byte,
deployerAddress common.Address,
contractAddress common.Address,
fundedKey *ecdsa.PrivateKey,
) {
// Fund the deployer address
fundAmount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(11)) // 11 AVAX
fundDeployerTx := utils.CreateNativeTransferTransaction(
ctx, subnetInfo, fundedKey, deployerAddress, fundAmount,
)
utils.SendTransactionAndWaitForSuccess(ctx, subnetInfo, fundDeployerTx)

log.Info("Finished funding Teleporter deployer", "blockchainID", subnetInfo.BlockchainID.Hex())

// Deploy Teleporter contract
rpcClient, err := rpc.DialContext(
ctx,
utils.HttpToRPCURI(subnetInfo.NodeURIs[0], subnetInfo.BlockchainID.String()),
)
Expect(err).Should(BeNil())
defer rpcClient.Close()

txHash := common.Hash{}
err = rpcClient.CallContext(ctx, &txHash, "eth_sendRawTransaction", hexutil.Encode(transactionBytes))
Expect(err).Should(BeNil())
utils.WaitForTransactionSuccess(ctx, subnetInfo, txHash)

teleporterCode, err := subnetInfo.RPCClient.CodeAt(ctx, contractAddress, nil)
Expect(err).Should(BeNil())
Expect(len(teleporterCode)).Should(BeNumerically(">", 2)) // 0x is an EOA, contract returns the bytecode

log.Info("Finished deploying Teleporter contract", "blockchainID", subnetInfo.BlockchainID.Hex())
}

// DeployTeleporterContractToCChain deploys the Teleporter contract to the C-Chain.
// The caller is responsible for generating the deployment transaction information
func (n *LocalNetwork) DeployTeleporterContracts(
func (n *LocalNetwork) DeployTeleporterContractToCChain(
transactionBytes []byte,
deployerAddress common.Address,
contractAddress common.Address,
fundedKey *ecdsa.PrivateKey,
updateNetworkTeleporter bool,
) {
log.Info("Deploying Teleporter contract to subnets", "contractAddress", contractAddress.String())
log.Info("Deploying Teleporter contract to C-Chain", "contractAddress", contractAddress.String())

ctx := context.Background()
n.deployTeleporterToChain(
ctx,
n.GetPrimaryNetworkInfo(),
transactionBytes,
deployerAddress,
contractAddress,
fundedKey,
)

subnets := n.GetAllSubnetsInfo()
for _, subnetInfo := range subnets {
// Fund the deployer address
{
fundAmount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(11)) // 11 AVAX
fundDeployerTx := utils.CreateNativeTransferTransaction(
ctx, subnetInfo, fundedKey, deployerAddress, fundAmount,
)
utils.SendTransactionAndWaitForSuccess(ctx, subnetInfo, fundDeployerTx)
}
log.Info("Finished funding Teleporter deployer", "blockchainID", subnetInfo.BlockchainID.Hex())
log.Info("Deployed Teleporter contracts to C-Chain")
}

// Deploy Teleporter contract
{
rpcClient, err := rpc.DialContext(
ctx,
utils.HttpToRPCURI(subnetInfo.NodeURIs[0], subnetInfo.BlockchainID.String()),
)
Expect(err).Should(BeNil())
defer rpcClient.Close()

txHash := common.Hash{}
err = rpcClient.CallContext(ctx, &txHash, "eth_sendRawTransaction", hexutil.Encode(transactionBytes))
Expect(err).Should(BeNil())
utils.WaitForTransactionSuccess(ctx, subnetInfo, txHash)

teleporterCode, err := subnetInfo.RPCClient.CodeAt(ctx, contractAddress, nil)
Expect(err).Should(BeNil())
Expect(len(teleporterCode)).Should(BeNumerically(">", 2)) // 0x is an EOA, contract returns the bytecode
}
log.Info("Finished deploying Teleporter contract", "blockchainID", subnetInfo.BlockchainID.Hex())
}
// DeployTeleporterContractToAllChains deploys the Teleporter contract to the C-Chain and all subnets.
// The caller is responsible for generating the deployment transaction information
func (n *LocalNetwork) DeployTeleporterContractToAllChains(
transactionBytes []byte,
deployerAddress common.Address,
contractAddress common.Address,
fundedKey *ecdsa.PrivateKey,
) {
log.Info("Deploying Teleporter contract to C-Chain and all subnets", "contractAddress", contractAddress.String())

if updateNetworkTeleporter {
n.SetTeleporterContractAddress(contractAddress)
ctx := context.Background()
for _, subnetInfo := range n.GetAllSubnetsInfo() {
n.deployTeleporterToChain(ctx, subnetInfo, transactionBytes, deployerAddress, contractAddress, fundedKey)
}
log.Info("Deployed Teleporter contracts to all subnets")

log.Info("Deployed Teleporter contracts to C-Chain and all subnets")
}

func (n *LocalNetwork) DeployTeleporterRegistryContracts(
Expand Down
Loading