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 }