diff --git a/vms/avm/block/executor/manager.go b/vms/avm/block/executor/manager.go index 48eea701bbd9..4ee6c37046ec 100644 --- a/vms/avm/block/executor/manager.go +++ b/vms/avm/block/executor/manager.go @@ -43,8 +43,8 @@ type Manager interface { // preferred state. This should *not* be used to verify transactions in a block. VerifyTx(tx *txs.Tx) error - // VerifyUniqueInputs verifies that the inputs are not duplicated in the - // provided blk or any of its ancestors pinned in memory. + // VerifyUniqueInputs returns nil iff no blocks in the inclusive + // ancestry of [blkID] consume an input in [inputs]. VerifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error } diff --git a/vms/platformvm/block/executor/backend.go b/vms/platformvm/block/executor/backend.go index fd0e75d0f664..4d915047f560 100644 --- a/vms/platformvm/block/executor/backend.go +++ b/vms/platformvm/block/executor/backend.go @@ -4,15 +4,19 @@ package executor import ( + "errors" "time" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/platformvm/block" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool" ) +var errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block") + // Shared fields used by visitors. type backend struct { mempool.Mempool @@ -95,3 +99,28 @@ func (b *backend) getTimestamp(blkID ids.ID) time.Time { // so we just return the chain time. return b.state.GetTimestamp() } + +// verifyUniqueInputs returns nil iff no blocks in the inclusive +// ancestry of [blkID] consume an input in [inputs]. +func (b *backend) verifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error { + if inputs.Len() == 0 { + return nil + } + + // Check for conflicts in ancestors. + for { + state, ok := b.blkIDToState[blkID] + if !ok { + // The parent state isn't pinned in memory. + // This means the parent must be accepted already. + return nil + } + + if state.inputs.Overlaps(inputs) { + return errConflictingParentTxs + } + + blk := state.statelessBlock + blkID = blk.Parent() + } +} diff --git a/vms/platformvm/block/executor/verifier.go b/vms/platformvm/block/executor/verifier.go index c4f25f992da8..abc2aa4e257c 100644 --- a/vms/platformvm/block/executor/verifier.go +++ b/vms/platformvm/block/executor/verifier.go @@ -9,7 +9,6 @@ import ( "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/platformvm/block" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/status" @@ -26,7 +25,6 @@ var ( errIncorrectBlockHeight = errors.New("incorrect block height") errChildBlockEarlierThanParent = errors.New("proposed timestamp before current chain time") errConflictingBatchTxs = errors.New("block contains conflicting transactions") - errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block") errOptionBlockTimestampNotMatchingParent = errors.New("option block proposed timestamp not matching parent block one") ) @@ -203,7 +201,7 @@ func (v *verifier) ApricotAtomicBlock(b *block.ApricotAtomicBlock) error { atomicExecutor.OnAccept.AddTx(b.Tx, status.Committed) - if err := v.verifyUniqueInputs(b, atomicExecutor.Inputs); err != nil { + if err := v.verifyUniqueInputs(b.Parent(), atomicExecutor.Inputs); err != nil { return err } @@ -441,7 +439,7 @@ func (v *verifier) standardBlock( } } - if err := v.verifyUniqueInputs(b, blkState.inputs); err != nil { + if err := v.verifyUniqueInputs(b.Parent(), blkState.inputs); err != nil { return err } @@ -461,28 +459,3 @@ func (v *verifier) standardBlock( v.Mempool.Remove(b.Transactions) return nil } - -// verifyUniqueInputs verifies that the inputs of the given block are not -// duplicated in any of the parent blocks pinned in memory. -func (v *verifier) verifyUniqueInputs(block block.Block, inputs set.Set[ids.ID]) error { - if inputs.Len() == 0 { - return nil - } - - // Check for conflicts in ancestors. - for { - parentID := block.Parent() - parentState, ok := v.blkIDToState[parentID] - if !ok { - // The parent state isn't pinned in memory. - // This means the parent must be accepted already. - return nil - } - - if parentState.inputs.Overlaps(inputs) { - return errConflictingParentTxs - } - - block = parentState.statelessBlock - } -}