From f4ae592c20829221d1a82f7e30159ecff289f749 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Mon, 19 Jun 2023 11:10:53 -0400 Subject: [PATCH 1/2] Remove dagState and GetUTXOFromID --- vms/avm/dag_state.go | 84 ------------------- vms/avm/index_test.go | 8 +- vms/avm/states/diff.go | 4 - vms/avm/states/mock_states.go | 45 ---------- vms/avm/states/state.go | 7 -- vms/avm/txs/executor/semantic_verifier.go | 4 +- .../txs/executor/semantic_verifier_test.go | 44 +++++----- vms/avm/unique_tx.go | 2 +- vms/avm/vm.go | 7 +- 9 files changed, 30 insertions(+), 175 deletions(-) delete mode 100644 vms/avm/dag_state.go diff --git a/vms/avm/dag_state.go b/vms/avm/dag_state.go deleted file mode 100644 index 927a8559e33e..000000000000 --- a/vms/avm/dag_state.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package avm - -import ( - "go.uber.org/zap" - - "github.com/ava-labs/avalanchego/database" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/avm/states" - "github.com/ava-labs/avalanchego/vms/avm/txs" - "github.com/ava-labs/avalanchego/vms/components/avax" -) - -var _ states.Chain = (*dagState)(nil) - -type dagState struct { - states.Chain - vm *VM -} - -func (s *dagState) GetUTXOFromID(utxoID *avax.UTXOID) (*avax.UTXO, error) { - inputID := utxoID.InputID() - utxo, err := s.GetUTXO(inputID) - if err == nil { - // If the UTXO exists in the base state, then we can immediately return - // it. - return utxo, nil - } - if err != database.ErrNotFound { - s.vm.ctx.Log.Error("fetching UTXO returned unexpected error", - zap.Stringer("txID", utxoID.TxID), - zap.Uint32("index", utxoID.OutputIndex), - zap.Stringer("utxoID", inputID), - zap.Error(err), - ) - return nil, err - } - - // The UTXO doesn't exist in the base state, so we need to check if the UTXO - // could exist from a currently processing tx. - inputTxID, inputIndex := utxoID.InputSource() - parent := UniqueTx{ - vm: s.vm, - txID: inputTxID, - } - - // If the parent doesn't exist or is otherwise invalid, then this UTXO isn't - // available. - if err := parent.verifyWithoutCacheWrites(); err != nil { - return nil, database.ErrNotFound - } - - // If the parent was accepted, the UTXO should have been in the base state. - // This means the UTXO was already consumed by a conflicting tx. - if status := parent.Status(); status.Decided() { - return nil, database.ErrNotFound - } - - parentUTXOs := parent.UTXOs() - - // At this point we have only verified the TxID portion of [utxoID] as being - // potentially valid. It is still possible that a user specified an invalid - // index. So, we must bounds check the parents UTXOs. - // - // Invariant: len(parentUTXOs) <= MaxInt32. This guarantees that casting - // inputIndex to an int, even on 32-bit architectures, will not overflow. - if uint32(len(parentUTXOs)) <= inputIndex { - return nil, database.ErrNotFound - } - return parentUTXOs[int(inputIndex)], nil -} - -func (s *dagState) GetTx(txID ids.ID) (*txs.Tx, error) { - tx := &UniqueTx{ - vm: s.vm, - txID: txID, - } - if status := tx.Status(); !status.Fetched() { - return nil, database.ErrNotFound - } - return tx.Tx, nil -} diff --git a/vms/avm/index_test.go b/vms/avm/index_test.go index 4f2682c3d6d5..2f3e853fbe14 100644 --- a/vms/avm/index_test.go +++ b/vms/avm/index_test.go @@ -94,7 +94,7 @@ func TestIndexTransaction_Ordered(t *testing.T) { var inputUTXOs []*avax.UTXO for _, utxoID := range uniqueParsedTX.InputUTXOs() { - utxo, err := vm.dagState.GetUTXOFromID(utxoID) + utxo, err := vm.state.GetUTXO(utxoID.InputID()) require.NoError(err) inputUTXOs = append(inputUTXOs, utxo) @@ -170,7 +170,7 @@ func TestIndexTransaction_MultipleTransactions(t *testing.T) { var inputUTXOs []*avax.UTXO for _, utxoID := range uniqueParsedTX.InputUTXOs() { - utxo, err := vm.dagState.GetUTXOFromID(utxoID) + utxo, err := vm.state.GetUTXO(utxoID.InputID()) require.NoError(err) inputUTXOs = append(inputUTXOs, utxo) @@ -235,7 +235,7 @@ func TestIndexTransaction_MultipleAddresses(t *testing.T) { var inputUTXOs []*avax.UTXO //nolint:prealloc for _, utxoID := range tx.Unsigned.InputUTXOs() { - utxo, err := vm.dagState.GetUTXOFromID(utxoID) + utxo, err := vm.state.GetUTXO(utxoID.InputID()) require.NoError(err) inputUTXOs = append(inputUTXOs, utxo) @@ -303,7 +303,7 @@ func TestIndexTransaction_UnorderedWrites(t *testing.T) { var inputUTXOs []*avax.UTXO for _, utxoID := range uniqueParsedTX.InputUTXOs() { - utxo, err := vm.dagState.GetUTXOFromID(utxoID) + utxo, err := vm.state.GetUTXO(utxoID.InputID()) require.NoError(err) inputUTXOs = append(inputUTXOs, utxo) diff --git a/vms/avm/states/diff.go b/vms/avm/states/diff.go index 5b77c0a7b4ca..e60ffd1bbc4f 100644 --- a/vms/avm/states/diff.go +++ b/vms/avm/states/diff.go @@ -76,10 +76,6 @@ func (d *diff) GetUTXO(utxoID ids.ID) (*avax.UTXO, error) { return parentState.GetUTXO(utxoID) } -func (d *diff) GetUTXOFromID(utxoID *avax.UTXOID) (*avax.UTXO, error) { - return d.GetUTXO(utxoID.InputID()) -} - func (d *diff) AddUTXO(utxo *avax.UTXO) { d.modifiedUTXOs[utxo.InputID()] = utxo } diff --git a/vms/avm/states/mock_states.go b/vms/avm/states/mock_states.go index a5b5f6ea70b0..d15cdbdf911e 100644 --- a/vms/avm/states/mock_states.go +++ b/vms/avm/states/mock_states.go @@ -179,21 +179,6 @@ func (mr *MockChainMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockChain)(nil).GetUTXO), arg0) } -// GetUTXOFromID mocks base method. -func (m *MockChain) GetUTXOFromID(arg0 *avax.UTXOID) (*avax.UTXO, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUTXOFromID", arg0) - ret0, _ := ret[0].(*avax.UTXO) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetUTXOFromID indicates an expected call of GetUTXOFromID. -func (mr *MockChainMockRecorder) GetUTXOFromID(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXOFromID", reflect.TypeOf((*MockChain)(nil).GetUTXOFromID), arg0) -} - // SetLastAccepted mocks base method. func (m *MockChain) SetLastAccepted(arg0 ids.ID) { m.ctrl.T.Helper() @@ -459,21 +444,6 @@ func (mr *MockStateMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockState)(nil).GetUTXO), arg0) } -// GetUTXOFromID mocks base method. -func (m *MockState) GetUTXOFromID(arg0 *avax.UTXOID) (*avax.UTXO, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUTXOFromID", arg0) - ret0, _ := ret[0].(*avax.UTXO) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetUTXOFromID indicates an expected call of GetUTXOFromID. -func (mr *MockStateMockRecorder) GetUTXOFromID(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXOFromID", reflect.TypeOf((*MockState)(nil).GetUTXOFromID), arg0) -} - // InitializeChainState mocks base method. func (m *MockState) InitializeChainState(arg0 ids.ID, arg1 time.Time) error { m.ctrl.T.Helper() @@ -727,21 +697,6 @@ func (mr *MockDiffMockRecorder) GetUTXO(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXO", reflect.TypeOf((*MockDiff)(nil).GetUTXO), arg0) } -// GetUTXOFromID mocks base method. -func (m *MockDiff) GetUTXOFromID(arg0 *avax.UTXOID) (*avax.UTXO, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUTXOFromID", arg0) - ret0, _ := ret[0].(*avax.UTXO) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetUTXOFromID indicates an expected call of GetUTXOFromID. -func (mr *MockDiffMockRecorder) GetUTXOFromID(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUTXOFromID", reflect.TypeOf((*MockDiff)(nil).GetUTXOFromID), arg0) -} - // SetLastAccepted mocks base method. func (m *MockDiff) SetLastAccepted(arg0 ids.ID) { m.ctrl.T.Helper() diff --git a/vms/avm/states/state.go b/vms/avm/states/state.go index f6394e73ea35..409897c061cf 100644 --- a/vms/avm/states/state.go +++ b/vms/avm/states/state.go @@ -47,9 +47,6 @@ var ( type ReadOnlyChain interface { avax.UTXOGetter - // TODO: Remove GetUTXOFromID after the DAG linearization - GetUTXOFromID(utxoID *avax.UTXOID) (*avax.UTXO, error) - GetTx(txID ids.ID) (*txs.Tx, error) GetBlockID(height uint64) (ids.ID, error) GetBlock(blkID ids.ID) (blocks.Block, error) @@ -238,10 +235,6 @@ func (s *state) GetUTXO(utxoID ids.ID) (*avax.UTXO, error) { return s.utxoState.GetUTXO(utxoID) } -func (s *state) GetUTXOFromID(utxoID *avax.UTXOID) (*avax.UTXO, error) { - return s.GetUTXO(utxoID.InputID()) -} - func (s *state) UTXOIDs(addr []byte, start ids.ID, limit int) ([]ids.ID, error) { return s.utxoState.UTXOIDs(addr, start, limit) } diff --git a/vms/avm/txs/executor/semantic_verifier.go b/vms/avm/txs/executor/semantic_verifier.go index fde1e631a4ee..7e81ff9cacf1 100644 --- a/vms/avm/txs/executor/semantic_verifier.go +++ b/vms/avm/txs/executor/semantic_verifier.go @@ -151,7 +151,7 @@ func (v *SemanticVerifier) verifyTransfer( in *avax.TransferableInput, cred verify.Verifiable, ) error { - utxo, err := v.State.GetUTXOFromID(&in.UTXOID) + utxo, err := v.State.GetUTXO(in.UTXOID.InputID()) if err != nil { return err } @@ -194,7 +194,7 @@ func (v *SemanticVerifier) verifyOperation( utxos = make([]interface{}, numUTXOs) ) for i, utxoID := range op.UTXOIDs { - utxo, err := v.State.GetUTXOFromID(utxoID) + utxo, err := v.State.GetUTXO(utxoID.InputID()) if err != nil { return err } diff --git a/vms/avm/txs/executor/semantic_verifier_test.go b/vms/avm/txs/executor/semantic_verifier_test.go index 63c2fa5cee54..85690f156576 100644 --- a/vms/avm/txs/executor/semantic_verifier_test.go +++ b/vms/avm/txs/executor/semantic_verifier_test.go @@ -127,7 +127,7 @@ func TestSemanticVerifierBaseTx(t *testing.T) { stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) return state @@ -154,7 +154,7 @@ func TestSemanticVerifierBaseTx(t *testing.T) { utxo := utxo utxo.Asset.ID = ids.GenerateTestID() - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) return state }, @@ -184,7 +184,7 @@ func TestSemanticVerifierBaseTx(t *testing.T) { Unsigned: &unsignedCreateAssetTx, } - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) return state @@ -208,7 +208,7 @@ func TestSemanticVerifierBaseTx(t *testing.T) { stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) return state @@ -232,7 +232,7 @@ func TestSemanticVerifierBaseTx(t *testing.T) { stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(nil, database.ErrNotFound) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(nil, database.ErrNotFound) return state }, @@ -261,7 +261,7 @@ func TestSemanticVerifierBaseTx(t *testing.T) { utxo := utxo utxo.Out = &output - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) return state @@ -321,7 +321,7 @@ func TestSemanticVerifierBaseTx(t *testing.T) { stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(nil, database.ErrNotFound) return state @@ -349,7 +349,7 @@ func TestSemanticVerifierBaseTx(t *testing.T) { Unsigned: &baseTx, } - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&tx, nil) return state @@ -496,7 +496,7 @@ func TestSemanticVerifierExportTx(t *testing.T) { stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) return state @@ -523,7 +523,7 @@ func TestSemanticVerifierExportTx(t *testing.T) { utxo := utxo utxo.Asset.ID = ids.GenerateTestID() - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) return state }, @@ -553,7 +553,7 @@ func TestSemanticVerifierExportTx(t *testing.T) { Unsigned: &unsignedCreateAssetTx, } - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) return state @@ -577,7 +577,7 @@ func TestSemanticVerifierExportTx(t *testing.T) { stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) return state @@ -601,7 +601,7 @@ func TestSemanticVerifierExportTx(t *testing.T) { stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(nil, database.ErrNotFound) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(nil, database.ErrNotFound) return state }, @@ -630,7 +630,7 @@ func TestSemanticVerifierExportTx(t *testing.T) { utxo := utxo utxo.Out = &output - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) return state @@ -690,7 +690,7 @@ func TestSemanticVerifierExportTx(t *testing.T) { stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(nil, database.ErrNotFound) return state @@ -718,7 +718,7 @@ func TestSemanticVerifierExportTx(t *testing.T) { Unsigned: &baseTx, } - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&tx, nil) return state @@ -858,7 +858,7 @@ func TestSemanticVerifierExportTxDifferentSubnet(t *testing.T) { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil) + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil) state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil) tx := &txs.Tx{ @@ -1016,7 +1016,7 @@ func TestSemanticVerifierImportTx(t *testing.T) { name: "valid", stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil).AnyTimes() + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil).AnyTimes() state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil).AnyTimes() return state }, @@ -1034,7 +1034,7 @@ func TestSemanticVerifierImportTx(t *testing.T) { createAssetTx := txs.Tx{ Unsigned: &unsignedCreateAssetTx, } - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil).AnyTimes() + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil).AnyTimes() state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil).AnyTimes() return state }, @@ -1047,7 +1047,7 @@ func TestSemanticVerifierImportTx(t *testing.T) { name: "invalid signature", stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil).AnyTimes() + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil).AnyTimes() state.EXPECT().GetTx(asset.ID).Return(&createAssetTx, nil).AnyTimes() return state }, @@ -1098,7 +1098,7 @@ func TestSemanticVerifierImportTx(t *testing.T) { name: "unknown asset", stateFunc: func(ctrl *gomock.Controller) states.Chain { state := states.NewMockChain(ctrl) - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil).AnyTimes() + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil).AnyTimes() state.EXPECT().GetTx(asset.ID).Return(nil, database.ErrNotFound) return state }, @@ -1114,7 +1114,7 @@ func TestSemanticVerifierImportTx(t *testing.T) { tx := txs.Tx{ Unsigned: &baseTx, } - state.EXPECT().GetUTXOFromID(&utxoID).Return(&utxo, nil).AnyTimes() + state.EXPECT().GetUTXO(utxoID.InputID()).Return(&utxo, nil).AnyTimes() state.EXPECT().GetTx(asset.ID).Return(&tx, nil) return state }, diff --git a/vms/avm/unique_tx.go b/vms/avm/unique_tx.go index 50aeaa516230..32de746d7c21 100644 --- a/vms/avm/unique_tx.go +++ b/vms/avm/unique_tx.go @@ -324,7 +324,7 @@ func (tx *UniqueTx) SemanticVerify() error { return tx.Unsigned.Visit(&executor.SemanticVerifier{ Backend: tx.vm.txBackend, - State: tx.vm.dagState, + State: tx.vm.state, Tx: tx.Tx, }) } diff --git a/vms/avm/vm.go b/vms/avm/vm.go index 4eb1cb541f2a..215681b485ca 100644 --- a/vms/avm/vm.go +++ b/vms/avm/vm.go @@ -129,7 +129,6 @@ type VM struct { uniqueTxs cache.Deduplicator[ids.ID, *UniqueTx] txBackend *txexecutor.Backend - dagState *dagState // These values are only initialized after the chain has been linearized. blockbuilder.Builder @@ -288,10 +287,6 @@ func (vm *VM) Initialize( FeeAssetID: vm.feeAssetID, Bootstrapped: false, } - vm.dagState = &dagState{ - Chain: vm.state, - vm: vm, - } return vm.state.Commit() } @@ -754,7 +749,7 @@ func (vm *VM) onAccept(tx *txs.Tx) error { continue } - utxo, err := vm.state.GetUTXOFromID(utxoID) + utxo, err := vm.state.GetUTXO(utxoID.InputID()) if err == database.ErrNotFound { vm.ctx.Log.Debug("dropping utxo from index", zap.Stringer("txID", txID), From d924932e846ddb0fb00e9538cdc32d0668a2d39e Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Mon, 19 Jun 2023 13:14:14 -0400 Subject: [PATCH 2/2] Fix unit tests --- vms/avm/service_test.go | 199 ++++++++-------------------------- vms/avm/vm_regression_test.go | 20 ++-- vms/avm/vm_test.go | 94 +++------------- 3 files changed, 71 insertions(+), 242 deletions(-) diff --git a/vms/avm/service_test.go b/vms/avm/service_test.go index 960d2c638223..081102bfa1d9 100644 --- a/vms/avm/service_test.go +++ b/vms/avm/service_test.go @@ -188,6 +188,26 @@ func verifyTxFeeDeducted(t *testing.T, s *Service, fromAddrs []ids.ShortID, numT return nil } +// issueAndAccept expects the context lock to be held +func issueAndAccept( + require *require.Assertions, + vm *VM, + issuer <-chan common.Message, + tx *txs.Tx, +) { + txID, err := vm.IssueTx(tx.Bytes()) + require.NoError(err) + require.Equal(tx.ID(), txID) + + vm.ctx.Lock.Unlock() + require.Equal(common.PendingTxs, <-issuer) + vm.ctx.Lock.Lock() + + txs := vm.PendingTxs(context.Background()) + require.Len(txs, 1) + require.NoError(txs[0].Accept(context.Background())) +} + func TestServiceIssueTx(t *testing.T) { require := require.New(t) @@ -235,17 +255,7 @@ func TestServiceGetTxStatus(t *testing.T) { require.NoError(s.GetTxStatus(nil, statusArgs, statusReply)) require.Equal(choices.Unknown, statusReply.Status) - _, err = vm.IssueTx(newTx.Bytes()) - require.NoError(err) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 1) - require.NoError(txs[0].Accept(context.Background())) + issueAndAccept(require, vm, issuer, newTx) statusReply = &GetTxStatusReply{} require.NoError(s.GetTxStatus(nil, statusArgs, statusReply)) @@ -650,23 +660,11 @@ func TestServiceGetTxJSON_BaseTx(t *testing.T) { }() newTx := newAvaxBaseTxWithOutputs(t, genesisBytes, vm) - - txID, err := vm.IssueTx(newTx.Bytes()) - require.NoError(err) - require.Equal(newTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 1) - require.NoError(txs[0].Accept(context.Background())) + issueAndAccept(require, vm, issuer, newTx) reply := api.GetTxReply{} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: newTx.ID(), Encoding: formatting.JSON, }, &reply)) @@ -691,23 +689,11 @@ func TestServiceGetTxJSON_ExportTx(t *testing.T) { }() newTx := newAvaxExportTxWithOutputs(t, genesisBytes, vm) - - txID, err := vm.IssueTx(newTx.Bytes()) - require.NoError(err) - require.Equal(newTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - - ctx.Lock.Lock() - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 1) - require.NoError(txs[0].Accept(context.Background())) + issueAndAccept(require, vm, issuer, newTx) reply := api.GetTxReply{} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: newTx.ID(), Encoding: formatting.JSON, }, &reply)) @@ -767,23 +753,12 @@ func TestServiceGetTxJSON_CreateAssetTx(t *testing.T) { require.NoError(vm.SetState(context.Background(), snow.NormalOp)) createAssetTx := newAvaxCreateAssetTxWithOutputs(t, vm) - txID, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) - require.Equal(createAssetTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 1) - require.NoError(txs[0].Accept(context.Background())) + issueAndAccept(require, vm, issuer, createAssetTx) reply := api.GetTxReply{} s := &Service{vm: vm} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: createAssetTx.ID(), Encoding: formatting.JSON, }, &reply)) @@ -845,30 +820,16 @@ func TestServiceGetTxJSON_OperationTxWithNftxMintOp(t *testing.T) { key := keys[0] createAssetTx := newAvaxCreateAssetTxWithOutputs(t, vm) - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) mintNFTTx := buildOperationTxWithOp(buildNFTxMintOp(createAssetTx, key, 2, 1)) require.NoError(mintNFTTx.SignNFTFx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{key}})) - - txID, err := vm.IssueTx(mintNFTTx.Bytes()) - require.NoError(err) - require.Equal(mintNFTTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 2) - require.NoError(txs[0].Accept(context.Background())) - require.NoError(txs[1].Accept(context.Background())) + issueAndAccept(require, vm, issuer, mintNFTTx) reply := api.GetTxReply{} s := &Service{vm: vm} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: mintNFTTx.ID(), Encoding: formatting.JSON, }, &reply)) @@ -934,33 +895,19 @@ func TestServiceGetTxJSON_OperationTxWithMultipleNftxMintOp(t *testing.T) { key := keys[0] createAssetTx := newAvaxCreateAssetTxWithOutputs(t, vm) - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) mintOp1 := buildNFTxMintOp(createAssetTx, key, 2, 1) mintOp2 := buildNFTxMintOp(createAssetTx, key, 3, 2) mintNFTTx := buildOperationTxWithOp(mintOp1, mintOp2) require.NoError(mintNFTTx.SignNFTFx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{key}, {key}})) - - txID, err := vm.IssueTx(mintNFTTx.Bytes()) - require.NoError(err) - require.Equal(mintNFTTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 2) - require.NoError(txs[0].Accept(context.Background())) - require.NoError(txs[1].Accept(context.Background())) + issueAndAccept(require, vm, issuer, mintNFTTx) reply := api.GetTxReply{} s := &Service{vm: vm} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: mintNFTTx.ID(), Encoding: formatting.JSON, }, &reply)) @@ -1025,30 +972,16 @@ func TestServiceGetTxJSON_OperationTxWithSecpMintOp(t *testing.T) { key := keys[0] createAssetTx := newAvaxCreateAssetTxWithOutputs(t, vm) - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) mintSecpOpTx := buildOperationTxWithOp(buildSecpMintOp(createAssetTx, key, 0)) require.NoError(mintSecpOpTx.SignSECP256K1Fx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{key}})) - - txID, err := vm.IssueTx(mintSecpOpTx.Bytes()) - require.NoError(err) - require.Equal(mintSecpOpTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 2) - require.NoError(txs[0].Accept(context.Background())) - require.NoError(txs[1].Accept(context.Background())) + issueAndAccept(require, vm, issuer, mintSecpOpTx) reply := api.GetTxReply{} s := &Service{vm: vm} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: mintSecpOpTx.ID(), Encoding: formatting.JSON, }, &reply)) @@ -1116,33 +1049,19 @@ func TestServiceGetTxJSON_OperationTxWithMultipleSecpMintOp(t *testing.T) { key := keys[0] createAssetTx := newAvaxCreateAssetTxWithOutputs(t, vm) - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) op1 := buildSecpMintOp(createAssetTx, key, 0) op2 := buildSecpMintOp(createAssetTx, key, 1) mintSecpOpTx := buildOperationTxWithOp(op1, op2) require.NoError(mintSecpOpTx.SignSECP256K1Fx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{key}, {key}})) - - txID, err := vm.IssueTx(mintSecpOpTx.Bytes()) - require.NoError(err) - require.Equal(mintSecpOpTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 2) - require.NoError(txs[0].Accept(context.Background())) - require.NoError(txs[1].Accept(context.Background())) + issueAndAccept(require, vm, issuer, mintSecpOpTx) reply := api.GetTxReply{} s := &Service{vm: vm} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: mintSecpOpTx.ID(), Encoding: formatting.JSON, }, &reply)) @@ -1208,30 +1127,16 @@ func TestServiceGetTxJSON_OperationTxWithPropertyFxMintOp(t *testing.T) { key := keys[0] createAssetTx := newAvaxCreateAssetTxWithOutputs(t, vm) - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) mintPropertyFxOpTx := buildOperationTxWithOp(buildPropertyFxMintOp(createAssetTx, key, 4)) require.NoError(mintPropertyFxOpTx.SignPropertyFx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{key}})) - - txID, err := vm.IssueTx(mintPropertyFxOpTx.Bytes()) - require.NoError(err) - require.Equal(mintPropertyFxOpTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 2) - require.NoError(txs[0].Accept(context.Background())) - require.NoError(txs[1].Accept(context.Background())) + issueAndAccept(require, vm, issuer, mintPropertyFxOpTx) reply := api.GetTxReply{} s := &Service{vm: vm} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: mintPropertyFxOpTx.ID(), Encoding: formatting.JSON, }, &reply)) @@ -1298,33 +1203,19 @@ func TestServiceGetTxJSON_OperationTxWithPropertyFxMintOpMultiple(t *testing.T) key := keys[0] createAssetTx := newAvaxCreateAssetTxWithOutputs(t, vm) - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) op1 := buildPropertyFxMintOp(createAssetTx, key, 4) op2 := buildPropertyFxMintOp(createAssetTx, key, 5) mintPropertyFxOpTx := buildOperationTxWithOp(op1, op2) require.NoError(mintPropertyFxOpTx.SignPropertyFx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{key}, {key}})) - - txID, err := vm.IssueTx(mintPropertyFxOpTx.Bytes()) - require.NoError(err) - require.Equal(mintPropertyFxOpTx.ID(), txID) - ctx.Lock.Unlock() - - msg := <-issuer - require.Equal(common.PendingTxs, msg) - ctx.Lock.Lock() - - txs := vm.PendingTxs(context.Background()) - require.Len(txs, 2) - require.NoError(txs[0].Accept(context.Background())) - require.NoError(txs[1].Accept(context.Background())) + issueAndAccept(require, vm, issuer, mintPropertyFxOpTx) reply := api.GetTxReply{} s := &Service{vm: vm} require.NoError(s.GetTx(nil, &api.GetTxArgs{ - TxID: txID, + TxID: mintPropertyFxOpTx.ID(), Encoding: formatting.JSON, }, &reply)) diff --git a/vms/avm/vm_regression_test.go b/vms/avm/vm_regression_test.go index 1e9f0f6dd67f..10af0c4508aa 100644 --- a/vms/avm/vm_regression_test.go +++ b/vms/avm/vm_regression_test.go @@ -9,7 +9,9 @@ import ( "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/manager" + "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/engine/common" @@ -33,12 +35,16 @@ func TestVerifyFxUsage(t *testing.T) { ctx.Lock.Unlock() }() + baseDBManager := manager.NewMemDB(version.Semantic1_0_0) + m := atomic.NewMemory(prefixdb.New([]byte{0}, baseDBManager.Current().Database)) + ctx.SharedMemory = m.NewSharedMemory(ctx.ChainID) + genesisBytes := BuildGenesisTest(t) issuer := make(chan common.Message, 1) require.NoError(vm.Initialize( context.Background(), ctx, - manager.NewMemDB(version.Semantic1_0_0), + baseDBManager, genesisBytes, nil, nil, @@ -96,9 +102,7 @@ func TestVerifyFxUsage(t *testing.T) { }, }} require.NoError(vm.parser.InitializeTx(createAssetTx)) - - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) mintNFTTx := &txs.Tx{Unsigned: &txs.OperationTx{ BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ @@ -122,9 +126,7 @@ func TestVerifyFxUsage(t *testing.T) { }}, }} require.NoError(mintNFTTx.SignNFTFx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{keys[0]}})) - - _, err = vm.IssueTx(mintNFTTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, mintNFTTx) spendTx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ NetworkID: constants.UnitTestID, @@ -144,7 +146,5 @@ func TestVerifyFxUsage(t *testing.T) { }}, }}} require.NoError(spendTx.SignSECP256K1Fx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{keys[0]}})) - - _, err = vm.IssueTx(spendTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, spendTx) } diff --git a/vms/avm/vm_test.go b/vms/avm/vm_test.go index 59f4b228c36f..9ceabee50ed7 100644 --- a/vms/avm/vm_test.go +++ b/vms/avm/vm_test.go @@ -578,12 +578,16 @@ func TestIssueNFT(t *testing.T) { ctx.Lock.Unlock() }() + baseDBManager := manager.NewMemDB(version.Semantic1_0_0) + m := atomic.NewMemory(prefixdb.New([]byte{0}, baseDBManager.Current().Database)) + ctx.SharedMemory = m.NewSharedMemory(ctx.ChainID) + genesisBytes := BuildGenesisTest(t) issuer := make(chan common.Message, 1) require.NoError(vm.Initialize( context.Background(), ctx, - manager.NewMemDB(version.Semantic1_0_0), + baseDBManager, genesisBytes, nil, nil, @@ -603,7 +607,6 @@ func TestIssueNFT(t *testing.T) { vm.batchTimeout = 0 require.NoError(vm.SetState(context.Background(), snow.Bootstrapping)) - require.NoError(vm.SetState(context.Background(), snow.NormalOp)) createAssetTx := &txs.Tx{Unsigned: &txs.CreateAssetTx{ @@ -635,9 +638,7 @@ func TestIssueNFT(t *testing.T) { }}, }} require.NoError(vm.parser.InitializeTx(createAssetTx)) - - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) mintNFTTx := &txs.Tx{Unsigned: &txs.OperationTx{ BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ @@ -661,9 +662,7 @@ func TestIssueNFT(t *testing.T) { }}, }} require.NoError(mintNFTTx.SignNFTFx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{keys[0]}})) - - _, err = vm.IssueTx(mintNFTTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, mintNFTTx) transferNFTTx := &txs.Tx{ Unsigned: &txs.OperationTx{ @@ -692,9 +691,7 @@ func TestIssueNFT(t *testing.T) { }, } require.NoError(vm.parser.InitializeTx(transferNFTTx)) - - _, err = vm.IssueTx(transferNFTTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, transferNFTTx) } // Test issuing a transaction that creates an Property family @@ -708,12 +705,16 @@ func TestIssueProperty(t *testing.T) { ctx.Lock.Unlock() }() + baseDBManager := manager.NewMemDB(version.Semantic1_0_0) + m := atomic.NewMemory(prefixdb.New([]byte{0}, baseDBManager.Current().Database)) + ctx.SharedMemory = m.NewSharedMemory(ctx.ChainID) + genesisBytes := BuildGenesisTest(t) issuer := make(chan common.Message, 1) require.NoError(vm.Initialize( context.Background(), ctx, - manager.NewMemDB(version.Semantic1_0_0), + baseDBManager, genesisBytes, nil, nil, @@ -760,9 +761,7 @@ func TestIssueProperty(t *testing.T) { }}, }} require.NoError(vm.parser.InitializeTx(createAssetTx)) - - _, err := vm.IssueTx(createAssetTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, createAssetTx) mintPropertyTx := &txs.Tx{Unsigned: &txs.OperationTx{ BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ @@ -794,9 +793,7 @@ func TestIssueProperty(t *testing.T) { require.NoError(mintPropertyTx.SignPropertyFx(codec, [][]*secp256k1.PrivateKey{ {keys[0]}, })) - - _, err = vm.IssueTx(mintPropertyTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, mintPropertyTx) burnPropertyTx := &txs.Tx{Unsigned: &txs.OperationTx{ BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ @@ -816,9 +813,7 @@ func TestIssueProperty(t *testing.T) { require.NoError(burnPropertyTx.SignPropertyFx(codec, [][]*secp256k1.PrivateKey{ {}, })) - - _, err = vm.IssueTx(burnPropertyTx.Bytes()) - require.NoError(err) + issueAndAccept(require, vm, issuer, burnPropertyTx) } func setupTxFeeAssets(t *testing.T) ([]byte, chan common.Message, *VM, *atomic.Memory) { @@ -1121,63 +1116,6 @@ func TestTxVerifyAfterGet(t *testing.T) { require.ErrorIs(err, database.ErrNotFound) } -func TestTxVerifyAfterVerifyAncestorTx(t *testing.T) { - require := require.New(t) - - _, vm, ctx, issueTxs := setupIssueTx(t) - defer func() { - require.NoError(vm.Shutdown(context.Background())) - ctx.Lock.Unlock() - }() - avaxTx := issueTxs[0] - firstTx := issueTxs[1] - secondTx := issueTxs[2] - key := keys[0] - firstTxDescendant := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ - NetworkID: constants.UnitTestID, - BlockchainID: chainID, - Ins: []*avax.TransferableInput{{ - UTXOID: avax.UTXOID{ - TxID: firstTx.ID(), - OutputIndex: 0, - }, - Asset: avax.Asset{ID: avaxTx.ID()}, - In: &secp256k1fx.TransferInput{ - Amt: startBalance - vm.TxFee, - Input: secp256k1fx.Input{ - SigIndices: []uint32{ - 0, - }, - }, - }, - }}, - Outs: []*avax.TransferableOutput{{ - Asset: avax.Asset{ID: avaxTx.ID()}, - Out: &secp256k1fx.TransferOutput{ - Amt: startBalance - 2*vm.TxFee, - OutputOwners: secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{key.PublicKey().Address()}, - }, - }, - }}, - }}} - require.NoError(firstTxDescendant.SignSECP256K1Fx(vm.parser.Codec(), [][]*secp256k1.PrivateKey{{key}})) - - parsedSecondTx, err := vm.ParseTx(context.Background(), secondTx.Bytes()) - require.NoError(err) - require.NoError(parsedSecondTx.Verify(context.Background())) - _, err = vm.IssueTx(firstTx.Bytes()) - require.NoError(err) - _, err = vm.IssueTx(firstTxDescendant.Bytes()) - require.NoError(err) - parsedFirstTx, err := vm.GetTx(context.Background(), firstTx.ID()) - require.NoError(err) - require.NoError(parsedSecondTx.Accept(context.Background())) - err = parsedFirstTx.Verify(context.Background()) - require.ErrorIs(err, database.ErrNotFound) -} - func TestImportTxSerialization(t *testing.T) { require := require.New(t)