github.com/ava-labs/avalanchego@v1.11.11/wallet/chain/c/backend.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package c
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"math/big"
    11  	"sync"
    12  
    13  	"github.com/ava-labs/coreth/plugin/evm"
    14  
    15  	"github.com/ava-labs/avalanchego/database"
    16  	"github.com/ava-labs/avalanchego/utils/math"
    17  	"github.com/ava-labs/avalanchego/vms/components/avax"
    18  	"github.com/ava-labs/avalanchego/wallet/subnet/primary/common"
    19  
    20  	ethcommon "github.com/ethereum/go-ethereum/common"
    21  )
    22  
    23  var (
    24  	_ Backend = (*backend)(nil)
    25  
    26  	errUnknownTxType = errors.New("unknown tx type")
    27  )
    28  
    29  // Backend defines the full interface required to support a C-chain wallet.
    30  type Backend interface {
    31  	common.ChainUTXOs
    32  	BuilderBackend
    33  	SignerBackend
    34  
    35  	AcceptAtomicTx(ctx context.Context, tx *evm.Tx) error
    36  }
    37  
    38  type backend struct {
    39  	common.ChainUTXOs
    40  
    41  	accountsLock sync.RWMutex
    42  	accounts     map[ethcommon.Address]*Account
    43  }
    44  
    45  type Account struct {
    46  	Balance *big.Int
    47  	Nonce   uint64
    48  }
    49  
    50  func NewBackend(
    51  	utxos common.ChainUTXOs,
    52  	accounts map[ethcommon.Address]*Account,
    53  ) Backend {
    54  	return &backend{
    55  		ChainUTXOs: utxos,
    56  		accounts:   accounts,
    57  	}
    58  }
    59  
    60  func (b *backend) AcceptAtomicTx(ctx context.Context, tx *evm.Tx) error {
    61  	switch tx := tx.UnsignedAtomicTx.(type) {
    62  	case *evm.UnsignedImportTx:
    63  		for _, input := range tx.ImportedInputs {
    64  			utxoID := input.InputID()
    65  			if err := b.RemoveUTXO(ctx, tx.SourceChain, utxoID); err != nil {
    66  				return err
    67  			}
    68  		}
    69  
    70  		b.accountsLock.Lock()
    71  		defer b.accountsLock.Unlock()
    72  
    73  		for _, output := range tx.Outs {
    74  			account, ok := b.accounts[output.Address]
    75  			if !ok {
    76  				continue
    77  			}
    78  
    79  			balance := new(big.Int).SetUint64(output.Amount)
    80  			balance.Mul(balance, avaxConversionRate)
    81  			account.Balance.Add(account.Balance, balance)
    82  		}
    83  	case *evm.UnsignedExportTx:
    84  		txID := tx.ID()
    85  		for i, out := range tx.ExportedOutputs {
    86  			err := b.AddUTXO(
    87  				ctx,
    88  				tx.DestinationChain,
    89  				&avax.UTXO{
    90  					UTXOID: avax.UTXOID{
    91  						TxID:        txID,
    92  						OutputIndex: uint32(i),
    93  					},
    94  					Asset: avax.Asset{ID: out.AssetID()},
    95  					Out:   out.Out,
    96  				},
    97  			)
    98  			if err != nil {
    99  				return err
   100  			}
   101  		}
   102  
   103  		b.accountsLock.Lock()
   104  		defer b.accountsLock.Unlock()
   105  
   106  		for _, input := range tx.Ins {
   107  			account, ok := b.accounts[input.Address]
   108  			if !ok {
   109  				continue
   110  			}
   111  
   112  			balance := new(big.Int).SetUint64(input.Amount)
   113  			balance.Mul(balance, avaxConversionRate)
   114  			if account.Balance.Cmp(balance) == -1 {
   115  				return errInsufficientFunds
   116  			}
   117  			account.Balance.Sub(account.Balance, balance)
   118  
   119  			newNonce, err := math.Add(input.Nonce, 1)
   120  			if err != nil {
   121  				return err
   122  			}
   123  			account.Nonce = newNonce
   124  		}
   125  	default:
   126  		return fmt.Errorf("%w: %T", errUnknownTxType, tx)
   127  	}
   128  	return nil
   129  }
   130  
   131  func (b *backend) Balance(_ context.Context, addr ethcommon.Address) (*big.Int, error) {
   132  	b.accountsLock.RLock()
   133  	defer b.accountsLock.RUnlock()
   134  
   135  	account, exists := b.accounts[addr]
   136  	if !exists {
   137  		return nil, database.ErrNotFound
   138  	}
   139  	return account.Balance, nil
   140  }
   141  
   142  func (b *backend) Nonce(_ context.Context, addr ethcommon.Address) (uint64, error) {
   143  	b.accountsLock.RLock()
   144  	defer b.accountsLock.RUnlock()
   145  
   146  	account, exists := b.accounts[addr]
   147  	if !exists {
   148  		return 0, database.ErrNotFound
   149  	}
   150  	return account.Nonce, nil
   151  }