github.com/ava-labs/avalanchego@v1.11.11/wallet/subnet/primary/api.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package primary
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/ava-labs/coreth/ethclient"
    11  	"github.com/ava-labs/coreth/plugin/evm"
    12  
    13  	"github.com/ava-labs/avalanchego/api/info"
    14  	"github.com/ava-labs/avalanchego/codec"
    15  	"github.com/ava-labs/avalanchego/ids"
    16  	"github.com/ava-labs/avalanchego/utils/constants"
    17  	"github.com/ava-labs/avalanchego/utils/rpc"
    18  	"github.com/ava-labs/avalanchego/utils/set"
    19  	"github.com/ava-labs/avalanchego/vms/avm"
    20  	"github.com/ava-labs/avalanchego/vms/components/avax"
    21  	"github.com/ava-labs/avalanchego/vms/platformvm"
    22  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    23  	"github.com/ava-labs/avalanchego/wallet/chain/c"
    24  	"github.com/ava-labs/avalanchego/wallet/chain/p"
    25  	"github.com/ava-labs/avalanchego/wallet/chain/x"
    26  
    27  	pbuilder "github.com/ava-labs/avalanchego/wallet/chain/p/builder"
    28  	xbuilder "github.com/ava-labs/avalanchego/wallet/chain/x/builder"
    29  	walletcommon "github.com/ava-labs/avalanchego/wallet/subnet/primary/common"
    30  	ethcommon "github.com/ethereum/go-ethereum/common"
    31  )
    32  
    33  const (
    34  	MainnetAPIURI = "https://api.avax.network"
    35  	FujiAPIURI    = "https://api.avax-test.network"
    36  	LocalAPIURI   = "http://localhost:9650"
    37  
    38  	fetchLimit = 1024
    39  )
    40  
    41  // TODO: Refactor UTXOClient definition to allow the client implementations to
    42  // perform their own assertions.
    43  var (
    44  	_ UTXOClient = platformvm.Client(nil)
    45  	_ UTXOClient = avm.Client(nil)
    46  )
    47  
    48  type UTXOClient interface {
    49  	GetAtomicUTXOs(
    50  		ctx context.Context,
    51  		addrs []ids.ShortID,
    52  		sourceChain string,
    53  		limit uint32,
    54  		startAddress ids.ShortID,
    55  		startUTXOID ids.ID,
    56  		options ...rpc.Option,
    57  	) ([][]byte, ids.ShortID, ids.ID, error)
    58  }
    59  
    60  type AVAXState struct {
    61  	PClient platformvm.Client
    62  	PCTX    *pbuilder.Context
    63  	XClient avm.Client
    64  	XCTX    *xbuilder.Context
    65  	CClient evm.Client
    66  	CCTX    *c.Context
    67  	UTXOs   walletcommon.UTXOs
    68  }
    69  
    70  func FetchState(
    71  	ctx context.Context,
    72  	uri string,
    73  	addrs set.Set[ids.ShortID],
    74  ) (
    75  	*AVAXState,
    76  	error,
    77  ) {
    78  	infoClient := info.NewClient(uri)
    79  	pClient := platformvm.NewClient(uri)
    80  	xClient := avm.NewClient(uri, "X")
    81  	cClient := evm.NewCChainClient(uri)
    82  
    83  	pCTX, err := p.NewContextFromClients(ctx, infoClient, xClient, pClient)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	xCTX, err := x.NewContextFromClients(ctx, infoClient, xClient)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	cCTX, err := c.NewContextFromClients(ctx, infoClient, xClient)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	utxos := walletcommon.NewUTXOs()
    99  	addrList := addrs.List()
   100  	chains := []struct {
   101  		id     ids.ID
   102  		client UTXOClient
   103  		codec  codec.Manager
   104  	}{
   105  		{
   106  			id:     constants.PlatformChainID,
   107  			client: pClient,
   108  			codec:  txs.Codec,
   109  		},
   110  		{
   111  			id:     xCTX.BlockchainID,
   112  			client: xClient,
   113  			codec:  xbuilder.Parser.Codec(),
   114  		},
   115  		{
   116  			id:     cCTX.BlockchainID,
   117  			client: cClient,
   118  			codec:  evm.Codec,
   119  		},
   120  	}
   121  	for _, destinationChain := range chains {
   122  		for _, sourceChain := range chains {
   123  			err = AddAllUTXOs(
   124  				ctx,
   125  				utxos,
   126  				destinationChain.client,
   127  				destinationChain.codec,
   128  				sourceChain.id,
   129  				destinationChain.id,
   130  				addrList,
   131  			)
   132  			if err != nil {
   133  				return nil, err
   134  			}
   135  		}
   136  	}
   137  	return &AVAXState{
   138  		PClient: pClient,
   139  		PCTX:    pCTX,
   140  		XClient: xClient,
   141  		XCTX:    xCTX,
   142  		CClient: cClient,
   143  		CCTX:    cCTX,
   144  		UTXOs:   utxos,
   145  	}, nil
   146  }
   147  
   148  type EthState struct {
   149  	Client   ethclient.Client
   150  	Accounts map[ethcommon.Address]*c.Account
   151  }
   152  
   153  func FetchEthState(
   154  	ctx context.Context,
   155  	uri string,
   156  	addrs set.Set[ethcommon.Address],
   157  ) (*EthState, error) {
   158  	path := fmt.Sprintf(
   159  		"%s/ext/%s/C/rpc",
   160  		uri,
   161  		constants.ChainAliasPrefix,
   162  	)
   163  	client, err := ethclient.Dial(path)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	accounts := make(map[ethcommon.Address]*c.Account, addrs.Len())
   169  	for addr := range addrs {
   170  		balance, err := client.BalanceAt(ctx, addr, nil)
   171  		if err != nil {
   172  			return nil, err
   173  		}
   174  		nonce, err := client.NonceAt(ctx, addr, nil)
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  		accounts[addr] = &c.Account{
   179  			Balance: balance,
   180  			Nonce:   nonce,
   181  		}
   182  	}
   183  	return &EthState{
   184  		Client:   client,
   185  		Accounts: accounts,
   186  	}, nil
   187  }
   188  
   189  // AddAllUTXOs fetches all the UTXOs referenced by [addresses] that were sent
   190  // from [sourceChainID] to [destinationChainID] from the [client]. It then uses
   191  // [codec] to parse the returned UTXOs and it adds them into [utxos]. If [ctx]
   192  // expires, then the returned error will be immediately reported.
   193  func AddAllUTXOs(
   194  	ctx context.Context,
   195  	utxos walletcommon.UTXOs,
   196  	client UTXOClient,
   197  	codec codec.Manager,
   198  	sourceChainID ids.ID,
   199  	destinationChainID ids.ID,
   200  	addrs []ids.ShortID,
   201  ) error {
   202  	var (
   203  		sourceChainIDStr = sourceChainID.String()
   204  		startAddr        ids.ShortID
   205  		startUTXO        ids.ID
   206  	)
   207  	for {
   208  		utxosBytes, endAddr, endUTXO, err := client.GetAtomicUTXOs(
   209  			ctx,
   210  			addrs,
   211  			sourceChainIDStr,
   212  			fetchLimit,
   213  			startAddr,
   214  			startUTXO,
   215  		)
   216  		if err != nil {
   217  			return err
   218  		}
   219  
   220  		for _, utxoBytes := range utxosBytes {
   221  			var utxo avax.UTXO
   222  			_, err := codec.Unmarshal(utxoBytes, &utxo)
   223  			if err != nil {
   224  				return err
   225  			}
   226  
   227  			if err := utxos.AddUTXO(ctx, sourceChainID, destinationChainID, &utxo); err != nil {
   228  				return err
   229  			}
   230  		}
   231  
   232  		if len(utxosBytes) < fetchLimit {
   233  			break
   234  		}
   235  
   236  		// Update the vars to query the next page of UTXOs.
   237  		startAddr = endAddr
   238  		startUTXO = endUTXO
   239  	}
   240  	return nil
   241  }