From d319c2b82d5f57a3b29e43d36d5e4c2092dab4da Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Mon, 24 Jun 2024 18:32:38 -0700 Subject: [PATCH 1/2] wip: separate atomic tx handling --- plugin/atx/codec.go | 38 ++ plugin/atx/errors.go | 35 ++ plugin/{evm => atx}/export_tx.go | 8 +- plugin/atx/formatting.go | 17 + plugin/{evm => atx}/import_tx.go | 12 +- plugin/atx/interfaces.go | 32 ++ plugin/{evm => atx}/metadata.go | 2 +- plugin/{evm => atx}/tx.go | 6 +- plugin/atx/vm.go | 354 ++++++++++++++++++ plugin/evm/atomic_backend.go | 3 + .../evm/atomic_trie_height_map_repair_test.go | 2 +- plugin/evm/atomic_trie_test.go | 6 +- plugin/evm/codec.go | 34 +- plugin/evm/export_tx_test.go | 41 +- plugin/evm/import_tx_test.go | 4 +- plugin/evm/interfaces.go | 10 + plugin/evm/mempool_atomic_gossiping_test.go | 6 +- plugin/evm/service.go | 4 +- plugin/evm/test_tx.go | 5 +- plugin/evm/tx_gossip_test.go | 6 +- plugin/evm/tx_test.go | 2 +- plugin/evm/vm.go | 306 ++------------- plugin/evm/vm_test.go | 76 ++-- 23 files changed, 622 insertions(+), 387 deletions(-) create mode 100644 plugin/atx/codec.go create mode 100644 plugin/atx/errors.go rename plugin/{evm => atx}/export_tx.go (99%) create mode 100644 plugin/atx/formatting.go rename plugin/{evm => atx}/import_tx.go (98%) create mode 100644 plugin/atx/interfaces.go rename plugin/{evm => atx}/metadata.go (98%) rename plugin/{evm => atx}/tx.go (98%) create mode 100644 plugin/atx/vm.go diff --git a/plugin/atx/codec.go b/plugin/atx/codec.go new file mode 100644 index 0000000000..7f759595c1 --- /dev/null +++ b/plugin/atx/codec.go @@ -0,0 +1,38 @@ +package atx + +import ( + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/codec/linearcodec" + "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +// Codec does serialization and deserialization +var Codec codec.Manager + +func init() { + Codec = codec.NewDefaultManager() + + var ( + lc = linearcodec.NewDefault() + errs = wrappers.Errs{} + ) + errs.Add( + lc.RegisterType(&UnsignedImportTx{}), + lc.RegisterType(&UnsignedExportTx{}), + ) + lc.SkipRegistrations(3) + errs.Add( + lc.RegisterType(&secp256k1fx.TransferInput{}), + lc.RegisterType(&secp256k1fx.MintOutput{}), + lc.RegisterType(&secp256k1fx.TransferOutput{}), + lc.RegisterType(&secp256k1fx.MintOperation{}), + lc.RegisterType(&secp256k1fx.Credential{}), + lc.RegisterType(&secp256k1fx.Input{}), + lc.RegisterType(&secp256k1fx.OutputOwners{}), + Codec.RegisterCodec(codecVersion, lc), + ) + if errs.Errored() { + panic(errs.Err) + } +} diff --git a/plugin/atx/errors.go b/plugin/atx/errors.go new file mode 100644 index 0000000000..58e9f3a82b --- /dev/null +++ b/plugin/atx/errors.go @@ -0,0 +1,35 @@ +package atx + +import "errors" + +var ( + errEmptyBlock = errors.New("empty block") + errUnsupportedFXs = errors.New("unsupported feature extensions") + errInvalidBlock = errors.New("invalid block") + errInvalidAddr = errors.New("invalid hex address") + errInsufficientAtomicTxFee = errors.New("atomic tx fee too low for atomic mempool") + errAssetIDMismatch = errors.New("asset IDs in the input don't match the utxo") + errNoImportInputs = errors.New("tx has no imported inputs") + errInputsNotSortedUnique = errors.New("inputs not sorted and unique") + errPublicKeySignatureMismatch = errors.New("signature doesn't match public key") + errWrongChainID = errors.New("tx has wrong chain ID") + errInsufficientFunds = errors.New("insufficient funds") + errNoExportOutputs = errors.New("tx has no export outputs") + errOutputsNotSorted = errors.New("tx outputs not sorted") + errOutputsNotSortedUnique = errors.New("outputs not sorted and unique") + errOverflowExport = errors.New("overflow when computing export amount + txFee") + errInvalidNonce = errors.New("invalid nonce") + errConflictingAtomicInputs = errors.New("invalid block due to conflicting atomic inputs") + errUnclesUnsupported = errors.New("uncles unsupported") + errRejectedParent = errors.New("rejected parent") + errInsufficientFundsForFee = errors.New("insufficient AVAX funds to pay transaction fee") + errNoEVMOutputs = errors.New("tx has no EVM outputs") + errNilBaseFeeApricotPhase3 = errors.New("nil base fee is invalid after apricotPhase3") + errNilExtDataGasUsedApricotPhase4 = errors.New("nil extDataGasUsed is invalid after apricotPhase4") + errNilBlockGasCostApricotPhase4 = errors.New("nil blockGasCost is invalid after apricotPhase4") + errConflictingAtomicTx = errors.New("conflicting atomic tx present") + errTooManyAtomicTx = errors.New("too many atomic tx") + errMissingAtomicTxs = errors.New("cannot build a block with non-empty extra data and zero atomic transactions") + errInvalidHeaderPredicateResults = errors.New("invalid header predicate results") + ErrConflictingAtomicInputs = errConflictingAtomicInputs +) diff --git a/plugin/evm/export_tx.go b/plugin/atx/export_tx.go similarity index 99% rename from plugin/evm/export_tx.go rename to plugin/atx/export_tx.go index b2e85ffa40..10a07de8b8 100644 --- a/plugin/evm/export_tx.go +++ b/plugin/atx/export_tx.go @@ -1,7 +1,7 @@ // (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package evm +package atx import ( "context" @@ -176,7 +176,7 @@ func (utx *UnsignedExportTx) Burned(assetID ids.ID) (uint64, error) { func (utx *UnsignedExportTx) SemanticVerify( vm *VM, stx *Tx, - _ *Block, + _ ids.ID, baseFee *big.Int, rules params.Rules, ) error { @@ -275,8 +275,8 @@ func (utx *UnsignedExportTx) AtomicOps() (ids.ID, *atomic.Requests, error) { return utx.DestinationChain, &atomic.Requests{PutRequests: elems}, nil } -// newExportTx returns a new ExportTx -func (vm *VM) newExportTx( +// NewExportTx returns a new ExportTx +func (vm *VM) NewExportTx( assetID ids.ID, // AssetID of the tokens to export amount uint64, // Amount of tokens to export chainID ids.ID, // Chain to send the UTXOs to diff --git a/plugin/atx/formatting.go b/plugin/atx/formatting.go new file mode 100644 index 0000000000..d4372d6503 --- /dev/null +++ b/plugin/atx/formatting.go @@ -0,0 +1,17 @@ +package atx + +import ( + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// GetEthAddress returns the ethereum address derived from [privKey] +func GetEthAddress(privKey *secp256k1.PrivateKey) common.Address { + return PublicKeyToEthAddress(privKey.PublicKey()) +} + +// PublicKeyToEthAddress returns the ethereum address derived from [pubKey] +func PublicKeyToEthAddress(pubKey *secp256k1.PublicKey) common.Address { + return crypto.PubkeyToAddress(*(pubKey.ToECDSA())) +} diff --git a/plugin/evm/import_tx.go b/plugin/atx/import_tx.go similarity index 98% rename from plugin/evm/import_tx.go rename to plugin/atx/import_tx.go index c6eb7c0930..75fd4274bc 100644 --- a/plugin/evm/import_tx.go +++ b/plugin/atx/import_tx.go @@ -1,7 +1,7 @@ // (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package evm +package atx import ( "context" @@ -177,7 +177,7 @@ func (utx *UnsignedImportTx) Burned(assetID ids.ID) (uint64, error) { func (utx *UnsignedImportTx) SemanticVerify( vm *VM, stx *Tx, - parent *Block, + parent ids.ID, baseFee *big.Int, rules params.Rules, ) error { @@ -273,8 +273,8 @@ func (utx *UnsignedImportTx) AtomicOps() (ids.ID, *atomic.Requests, error) { return utx.SourceChain, &atomic.Requests{RemoveRequests: utxoIDs}, nil } -// newImportTx returns a new ImportTx -func (vm *VM) newImportTx( +// NewImportTx returns a new ImportTx +func (vm *VM) NewImportTx( chainID ids.ID, // chain to import from to common.Address, // Address of recipient baseFee *big.Int, // fee to use post-AP3 @@ -290,11 +290,11 @@ func (vm *VM) newImportTx( return nil, fmt.Errorf("problem retrieving atomic UTXOs: %w", err) } - return vm.newImportTxWithUTXOs(chainID, to, baseFee, kc, atomicUTXOs) + return vm.NewImportTxWithUTXOs(chainID, to, baseFee, kc, atomicUTXOs) } // newImportTx returns a new ImportTx -func (vm *VM) newImportTxWithUTXOs( +func (vm *VM) NewImportTxWithUTXOs( chainID ids.ID, // chain to import from to common.Address, // Address of recipient baseFee *big.Int, // fee to use post-AP3 diff --git a/plugin/atx/interfaces.go b/plugin/atx/interfaces.go new file mode 100644 index 0000000000..b7901430fe --- /dev/null +++ b/plugin/atx/interfaces.go @@ -0,0 +1,32 @@ +package atx + +import ( + "math/big" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/coreth/core/types" + "github.com/ethereum/go-ethereum/common" +) + +type StateDB interface { + SubBalance(common.Address, *big.Int) + AddBalance(common.Address, *big.Int) + GetBalance(common.Address) *big.Int + + GetBalanceMultiCoin(common.Address, common.Hash) *big.Int + SubBalanceMultiCoin(common.Address, common.Hash, *big.Int) + AddBalanceMultiCoin(common.Address, common.Hash, *big.Int) + + GetNonce(common.Address) uint64 + SetNonce(common.Address, uint64) +} + +type BlockChain interface { + State() (StateDB, error) + CurrentHeader() *types.Header +} + +type BlockGetter interface { + GetBlockAndAtomicTxs(ids.ID) ([]*Tx, choices.Status, ids.ID, error) +} diff --git a/plugin/evm/metadata.go b/plugin/atx/metadata.go similarity index 98% rename from plugin/evm/metadata.go rename to plugin/atx/metadata.go index 2665d329bc..b25ab2d47e 100644 --- a/plugin/evm/metadata.go +++ b/plugin/atx/metadata.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package evm +package atx import ( "github.com/ava-labs/avalanchego/ids" diff --git a/plugin/evm/tx.go b/plugin/atx/tx.go similarity index 98% rename from plugin/evm/tx.go rename to plugin/atx/tx.go index 4df2bcc97b..e5255a9ee7 100644 --- a/plugin/evm/tx.go +++ b/plugin/atx/tx.go @@ -1,7 +1,7 @@ // (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package evm +package atx import ( "bytes" @@ -123,7 +123,7 @@ type UnsignedAtomicTx interface { // Verify attempts to verify that the transaction is well formed Verify(ctx *snow.Context, rules params.Rules) error // Attempts to verify this transaction with the provided state. - SemanticVerify(vm *VM, stx *Tx, parent *Block, baseFee *big.Int, rules params.Rules) error + SemanticVerify(vm *VM, stx *Tx, parent ids.ID, baseFee *big.Int, rules params.Rules) error // AtomicOps returns the blockchainID and set of atomic requests that // must be applied to shared memory for this transaction to be accepted. // The set of atomic requests must be returned in a consistent order. @@ -287,3 +287,5 @@ func mergeAtomicOps(txs []*Tx) (map[ids.ID]*atomic.Requests, error) { } return output, nil } + +var MergeAtomicOps = mergeAtomicOps diff --git a/plugin/atx/vm.go b/plugin/atx/vm.go new file mode 100644 index 0000000000..76963e7c27 --- /dev/null +++ b/plugin/atx/vm.go @@ -0,0 +1,354 @@ +package atx + +import ( + "fmt" + "math/big" + + "github.com/ava-labs/avalanchego/cache" + "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/choices" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/coreth/params" + "github.com/ethereum/go-ethereum/common" +) + +const ( + x2cRateInt64 int64 = 1_000_000_000 + x2cRateMinus1Int64 int64 = x2cRateInt64 - 1 +) + +var ( + // x2cRate is the conversion rate between the smallest denomination on the X-Chain + // 1 nAVAX and the smallest denomination on the C-Chain 1 wei. Where 1 nAVAX = 1 gWei. + // This is only required for AVAX because the denomination of 1 AVAX is 9 decimal + // places on the X and P chains, but is 18 decimal places within the EVM. + x2cRate = big.NewInt(x2cRateInt64) + x2cRateMinus1 = big.NewInt(x2cRateMinus1Int64) +) + +const ( + codecVersion = uint16(0) + maxUTXOsToFetch = 1024 + secpCacheSize = 1024 +) + +type VM struct { + ctx *snow.Context + bootstrapped bool + codec codec.Manager + clock *mockable.Clock + blockChain BlockChain + chainConfig *params.ChainConfig + blockGetter BlockGetter + + fx secp256k1fx.Fx + secpCache secp256k1.RecoverCache +} + +func NewVM(ctx *snow.Context, bVM BlockGetter, codec codec.Manager, clock *mockable.Clock, chain BlockChain, chainConfig *params.ChainConfig) (*VM, error) { + vm := &VM{ + ctx: ctx, + codec: codec, + clock: clock, + blockChain: chain, + chainConfig: chainConfig, + blockGetter: bVM, + } + vm.secpCache = secp256k1.RecoverCache{ + LRU: cache.LRU[ids.ID, *secp256k1.PublicKey]{ + Size: secpCacheSize, + }, + } + return vm, vm.fx.Initialize(bVM) +} + +type Block struct{} + +// GetAtomicUTXOs returns the utxos that at least one of the provided addresses is +// referenced in. +func (vm *VM) GetAtomicUTXOs( + chainID ids.ID, + addrs set.Set[ids.ShortID], + startAddr ids.ShortID, + startUTXOID ids.ID, + limit int, +) ([]*avax.UTXO, ids.ShortID, ids.ID, error) { + if limit <= 0 || limit > maxUTXOsToFetch { + limit = maxUTXOsToFetch + } + + addrsList := make([][]byte, addrs.Len()) + for i, addr := range addrs.List() { + addrsList[i] = addr.Bytes() + } + + allUTXOBytes, lastAddr, lastUTXO, err := vm.ctx.SharedMemory.Indexed( + chainID, + addrsList, + startAddr.Bytes(), + startUTXOID[:], + limit, + ) + if err != nil { + return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("error fetching atomic UTXOs: %w", err) + } + + lastAddrID, err := ids.ToShortID(lastAddr) + if err != nil { + lastAddrID = ids.ShortEmpty + } + lastUTXOID, err := ids.ToID(lastUTXO) + if err != nil { + lastUTXOID = ids.Empty + } + + utxos := make([]*avax.UTXO, len(allUTXOBytes)) + for i, utxoBytes := range allUTXOBytes { + utxo := &avax.UTXO{} + if _, err := vm.codec.Unmarshal(utxoBytes, utxo); err != nil { + return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("error parsing UTXO: %w", err) + } + utxos[i] = utxo + } + return utxos, lastAddrID, lastUTXOID, nil +} + +// GetSpendableFunds returns a list of EVMInputs and keys (in corresponding +// order) to total [amount] of [assetID] owned by [keys]. +// Note: we return [][]*secp256k1.PrivateKey even though each input +// corresponds to a single key, so that the signers can be passed in to +// [tx.Sign] which supports multiple keys on a single input. +func (vm *VM) GetSpendableFunds( + keys []*secp256k1.PrivateKey, + assetID ids.ID, + amount uint64, +) ([]EVMInput, [][]*secp256k1.PrivateKey, error) { + // Note: current state uses the state of the preferred block. + state, err := vm.blockChain.State() + if err != nil { + return nil, nil, err + } + inputs := []EVMInput{} + signers := [][]*secp256k1.PrivateKey{} + // Note: we assume that each key in [keys] is unique, so that iterating over + // the keys will not produce duplicated nonces in the returned EVMInput slice. + for _, key := range keys { + if amount == 0 { + break + } + addr := GetEthAddress(key) + var balance uint64 + if assetID == vm.ctx.AVAXAssetID { + // If the asset is AVAX, we divide by the x2cRate to convert back to the correct + // denomination of AVAX that can be exported. + balance = new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64() + } else { + balance = state.GetBalanceMultiCoin(addr, common.Hash(assetID)).Uint64() + } + if balance == 0 { + continue + } + if amount < balance { + balance = amount + } + nonce, err := vm.GetCurrentNonce(addr) + if err != nil { + return nil, nil, err + } + inputs = append(inputs, EVMInput{ + Address: addr, + Amount: balance, + AssetID: assetID, + Nonce: nonce, + }) + signers = append(signers, []*secp256k1.PrivateKey{key}) + amount -= balance + } + + if amount > 0 { + return nil, nil, errInsufficientFunds + } + + return inputs, signers, nil +} + +// GetSpendableAVAXWithFee returns a list of EVMInputs and keys (in corresponding +// order) to total [amount] + [fee] of [AVAX] owned by [keys]. +// This function accounts for the added cost of the additional inputs needed to +// create the transaction and makes sure to skip any keys with a balance that is +// insufficient to cover the additional fee. +// Note: we return [][]*secp256k1.PrivateKey even though each input +// corresponds to a single key, so that the signers can be passed in to +// [tx.Sign] which supports multiple keys on a single input. +func (vm *VM) GetSpendableAVAXWithFee( + keys []*secp256k1.PrivateKey, + amount uint64, + cost uint64, + baseFee *big.Int, +) ([]EVMInput, [][]*secp256k1.PrivateKey, error) { + // Note: current state uses the state of the preferred block. + state, err := vm.blockChain.State() + if err != nil { + return nil, nil, err + } + + initialFee, err := CalculateDynamicFee(cost, baseFee) + if err != nil { + return nil, nil, err + } + + newAmount, err := math.Add64(amount, initialFee) + if err != nil { + return nil, nil, err + } + amount = newAmount + + inputs := []EVMInput{} + signers := [][]*secp256k1.PrivateKey{} + // Note: we assume that each key in [keys] is unique, so that iterating over + // the keys will not produce duplicated nonces in the returned EVMInput slice. + for _, key := range keys { + if amount == 0 { + break + } + + prevFee, err := CalculateDynamicFee(cost, baseFee) + if err != nil { + return nil, nil, err + } + + newCost := cost + EVMInputGas + newFee, err := CalculateDynamicFee(newCost, baseFee) + if err != nil { + return nil, nil, err + } + + additionalFee := newFee - prevFee + + addr := GetEthAddress(key) + // Since the asset is AVAX, we divide by the x2cRate to convert back to + // the correct denomination of AVAX that can be exported. + balance := new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64() + // If the balance for [addr] is insufficient to cover the additional cost + // of adding an input to the transaction, skip adding the input altogether + if balance <= additionalFee { + continue + } + + // Update the cost for the next iteration + cost = newCost + + newAmount, err := math.Add64(amount, additionalFee) + if err != nil { + return nil, nil, err + } + amount = newAmount + + // Use the entire [balance] as an input, but if the required [amount] + // is less than the balance, update the [inputAmount] to spend the + // minimum amount to finish the transaction. + inputAmount := balance + if amount < balance { + inputAmount = amount + } + nonce, err := vm.GetCurrentNonce(addr) + if err != nil { + return nil, nil, err + } + inputs = append(inputs, EVMInput{ + Address: addr, + Amount: inputAmount, + AssetID: vm.ctx.AVAXAssetID, + Nonce: nonce, + }) + signers = append(signers, []*secp256k1.PrivateKey{key}) + amount -= inputAmount + } + + if amount > 0 { + return nil, nil, errInsufficientFunds + } + + return inputs, signers, nil +} + +// GetCurrentNonce returns the nonce associated with the address at the +// preferred block +func (vm *VM) GetCurrentNonce(address common.Address) (uint64, error) { + // Note: current state uses the state of the preferred block. + state, err := vm.blockChain.State() + if err != nil { + return 0, err + } + return state.GetNonce(address), nil +} + +// currentRules returns the chain rules for the current block. +func (vm *VM) currentRules() params.Rules { + header := vm.blockChain.CurrentHeader() + return vm.chainConfig.Rules(header.Number, header.Time) +} + +// conflicts returns an error if [inputs] conflicts with any of the atomic inputs contained in [ancestor] +// or any of its ancestor blocks going back to the last accepted block in its ancestry. If [ancestor] is +// accepted, then nil will be returned immediately. +// If the ancestry of [ancestor] cannot be fetched, then [errRejectedParent] may be returned. +func (vm *VM) conflicts(inputs set.Set[ids.ID], ancestorID ids.ID) error { + for { + // If the ancestor is unknown, then the parent failed + // verification when it was called. + // If the ancestor is rejected, then this block shouldn't be + // inserted into the canonical chain because the parent is + // will be missing. + // If the ancestor is processing, then the block may have + // been verified. + txs, status, parentID, err := vm.blockGetter.GetBlockAndAtomicTxs(ancestorID) + if err != nil { + return errRejectedParent + } + if status == choices.Unknown || status == choices.Rejected { + return errRejectedParent + } + + if status == choices.Accepted { + break + } + + // If any of the atomic transactions in the ancestor conflict with [inputs] + // return an error. + for _, atomicTx := range txs { + if inputs.Overlaps(atomicTx.InputUTXOs()) { + return errConflictingAtomicInputs + } + } + + // Move up the chain. + ancestorID = parentID + } + return nil +} + +func (vm *VM) Bootstrapping() error { return vm.fx.Bootstrapping() } +func (vm *VM) Bootstrapped() error { + vm.bootstrapped = true + return vm.fx.Bootstrapped() +} + +// mergeAtomicOps merges atomic ops for [chainID] represented by [requests] +// to the [output] map provided. +func mergeAtomicOpsToMap(output map[ids.ID]*atomic.Requests, chainID ids.ID, requests *atomic.Requests) { + if request, exists := output[chainID]; exists { + request.PutRequests = append(request.PutRequests, requests.PutRequests...) + request.RemoveRequests = append(request.RemoveRequests, requests.RemoveRequests...) + } else { + output[chainID] = requests + } +} diff --git a/plugin/evm/atomic_backend.go b/plugin/evm/atomic_backend.go index dc8c36a396..cf73e5d7d2 100644 --- a/plugin/evm/atomic_backend.go +++ b/plugin/evm/atomic_backend.go @@ -16,6 +16,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/plugin/atx" syncclient "github.com/ava-labs/coreth/sync/client" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -23,6 +24,8 @@ import ( var _ AtomicBackend = &atomicBackend{} +var mergeAtomicOps = atx.MergeAtomicOps + // AtomicBackend abstracts the verification and processing // of atomic transactions type AtomicBackend interface { diff --git a/plugin/evm/atomic_trie_height_map_repair_test.go b/plugin/evm/atomic_trie_height_map_repair_test.go index 0b95b252cb..d66613b92c 100644 --- a/plugin/evm/atomic_trie_height_map_repair_test.go +++ b/plugin/evm/atomic_trie_height_map_repair_test.go @@ -53,7 +53,7 @@ func (test testAtomicTrieRepairHeightMap) run(t *testing.T) { heightMap := make(map[uint64]common.Hash) for height := uint64(1); height <= test.lastAccepted; height++ { - atomicRequests := testDataImportTx().mustAtomicOps() + atomicRequests := mustAtomicOps(testDataImportTx()) if test.skipAtomicTxs(height) { atomicRequests = nil } diff --git a/plugin/evm/atomic_trie_test.go b/plugin/evm/atomic_trie_test.go index db3b675a41..b9bc223940 100644 --- a/plugin/evm/atomic_trie_test.go +++ b/plugin/evm/atomic_trie_test.go @@ -25,7 +25,7 @@ import ( const testCommitInterval = 100 -func (tx *Tx) mustAtomicOps() map[ids.ID]*atomic.Requests { +func mustAtomicOps(tx *Tx) map[ids.ID]*atomic.Requests { id, reqs, err := tx.AtomicOps() if err != nil { panic(err) @@ -281,7 +281,7 @@ func TestIndexerWriteAndRead(t *testing.T) { // process 305 blocks so that we get three commits (100, 200, 300) for height := uint64(1); height <= testCommitInterval*3+5; /*=305*/ height++ { - atomicRequests := testDataImportTx().mustAtomicOps() + atomicRequests := mustAtomicOps(testDataImportTx()) err := indexAtomicTxs(atomicTrie, height, atomicRequests) assert.NoError(t, err) if height%testCommitInterval == 0 { @@ -370,7 +370,7 @@ func TestIndexingNilShouldNotImpactTrie(t *testing.T) { // operations to index ops := make([]map[ids.ID]*atomic.Requests, 0) for i := 0; i <= testCommitInterval; i++ { - ops = append(ops, testDataImportTx().mustAtomicOps()) + ops = append(ops, mustAtomicOps(testDataImportTx())) } // without nils diff --git a/plugin/evm/codec.go b/plugin/evm/codec.go index e4c38761e3..6a43bc2b23 100644 --- a/plugin/evm/codec.go +++ b/plugin/evm/codec.go @@ -7,40 +7,10 @@ import ( "fmt" "github.com/ava-labs/avalanchego/codec" - "github.com/ava-labs/avalanchego/codec/linearcodec" - "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/coreth/plugin/atx" ) -// Codec does serialization and deserialization -var Codec codec.Manager - -func init() { - Codec = codec.NewDefaultManager() - - var ( - lc = linearcodec.NewDefault() - errs = wrappers.Errs{} - ) - errs.Add( - lc.RegisterType(&UnsignedImportTx{}), - lc.RegisterType(&UnsignedExportTx{}), - ) - lc.SkipRegistrations(3) - errs.Add( - lc.RegisterType(&secp256k1fx.TransferInput{}), - lc.RegisterType(&secp256k1fx.MintOutput{}), - lc.RegisterType(&secp256k1fx.TransferOutput{}), - lc.RegisterType(&secp256k1fx.MintOperation{}), - lc.RegisterType(&secp256k1fx.Credential{}), - lc.RegisterType(&secp256k1fx.Input{}), - lc.RegisterType(&secp256k1fx.OutputOwners{}), - Codec.RegisterCodec(codecVersion, lc), - ) - if errs.Errored() { - panic(errs.Err) - } -} +var Codec = atx.Codec // extractAtomicTxs returns the atomic transactions in [atomicTxBytes] if // they exist. diff --git a/plugin/evm/export_tx_test.go b/plugin/evm/export_tx_test.go index f7c0e92cc6..f6b4c68306 100644 --- a/plugin/evm/export_tx_test.go +++ b/plugin/evm/export_tx_test.go @@ -6,6 +6,7 @@ package evm import ( "bytes" "context" + "errors" "math/big" "testing" @@ -18,9 +19,27 @@ import ( "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/atx" "github.com/ethereum/go-ethereum/common" ) +var ( + errWrongBlockchainID = errors.New("wrong blockchain ID provided") + errWrongNetworkID = errors.New("tx was issued with a different network ID") + errNilTx = errors.New("tx is nil") + errNoValueOutput = errors.New("output has no value") + errNoValueInput = errors.New("input has no value") + errNilOutput = errors.New("nil output") + errNilInput = errors.New("nil input") + errEmptyAssetID = errors.New("empty asset ID is not valid") + errNilBaseFee = errors.New("cannot calculate dynamic fee with nil baseFee") + errFeeOverflow = errors.New("overflow occurred while calculating the fee") + errExportNonAVAXInputBanff = errors.New("export input cannot contain non-AVAX in Banff") + errExportNonAVAXOutputBanff = errors.New("export output cannot contain non-AVAX in Banff") + errImportNonAVAXInputBanff = errors.New("import input cannot contain non-AVAX in Banff") + errImportNonAVAXOutputBanff = errors.New("import output cannot contain non-AVAX in Banff") +) + // createExportTxOptions adds funds to shared memory, imports them, and returns a list of export transactions // that attempt to send the funds to each of the test keys (list of length 3). func createExportTxOptions(t *testing.T, vm *VM, issuer chan engCommon.Message, sharedMemory *atomic.Memory) []*Tx { @@ -54,7 +73,7 @@ func createExportTxOptions(t *testing.T, vm *VM, issuer chan engCommon.Message, } // Import the funds - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -85,7 +104,7 @@ func createExportTxOptions(t *testing.T, vm *VM, issuer chan engCommon.Message, // Use the funds to create 3 conflicting export transactions sending the funds to each of the test addresses exportTxs := make([]*Tx, 0, 3) for _, addr := range testShortIDAddrs { - exportTx, err := vm.newExportTx(vm.ctx.AVAXAssetID, uint64(5000000), vm.ctx.XChainID, addr, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + exportTx, err := vm.NewExportTx(vm.ctx.AVAXAssetID, uint64(5000000), vm.ctx.XChainID, addr, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -366,7 +385,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) { t.Fatal(err) } - tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -910,7 +929,7 @@ func TestExportTxSemanticVerify(t *testing.T) { tx := test.tx exportTx := tx.UnsignedAtomicTx - err := exportTx.SemanticVerify(vm, tx, parent, test.baseFee, test.rules) + err := exportTx.SemanticVerify(vm.VM, tx, parent.ID(), test.baseFee, test.rules) if test.shouldErr && err == nil { t.Fatalf("should have errored but returned valid") } @@ -1121,7 +1140,7 @@ func TestExportTxVerify(t *testing.T) { // Pass in a list of signers here with the appropriate length // to avoid causing a nil-pointer error in the helper method emptySigners := make([][]*secp256k1.PrivateKey, 2) - SortEVMInputsAndSigners(exportTx.Ins, emptySigners) + atx.SortEVMInputsAndSigners(exportTx.Ins, emptySigners) ctx := NewContext() @@ -1721,7 +1740,7 @@ func TestNewExportTx(t *testing.T) { t.Fatal(err) } - tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -1752,14 +1771,14 @@ func TestNewExportTx(t *testing.T) { parent = vm.LastAcceptedBlockInternal().(*Block) exportAmount := uint64(5000000) - tx, err = vm.newExportTx(vm.ctx.AVAXAssetID, exportAmount, vm.ctx.XChainID, testShortIDAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err = vm.NewExportTx(vm.ctx.AVAXAssetID, exportAmount, vm.ctx.XChainID, testShortIDAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } exportTx := tx.UnsignedAtomicTx - if err := exportTx.SemanticVerify(vm, tx, parent, parent.ethBlock.BaseFee(), test.rules); err != nil { + if err := exportTx.SemanticVerify(vm.VM, tx, parent.ID(), parent.ethBlock.BaseFee(), test.rules); err != nil { t.Fatal("newExportTx created an invalid transaction", err) } @@ -1910,7 +1929,7 @@ func TestNewExportTxMulticoin(t *testing.T) { t.Fatal(err) } - tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -1947,14 +1966,14 @@ func TestNewExportTxMulticoin(t *testing.T) { t.Fatal(err) } - tx, err = vm.newExportTx(tid, exportAmount, vm.ctx.XChainID, exportId, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err = vm.NewExportTx(tid, exportAmount, vm.ctx.XChainID, exportId, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } exportTx := tx.UnsignedAtomicTx - if err := exportTx.SemanticVerify(vm, tx, parent, parent.ethBlock.BaseFee(), test.rules); err != nil { + if err := exportTx.SemanticVerify(vm.VM, tx, parent.ID(), parent.ethBlock.BaseFee(), test.rules); err != nil { t.Fatal("newExportTx created an invalid transaction", err) } diff --git a/plugin/evm/import_tx_test.go b/plugin/evm/import_tx_test.go index ddcde4a879..f5c7eb2593 100644 --- a/plugin/evm/import_tx_test.go +++ b/plugin/evm/import_tx_test.go @@ -53,7 +53,7 @@ func createImportTxOptions(t *testing.T, vm *VM, sharedMemory *atomic.Memory) [] importTxs := make([]*Tx, 0, 3) for _, ethAddr := range testEthAddrs { - importTx, err := vm.newImportTx(vm.ctx.XChainID, ethAddr, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, ethAddr, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -432,7 +432,7 @@ func TestNewImportTx(t *testing.T) { t.Fatal(err) } - tx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } diff --git a/plugin/evm/interfaces.go b/plugin/evm/interfaces.go index f95e637139..8e903a6f92 100644 --- a/plugin/evm/interfaces.go +++ b/plugin/evm/interfaces.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/coreth/eth" "github.com/ava-labs/coreth/miner" "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/atx" "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/coreth/v2/chain" "github.com/ethereum/go-ethereum/common" @@ -163,3 +164,12 @@ func (v *v2BlockChainer) State() (StateDB, error) { func (v *v2BlockChainer) StateAt(root common.Hash) (StateDB, error) { return v.BlockChain.StateAt(root) } + +type atxChain struct { + BlockChain +} + +func (a *atxChain) State() (atx.StateDB, error) { + state, err := a.BlockChain.State() + return state, err +} diff --git a/plugin/evm/mempool_atomic_gossiping_test.go b/plugin/evm/mempool_atomic_gossiping_test.go index 741c177b90..d4ac884805 100644 --- a/plugin/evm/mempool_atomic_gossiping_test.go +++ b/plugin/evm/mempool_atomic_gossiping_test.go @@ -131,14 +131,14 @@ func TestMempoolPriorityDrop(t *testing.T) { mempool := vm.mempool mempool.maxSize = 1 - tx1, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + tx1, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } assert.NoError(mempool.AddTx(tx1)) assert.True(mempool.has(tx1.ID())) - tx2, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[1], initialBaseFee, []*secp256k1.PrivateKey{testKeys[1]}) + tx2, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[1], initialBaseFee, []*secp256k1.PrivateKey{testKeys[1]}) if err != nil { t.Fatal(err) } @@ -146,7 +146,7 @@ func TestMempoolPriorityDrop(t *testing.T) { assert.True(mempool.has(tx1.ID())) assert.False(mempool.has(tx2.ID())) - tx3, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[1], new(big.Int).Mul(initialBaseFee, big.NewInt(2)), []*secp256k1.PrivateKey{testKeys[1]}) + tx3, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[1], new(big.Int).Mul(initialBaseFee, big.NewInt(2)), []*secp256k1.PrivateKey{testKeys[1]}) if err != nil { t.Fatal(err) } diff --git a/plugin/evm/service.go b/plugin/evm/service.go index 7f57be5520..382f246b5d 100644 --- a/plugin/evm/service.go +++ b/plugin/evm/service.go @@ -219,7 +219,7 @@ func (service *AvaxAPI) Import(_ *http.Request, args *ImportArgs, response *api. baseFee = args.BaseFee.ToInt() } - tx, err := service.vm.newImportTx(chainID, args.To, baseFee, privKeys) + tx, err := service.vm.NewImportTx(chainID, args.To, baseFee, privKeys) if err != nil { return err } @@ -322,7 +322,7 @@ func (service *AvaxAPI) Export(_ *http.Request, args *ExportArgs, response *api. } // Create the transaction - tx, err := service.vm.newExportTx( + tx, err := service.vm.NewExportTx( assetID, // AssetID uint64(args.Amount), // Amount chainID, // ID of the chain to send the funds to diff --git a/plugin/evm/test_tx.go b/plugin/evm/test_tx.go index 2aecb4e517..4fef92b0ae 100644 --- a/plugin/evm/test_tx.go +++ b/plugin/evm/test_tx.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/coreth/params" + "github.com/ava-labs/coreth/plugin/atx" ) type TestUnsignedTx struct { @@ -65,12 +66,12 @@ func (t *TestUnsignedTx) SignedBytes() []byte { return t.SignedBytesV } func (t *TestUnsignedTx) InputUTXOs() set.Set[ids.ID] { return t.InputUTXOsV } // SemanticVerify implements the UnsignedAtomicTx interface -func (t *TestUnsignedTx) SemanticVerify(vm *VM, stx *Tx, parent *Block, baseFee *big.Int, rules params.Rules) error { +func (t *TestUnsignedTx) SemanticVerify(vm *atx.VM, stx *Tx, parent ids.ID, baseFee *big.Int, rules params.Rules) error { return t.SemanticVerifyV } // EVMStateTransfer implements the UnsignedAtomicTx interface -func (t *TestUnsignedTx) EVMStateTransfer(ctx *snow.Context, state StateDB) error { +func (t *TestUnsignedTx) EVMStateTransfer(ctx *snow.Context, state atx.StateDB) error { return t.EVMStateTransferV } diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index 67f3868435..272570f614 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -271,7 +271,7 @@ func TestAtomicTxGossip(t *testing.T) { pk.PublicKey().Address(), ) require.NoError(err) - tx, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) + tx, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) require.NoError(err) require.NoError(vm.mempool.AddLocalTx(tx)) @@ -492,7 +492,7 @@ func TestAtomicTxPushGossipOutbound(t *testing.T) { pk.PublicKey().Address(), ) require.NoError(err) - tx, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) + tx, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) require.NoError(err) require.NoError(vm.mempool.AddLocalTx(tx)) vm.atomicTxPushGossiper.Add(&GossipAtomicTx{tx}) @@ -568,7 +568,7 @@ func TestAtomicTxPushGossipInbound(t *testing.T) { pk.PublicKey().Address(), ) require.NoError(err) - tx, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) + tx, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, address, initialBaseFee, secp256k1fx.NewKeychain(pk), []*avax.UTXO{utxo}) require.NoError(err) require.NoError(vm.mempool.AddLocalTx(tx)) diff --git a/plugin/evm/tx_test.go b/plugin/evm/tx_test.go index 1c72120065..cfe562544b 100644 --- a/plugin/evm/tx_test.go +++ b/plugin/evm/tx_test.go @@ -115,7 +115,7 @@ func executeTxTest(t *testing.T, test atomicTxTest) { } lastAcceptedBlock := vm.LastAcceptedBlockInternal().(*Block) - if err := tx.UnsignedAtomicTx.SemanticVerify(vm, tx, lastAcceptedBlock, baseFee, rules); len(test.semanticVerifyErr) == 0 && err != nil { + if err := tx.UnsignedAtomicTx.SemanticVerify(vm.VM, tx, lastAcceptedBlock.ID(), baseFee, rules); len(test.semanticVerifyErr) == 0 && err != nil { t.Fatalf("SemanticVerify failed unexpectedly due to: %s", err) } else if len(test.semanticVerifyErr) != 0 { if err == nil { diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 899f5169f9..c279bdc717 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -34,6 +34,7 @@ import ( "github.com/ava-labs/coreth/miner" "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/peer" + "github.com/ava-labs/coreth/plugin/atx" "github.com/ava-labs/coreth/plugin/evm/message" "github.com/ava-labs/coreth/trie/triedb/hashdb" @@ -65,7 +66,6 @@ import ( avalancheRPC "github.com/gorilla/rpc/v2" - "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" "github.com/ava-labs/avalanchego/database" @@ -76,18 +76,14 @@ import ( "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/perms" "github.com/ava-labs/avalanchego/utils/profiler" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/chain" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" @@ -120,11 +116,9 @@ const ( // Max time from current time allowed for blocks, before they're considered future blocks // and fail verification maxFutureBlockTime = 10 * time.Second - maxUTXOsToFetch = 1024 defaultMempoolSize = 4096 codecVersion = uint16(0) - secpCacheSize = 1024 decidedCacheSize = 10 * units.MiB missingCacheSize = 50 unverifiedCacheSize = 5 * units.MiB @@ -195,7 +189,7 @@ var ( errOutputsNotSortedUnique = errors.New("outputs not sorted and unique") errOverflowExport = errors.New("overflow when computing export amount + txFee") errInvalidNonce = errors.New("invalid nonce") - errConflictingAtomicInputs = errors.New("invalid block due to conflicting atomic inputs") + errConflictingAtomicInputs = atx.ErrConflictingAtomicInputs errUnclesUnsupported = errors.New("uncles unsupported") errRejectedParent = errors.New("rejected parent") errInsufficientFundsForFee = errors.New("insufficient AVAX funds to pay transaction fee") @@ -300,9 +294,6 @@ type VM struct { shutdownChan chan struct{} shutdownWg sync.WaitGroup - fx secp256k1fx.Fx - secpCache secp256k1.RecoverCache - // Continuous Profiler profiler profiler.ContinuousProfiler @@ -335,8 +326,23 @@ type VM struct { atomicTxGossipHandler p2p.Handler atomicTxPushGossiper *gossip.PushGossiper[*GossipAtomicTx] atomicTxPullGossiper gossip.Gossiper + + *atx.VM } +type ( + Tx = atx.Tx + EVMInput = atx.EVMInput + EVMOutput = atx.EVMOutput + UnsignedImportTx = atx.UnsignedImportTx + UnsignedAtomicTx = atx.UnsignedAtomicTx + UnsignedExportTx = atx.UnsignedExportTx +) + +var ( + CalculateDynamicFee = atx.CalculateDynamicFee +) + // Codec implements the secp256k1fx interface func (vm *VM) Codec() codec.Manager { return vm.codec } @@ -551,11 +557,6 @@ func (vm *VM) Initialize( vm.chainConfig = g.Config vm.networkID = vm.ethConfig.NetworkId - vm.secpCache = secp256k1.RecoverCache{ - LRU: cache.LRU[ids.ID, *secp256k1.PublicKey]{ - Size: secpCacheSize, - }, - } if err := vm.chainConfig.Verify(); err != nil { return fmt.Errorf("failed to verify chain config: %w", err) @@ -653,7 +654,8 @@ func (vm *VM) Initialize( // ignored by the VM's codec. vm.baseCodec = linearcodec.NewDefault() - if err := vm.fx.Initialize(vm); err != nil { + vm.VM, err = atx.NewVM(vm.ctx, vm, vm.codec, &vm.clock, &atxChain{vm.blockChain}, vm.chainConfig) + if err != nil { return err } @@ -1040,14 +1042,14 @@ func (vm *VM) SetState(_ context.Context, state snow.State) error { // Ensure snapshots are initialized before bootstrapping (i.e., if state sync is skipped). // Note calling this function has no effect if snapshots are already initialized. vm.blockChain.InitializeSnapshots() - return vm.fx.Bootstrapping() + return vm.VM.Bootstrapping() case snow.NormalOp: // Initialize goroutines related to block building once we enter normal operation as there is no need to handle mempool gossip before this point. if err := vm.initBlockBuilding(); err != nil { return fmt.Errorf("failed to initialize block building: %w", err) } vm.bootstrapped = true - return vm.fx.Bootstrapped() + return vm.VM.Bootstrapped() default: return snow.ErrUnknownState } @@ -1498,47 +1500,6 @@ func (vm *VM) CreateStaticHandlers(context.Context) (map[string]http.Handler, er ****************************************************************************** */ -// conflicts returns an error if [inputs] conflicts with any of the atomic inputs contained in [ancestor] -// or any of its ancestor blocks going back to the last accepted block in its ancestry. If [ancestor] is -// accepted, then nil will be returned immediately. -// If the ancestry of [ancestor] cannot be fetched, then [errRejectedParent] may be returned. -func (vm *VM) conflicts(inputs set.Set[ids.ID], ancestor *Block) error { - for ancestor.Status() != choices.Accepted { - // If any of the atomic transactions in the ancestor conflict with [inputs] - // return an error. - for _, atomicTx := range ancestor.atomicTxs { - if inputs.Overlaps(atomicTx.InputUTXOs()) { - return errConflictingAtomicInputs - } - } - - // Move up the chain. - nextAncestorID := ancestor.Parent() - // If the ancestor is unknown, then the parent failed - // verification when it was called. - // If the ancestor is rejected, then this block shouldn't be - // inserted into the canonical chain because the parent is - // will be missing. - // If the ancestor is processing, then the block may have - // been verified. - nextAncestorIntf, err := vm.GetBlockInternal(context.TODO(), nextAncestorID) - if err != nil { - return errRejectedParent - } - - if blkStatus := nextAncestorIntf.Status(); blkStatus == choices.Unknown || blkStatus == choices.Rejected { - return errRejectedParent - } - nextAncestor, ok := nextAncestorIntf.(*Block) - if !ok { - return fmt.Errorf("ancestor block %s had unexpected type %T", nextAncestor.ID(), nextAncestorIntf) - } - ancestor = nextAncestor - } - - return nil -} - // getAtomicTx returns the requested transaction, status, and height. // If the status is Unknown, then the returned transaction will be nil. func (vm *VM) getAtomicTx(txID ids.ID) (*Tx, Status, uint64, error) { @@ -1635,7 +1596,7 @@ func (vm *VM) verifyTx(tx *Tx, parentHash common.Hash, baseFee *big.Int, state S if !ok { return fmt.Errorf("parent block %s had unexpected type %T", parentIntf.ID(), parentIntf) } - if err := tx.UnsignedAtomicTx.SemanticVerify(vm, tx, parent, baseFee, rules); err != nil { + if err := tx.UnsignedAtomicTx.SemanticVerify(vm.VM, tx, parent.ID(), baseFee, rules); err != nil { return err } return tx.UnsignedAtomicTx.EVMStateTransfer(vm.ctx, state) @@ -1671,7 +1632,7 @@ func (vm *VM) verifyTxs(txs []*Tx, parentHash common.Hash, baseFee *big.Int, hei inputs := set.Set[ids.ID]{} for _, atomicTx := range txs { utx := atomicTx.UnsignedAtomicTx - if err := utx.SemanticVerify(vm, atomicTx, ancestor, baseFee, rules); err != nil { + if err := utx.SemanticVerify(vm.VM, atomicTx, ancestor.ID(), baseFee, rules); err != nil { return fmt.Errorf("invalid block due to failed semanatic verify: %w at height %d", err, height) } txInputs := utx.InputUTXOs() @@ -1683,223 +1644,16 @@ func (vm *VM) verifyTxs(txs []*Tx, parentHash common.Hash, baseFee *big.Int, hei return nil } -// GetAtomicUTXOs returns the utxos that at least one of the provided addresses is -// referenced in. -func (vm *VM) GetAtomicUTXOs( - chainID ids.ID, - addrs set.Set[ids.ShortID], - startAddr ids.ShortID, - startUTXOID ids.ID, - limit int, -) ([]*avax.UTXO, ids.ShortID, ids.ID, error) { - if limit <= 0 || limit > maxUTXOsToFetch { - limit = maxUTXOsToFetch - } - - addrsList := make([][]byte, addrs.Len()) - for i, addr := range addrs.List() { - addrsList[i] = addr.Bytes() - } - - allUTXOBytes, lastAddr, lastUTXO, err := vm.ctx.SharedMemory.Indexed( - chainID, - addrsList, - startAddr.Bytes(), - startUTXOID[:], - limit, - ) - if err != nil { - return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("error fetching atomic UTXOs: %w", err) - } - - lastAddrID, err := ids.ToShortID(lastAddr) - if err != nil { - lastAddrID = ids.ShortEmpty - } - lastUTXOID, err := ids.ToID(lastUTXO) - if err != nil { - lastUTXOID = ids.Empty - } - - utxos := make([]*avax.UTXO, len(allUTXOBytes)) - for i, utxoBytes := range allUTXOBytes { - utxo := &avax.UTXO{} - if _, err := vm.codec.Unmarshal(utxoBytes, utxo); err != nil { - return nil, ids.ShortID{}, ids.ID{}, fmt.Errorf("error parsing UTXO: %w", err) - } - utxos[i] = utxo - } - return utxos, lastAddrID, lastUTXOID, nil -} - -// GetSpendableFunds returns a list of EVMInputs and keys (in corresponding -// order) to total [amount] of [assetID] owned by [keys]. -// Note: we return [][]*secp256k1.PrivateKey even though each input -// corresponds to a single key, so that the signers can be passed in to -// [tx.Sign] which supports multiple keys on a single input. -func (vm *VM) GetSpendableFunds( - keys []*secp256k1.PrivateKey, - assetID ids.ID, - amount uint64, -) ([]EVMInput, [][]*secp256k1.PrivateKey, error) { - // Note: current state uses the state of the preferred block. - state, err := vm.blockChain.State() - if err != nil { - return nil, nil, err - } - inputs := []EVMInput{} - signers := [][]*secp256k1.PrivateKey{} - // Note: we assume that each key in [keys] is unique, so that iterating over - // the keys will not produce duplicated nonces in the returned EVMInput slice. - for _, key := range keys { - if amount == 0 { - break - } - addr := GetEthAddress(key) - var balance uint64 - if assetID == vm.ctx.AVAXAssetID { - // If the asset is AVAX, we divide by the x2cRate to convert back to the correct - // denomination of AVAX that can be exported. - balance = new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64() - } else { - balance = state.GetBalanceMultiCoin(addr, common.Hash(assetID)).Uint64() - } - if balance == 0 { - continue - } - if amount < balance { - balance = amount - } - nonce, err := vm.GetCurrentNonce(addr) - if err != nil { - return nil, nil, err - } - inputs = append(inputs, EVMInput{ - Address: addr, - Amount: balance, - AssetID: assetID, - Nonce: nonce, - }) - signers = append(signers, []*secp256k1.PrivateKey{key}) - amount -= balance - } - - if amount > 0 { - return nil, nil, errInsufficientFunds - } - - return inputs, signers, nil -} - -// GetSpendableAVAXWithFee returns a list of EVMInputs and keys (in corresponding -// order) to total [amount] + [fee] of [AVAX] owned by [keys]. -// This function accounts for the added cost of the additional inputs needed to -// create the transaction and makes sure to skip any keys with a balance that is -// insufficient to cover the additional fee. -// Note: we return [][]*secp256k1.PrivateKey even though each input -// corresponds to a single key, so that the signers can be passed in to -// [tx.Sign] which supports multiple keys on a single input. -func (vm *VM) GetSpendableAVAXWithFee( - keys []*secp256k1.PrivateKey, - amount uint64, - cost uint64, - baseFee *big.Int, -) ([]EVMInput, [][]*secp256k1.PrivateKey, error) { - // Note: current state uses the state of the preferred block. - state, err := vm.blockChain.State() +func (vm *VM) GetBlockAndAtomicTxs(blkID ids.ID) ([]*Tx, choices.Status, ids.ID, error) { + blkIntf, err := vm.GetBlockInternal(context.TODO(), blkID) if err != nil { - return nil, nil, err - } - - initialFee, err := CalculateDynamicFee(cost, baseFee) - if err != nil { - return nil, nil, err + return nil, choices.Unknown, ids.ID{}, err } - - newAmount, err := math.Add64(amount, initialFee) - if err != nil { - return nil, nil, err - } - amount = newAmount - - inputs := []EVMInput{} - signers := [][]*secp256k1.PrivateKey{} - // Note: we assume that each key in [keys] is unique, so that iterating over - // the keys will not produce duplicated nonces in the returned EVMInput slice. - for _, key := range keys { - if amount == 0 { - break - } - - prevFee, err := CalculateDynamicFee(cost, baseFee) - if err != nil { - return nil, nil, err - } - - newCost := cost + EVMInputGas - newFee, err := CalculateDynamicFee(newCost, baseFee) - if err != nil { - return nil, nil, err - } - - additionalFee := newFee - prevFee - - addr := GetEthAddress(key) - // Since the asset is AVAX, we divide by the x2cRate to convert back to - // the correct denomination of AVAX that can be exported. - balance := new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64() - // If the balance for [addr] is insufficient to cover the additional cost - // of adding an input to the transaction, skip adding the input altogether - if balance <= additionalFee { - continue - } - - // Update the cost for the next iteration - cost = newCost - - newAmount, err := math.Add64(amount, additionalFee) - if err != nil { - return nil, nil, err - } - amount = newAmount - - // Use the entire [balance] as an input, but if the required [amount] - // is less than the balance, update the [inputAmount] to spend the - // minimum amount to finish the transaction. - inputAmount := balance - if amount < balance { - inputAmount = amount - } - nonce, err := vm.GetCurrentNonce(addr) - if err != nil { - return nil, nil, err - } - inputs = append(inputs, EVMInput{ - Address: addr, - Amount: inputAmount, - AssetID: vm.ctx.AVAXAssetID, - Nonce: nonce, - }) - signers = append(signers, []*secp256k1.PrivateKey{key}) - amount -= inputAmount - } - - if amount > 0 { - return nil, nil, errInsufficientFunds - } - - return inputs, signers, nil -} - -// GetCurrentNonce returns the nonce associated with the address at the -// preferred block -func (vm *VM) GetCurrentNonce(address common.Address) (uint64, error) { - // Note: current state uses the state of the preferred block. - state, err := vm.blockChain.State() - if err != nil { - return 0, err + blk, ok := blkIntf.(*Block) + if !ok { + return nil, choices.Unknown, ids.ID{}, fmt.Errorf("block %s had unexpected type %T", blk.ID(), blkIntf) } - return state.GetNonce(address), nil + return blk.atomicTxs, blk.Status(), blk.Parent(), nil } // currentRules returns the chain rules for the current block. diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index e572d0107d..107f7ea70e 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -392,7 +392,7 @@ func TestCrossChainMessagestoVM(t *testing.T) { newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) require.NoError(err) err = vm.mempool.AddLocalTx(importTx) @@ -669,7 +669,7 @@ func TestImportMissingUTXOs(t *testing.T) { require.NoError(t, err) }() - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) require.NoError(t, err) err = vm.mempool.AddLocalTx(importTx) require.NoError(t, err) @@ -710,7 +710,7 @@ func TestIssueAtomicTxs(t *testing.T) { } }() - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -770,7 +770,7 @@ func TestIssueAtomicTxs(t *testing.T) { // t.Fatal("Expected logs to be non-nil") // } - exportTx, err := vm.newExportTx(vm.ctx.AVAXAssetID, importAmount-(2*params.AvalancheAtomicTxFee), vm.ctx.XChainID, testShortIDAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + exportTx, err := vm.NewExportTx(vm.ctx.AVAXAssetID, importAmount-(2*params.AvalancheAtomicTxFee), vm.ctx.XChainID, testShortIDAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -837,7 +837,7 @@ func TestBuildEthTxBlock(t *testing.T) { newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -999,14 +999,14 @@ func testConflictingImportTxs(t *testing.T, genesis string) { importTxs := make([]*Tx, 0, 3) conflictTxs := make([]*Tx, 0, 3) for i, key := range testKeys { - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[i], initialBaseFee, []*secp256k1.PrivateKey{key}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[i], initialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } importTxs = append(importTxs, importTx) conflictAddr := testEthAddrs[(i+1)%len(testEthAddrs)] - conflictTx, err := vm.newImportTx(vm.ctx.XChainID, conflictAddr, initialBaseFee, []*secp256k1.PrivateKey{key}) + conflictTx, err := vm.NewImportTx(vm.ctx.XChainID, conflictAddr, initialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } @@ -1173,11 +1173,11 @@ func TestReissueAtomicTxHigherGasPrice(t *testing.T) { if err != nil { t.Fatal(err) } - tx1, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo}) + tx1, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo}) if err != nil { t.Fatal(err) } - tx2, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), kc, []*avax.UTXO{utxo}) + tx2, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), kc, []*avax.UTXO{utxo}) if err != nil { t.Fatal(err) } @@ -1200,11 +1200,11 @@ func TestReissueAtomicTxHigherGasPrice(t *testing.T) { if err != nil { t.Fatal(err) } - tx1, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo1, utxo2}) + tx1, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo1, utxo2}) if err != nil { t.Fatal(err) } - tx2, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), kc, []*avax.UTXO{utxo1}) + tx2, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), kc, []*avax.UTXO{utxo1}) if err != nil { t.Fatal(err) } @@ -1228,17 +1228,17 @@ func TestReissueAtomicTxHigherGasPrice(t *testing.T) { t.Fatal(err) } - importTx1, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo1}) + importTx1, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo1}) if err != nil { t.Fatal(err) } - importTx2, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(3), initialBaseFee), kc, []*avax.UTXO{utxo2}) + importTx2, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(3), initialBaseFee), kc, []*avax.UTXO{utxo2}) if err != nil { t.Fatal(err) } - reissuanceTx1, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(2), initialBaseFee), kc, []*avax.UTXO{utxo1, utxo2}) + reissuanceTx1, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(2), initialBaseFee), kc, []*avax.UTXO{utxo1, utxo2}) if err != nil { t.Fatal(err) } @@ -1258,7 +1258,7 @@ func TestReissueAtomicTxHigherGasPrice(t *testing.T) { assert.True(t, vm.mempool.has(importTx2.ID())) assert.False(t, vm.mempool.has(reissuanceTx1.ID())) - reissuanceTx2, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(4), initialBaseFee), kc, []*avax.UTXO{utxo1, utxo2}) + reissuanceTx2, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(4), initialBaseFee), kc, []*avax.UTXO{utxo1, utxo2}) if err != nil { t.Fatal(err) } @@ -1337,7 +1337,7 @@ func TestSetPreferenceRace(t *testing.T) { newTxPoolHeadChan2 := make(chan core.NewTxPoolReorgEvent, 1) vm2.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan2) - importTx, err := vm1.newImportTx(vm1.ctx.XChainID, testEthAddrs[1], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm1.NewImportTx(vm1.ctx.XChainID, testEthAddrs[1], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -1581,12 +1581,12 @@ func TestConflictingTransitiveAncestryWithGap(t *testing.T) { newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) - importTx0A, err := vm.newImportTx(vm.ctx.XChainID, key.Address, initialBaseFee, []*secp256k1.PrivateKey{key0}) + importTx0A, err := vm.NewImportTx(vm.ctx.XChainID, key.Address, initialBaseFee, []*secp256k1.PrivateKey{key0}) if err != nil { t.Fatal(err) } // Create a conflicting transaction - importTx0B, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[2], initialBaseFee, []*secp256k1.PrivateKey{key0}) + importTx0B, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[2], initialBaseFee, []*secp256k1.PrivateKey{key0}) if err != nil { t.Fatal(err) } @@ -1644,7 +1644,7 @@ func TestConflictingTransitiveAncestryWithGap(t *testing.T) { t.Fatal(err) } - importTx1, err := vm.newImportTx(vm.ctx.XChainID, key.Address, initialBaseFee, []*secp256k1.PrivateKey{key1}) + importTx1, err := vm.NewImportTx(vm.ctx.XChainID, key.Address, initialBaseFee, []*secp256k1.PrivateKey{key1}) if err != nil { t.Fatalf("Failed to issue importTx1 due to: %s", err) } @@ -1723,7 +1723,7 @@ func TestBonusBlocksTxs(t *testing.T) { t.Fatal(err) } - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -1816,7 +1816,7 @@ func TestReorgProtection(t *testing.T) { key := testKeys[0].ToECDSA() address := testEthAddrs[0] - importTx, err := vm1.newImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm1.NewImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -1998,7 +1998,7 @@ func TestNonCanonicalAccept(t *testing.T) { key := testKeys[0].ToECDSA() address := testEthAddrs[0] - importTx, err := vm1.newImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm1.NewImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -2173,7 +2173,7 @@ func TestStickyPreference(t *testing.T) { key := testKeys[0].ToECDSA() address := testEthAddrs[0] - importTx, err := vm1.newImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm1.NewImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -2446,7 +2446,7 @@ func TestUncleBlock(t *testing.T) { key := testKeys[0].ToECDSA() address := testEthAddrs[0] - importTx, err := vm1.newImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm1.NewImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -2629,7 +2629,7 @@ func TestEmptyBlock(t *testing.T) { } }() - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -2710,7 +2710,7 @@ func TestAcceptReorg(t *testing.T) { key := testKeys[0].ToECDSA() address := testEthAddrs[0] - importTx, err := vm1.newImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm1.NewImportTx(vm1.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -2905,7 +2905,7 @@ func TestFutureBlock(t *testing.T) { } }() - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -2970,7 +2970,7 @@ func TestBuildApricotPhase1Block(t *testing.T) { key := testKeys[0].ToECDSA() address := testEthAddrs[0] - importTx, err := vm.newImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -3086,7 +3086,7 @@ func TestLastAcceptedBlockNumberAllow(t *testing.T) { } }() - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -3164,7 +3164,7 @@ func TestReissueAtomicTx(t *testing.T) { t.Fatal(err) } - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -3495,7 +3495,7 @@ func TestBuildApricotPhase4Block(t *testing.T) { t.Fatal(err) } - importTx, err := vm.newImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -3677,7 +3677,7 @@ func TestBuildApricotPhase5Block(t *testing.T) { t.Fatal(err) } - importTx, err := vm.newImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, address, initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -3884,7 +3884,7 @@ func TestAtomicTxBuildBlockDropsConflicts(t *testing.T) { // Create a conflict set for each pair of transactions conflictSets := make([]set.Set[ids.ID], len(testKeys)) for index, key := range testKeys { - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[index], initialBaseFee, []*secp256k1.PrivateKey{key}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[index], initialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } @@ -3892,7 +3892,7 @@ func TestAtomicTxBuildBlockDropsConflicts(t *testing.T) { t.Fatal(err) } conflictSets[index].Add(importTx.ID()) - conflictTx, err := vm.newImportTx(vm.ctx.XChainID, conflictKey.Address, initialBaseFee, []*secp256k1.PrivateKey{key}) + conflictTx, err := vm.NewImportTx(vm.ctx.XChainID, conflictKey.Address, initialBaseFee, []*secp256k1.PrivateKey{key}) if err != nil { t.Fatal(err) } @@ -3953,7 +3953,7 @@ func TestBuildBlockDoesNotExceedAtomicGasLimit(t *testing.T) { utxo, err := addUTXO(sharedMemory, vm.ctx, txID, uint32(i), vm.ctx.AVAXAssetID, importAmount, testShortIDAddrs[0]) assert.NoError(t, err) - importTx, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo}) + importTx, err := vm.NewImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, kc, []*avax.UTXO{utxo}) if err != nil { t.Fatal(err) } @@ -4012,7 +4012,7 @@ func TestExtraStateChangeAtomicGasLimitExceeded(t *testing.T) { // Double the initial base fee used when estimating the cost of this transaction to ensure that when it is // used in ApricotPhase5 it still pays a sufficient fee with the fixed fee per atomic transaction. - importTx, err := vm1.newImportTx(vm1.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm1.NewImportTx(vm1.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(common.Big2, initialBaseFee), []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } @@ -4071,7 +4071,7 @@ func TestSkipChainConfigCheckCompatible(t *testing.T) { // Since rewinding is permitted for last accepted height of 0, we must // accept one block to test the SkipUpgradeCheck functionality. - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) require.NoError(t, err) require.NoError(t, vm.mempool.AddLocalTx(importTx)) <-issuer @@ -4164,7 +4164,7 @@ func TestParentBeaconRootBlock(t *testing.T) { } }() - importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + importTx, err := vm.NewImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) if err != nil { t.Fatal(err) } From 7902c66431093c8e1c58f568df8d4ff3ac8097bc Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Tue, 25 Jun 2024 08:24:06 -0700 Subject: [PATCH 2/2] go mod tidy --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6b6e23b7e6..798acedf42 100644 --- a/go.mod +++ b/go.mod @@ -128,7 +128,7 @@ require ( golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/term v0.18.0 // indirect - golang.org/x/tools v0.16.0 // indirect + golang.org/x/tools v0.17.0 // indirect gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect diff --git a/go.sum b/go.sum index e5b6be1c0a..98f9e21910 100644 --- a/go.sum +++ b/go.sum @@ -864,8 +864,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=