github.com/MetalBlockchain/metalgo@v1.11.9/tests/e2e/x/interchain_workflow.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package x
     5  
     6  import (
     7  	"math/big"
     8  
     9  	"github.com/MetalBlockchain/coreth/plugin/evm"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/tests/fixture/e2e"
    14  	"github.com/MetalBlockchain/metalgo/utils/constants"
    15  	"github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1"
    16  	"github.com/MetalBlockchain/metalgo/utils/set"
    17  	"github.com/MetalBlockchain/metalgo/utils/units"
    18  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    19  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    20  	"github.com/MetalBlockchain/metalgo/wallet/subnet/primary/common"
    21  
    22  	ginkgo "github.com/onsi/ginkgo/v2"
    23  )
    24  
    25  var _ = e2e.DescribeXChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainLabel), func() {
    26  	require := require.New(ginkgo.GinkgoT())
    27  
    28  	const transferAmount = 10 * units.Avax
    29  
    30  	ginkgo.It("should ensure that funds can be transferred from the X-Chain to the C-Chain and the P-Chain", func() {
    31  		nodeURI := e2e.Env.GetRandomNodeURI()
    32  
    33  		ginkgo.By("creating wallet with a funded key to send from and recipient key to deliver to")
    34  		recipientKey, err := secp256k1.NewPrivateKey()
    35  		require.NoError(err)
    36  		keychain := e2e.Env.NewKeychain(1)
    37  		keychain.Add(recipientKey)
    38  		baseWallet := e2e.NewWallet(keychain, nodeURI)
    39  		xWallet := baseWallet.X()
    40  		cWallet := baseWallet.C()
    41  		pWallet := baseWallet.P()
    42  
    43  		ginkgo.By("defining common configuration")
    44  		recipientEthAddress := evm.GetEthAddress(recipientKey)
    45  		xBuilder := xWallet.Builder()
    46  		xContext := xBuilder.Context()
    47  		cBuilder := cWallet.Builder()
    48  		cContext := cBuilder.Context()
    49  		avaxAssetID := xContext.AVAXAssetID
    50  		// Use the same owner for sending to X-Chain and importing funds to P-Chain
    51  		recipientOwner := secp256k1fx.OutputOwners{
    52  			Threshold: 1,
    53  			Addrs: []ids.ShortID{
    54  				recipientKey.Address(),
    55  			},
    56  		}
    57  		// Use the same outputs for both C-Chain and P-Chain exports
    58  		exportOutputs := []*avax.TransferableOutput{
    59  			{
    60  				Asset: avax.Asset{
    61  					ID: avaxAssetID,
    62  				},
    63  				Out: &secp256k1fx.TransferOutput{
    64  					Amt: transferAmount,
    65  					OutputOwners: secp256k1fx.OutputOwners{
    66  						Threshold: 1,
    67  						Addrs: []ids.ShortID{
    68  							keychain.Keys[0].Address(),
    69  						},
    70  					},
    71  				},
    72  			},
    73  		}
    74  
    75  		ginkgo.By("sending funds from one address to another on the X-Chain", func() {
    76  			_, err = xWallet.IssueBaseTx(
    77  				[]*avax.TransferableOutput{{
    78  					Asset: avax.Asset{
    79  						ID: avaxAssetID,
    80  					},
    81  					Out: &secp256k1fx.TransferOutput{
    82  						Amt:          transferAmount,
    83  						OutputOwners: recipientOwner,
    84  					},
    85  				}},
    86  				e2e.WithDefaultContext(),
    87  			)
    88  			require.NoError(err)
    89  		})
    90  
    91  		ginkgo.By("checking that the X-Chain recipient address has received the sent funds", func() {
    92  			balances, err := xWallet.Builder().GetFTBalance(common.WithCustomAddresses(set.Of(
    93  				recipientKey.Address(),
    94  			)))
    95  			require.NoError(err)
    96  			require.Positive(balances[avaxAssetID])
    97  		})
    98  
    99  		ginkgo.By("exporting AVAX from the X-Chain to the C-Chain", func() {
   100  			_, err := xWallet.IssueExportTx(
   101  				cContext.BlockchainID,
   102  				exportOutputs,
   103  				e2e.WithDefaultContext(),
   104  			)
   105  			require.NoError(err)
   106  		})
   107  
   108  		ginkgo.By("initializing a new eth client")
   109  		ethClient := e2e.NewEthClient(nodeURI)
   110  
   111  		ginkgo.By("importing AVAX from the X-Chain to the C-Chain", func() {
   112  			_, err := cWallet.IssueImportTx(
   113  				xContext.BlockchainID,
   114  				recipientEthAddress,
   115  				e2e.WithDefaultContext(),
   116  				e2e.WithSuggestedGasPrice(ethClient),
   117  			)
   118  			require.NoError(err)
   119  		})
   120  
   121  		ginkgo.By("checking that the recipient address has received imported funds on the C-Chain")
   122  		e2e.Eventually(func() bool {
   123  			balance, err := ethClient.BalanceAt(e2e.DefaultContext(), recipientEthAddress, nil)
   124  			require.NoError(err)
   125  			return balance.Cmp(big.NewInt(0)) > 0
   126  		}, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see recipient address funded before timeout")
   127  
   128  		ginkgo.By("exporting AVAX from the X-Chain to the P-Chain", func() {
   129  			_, err := xWallet.IssueExportTx(
   130  				constants.PlatformChainID,
   131  				exportOutputs,
   132  				e2e.WithDefaultContext(),
   133  			)
   134  			require.NoError(err)
   135  		})
   136  
   137  		ginkgo.By("importing AVAX from the X-Chain to the P-Chain", func() {
   138  			_, err := pWallet.IssueImportTx(
   139  				xContext.BlockchainID,
   140  				&recipientOwner,
   141  				e2e.WithDefaultContext(),
   142  			)
   143  			require.NoError(err)
   144  		})
   145  
   146  		ginkgo.By("checking that the recipient address has received imported funds on the P-Chain", func() {
   147  			balances, err := pWallet.Builder().GetBalance(common.WithCustomAddresses(set.Of(
   148  				recipientKey.Address(),
   149  			)))
   150  			require.NoError(err)
   151  			require.Positive(balances[avaxAssetID])
   152  		})
   153  
   154  		e2e.CheckBootstrapIsPossible(e2e.Env.GetNetwork())
   155  	})
   156  })