github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/block/executor/backend.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package executor 5 6 import ( 7 "errors" 8 "time" 9 10 "github.com/MetalBlockchain/metalgo/ids" 11 "github.com/MetalBlockchain/metalgo/snow" 12 "github.com/MetalBlockchain/metalgo/utils/set" 13 "github.com/MetalBlockchain/metalgo/vms/platformvm/block" 14 "github.com/MetalBlockchain/metalgo/vms/platformvm/state" 15 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/mempool" 16 ) 17 18 var errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block") 19 20 // Shared fields used by visitors. 21 type backend struct { 22 mempool.Mempool 23 // lastAccepted is the ID of the last block that had Accept() called on it. 24 lastAccepted ids.ID 25 26 // blkIDToState is a map from a block's ID to the state of the block. 27 // Blocks are put into this map when they are verified. 28 // Proposal blocks are removed from this map when they are rejected 29 // or when a child is accepted. 30 // All other blocks are removed when they are accepted/rejected. 31 // Note that Genesis block is a commit block so no need to update 32 // blkIDToState with it upon backend creation (Genesis is already accepted) 33 blkIDToState map[ids.ID]*blockState 34 state state.State 35 36 ctx *snow.Context 37 } 38 39 func (b *backend) GetState(blkID ids.ID) (state.Chain, bool) { 40 // If the block is in the map, it is either processing or a proposal block 41 // that was accepted without an accepted child. 42 if state, ok := b.blkIDToState[blkID]; ok { 43 if state.onAcceptState != nil { 44 return state.onAcceptState, true 45 } 46 return nil, false 47 } 48 49 // Note: If the last accepted block is a proposal block, we will have 50 // returned in the above if statement. 51 return b.state, blkID == b.state.GetLastAccepted() 52 } 53 54 func (b *backend) getOnAbortState(blkID ids.ID) (state.Diff, bool) { 55 state, ok := b.blkIDToState[blkID] 56 if !ok || state.onAbortState == nil { 57 return nil, false 58 } 59 return state.onAbortState, true 60 } 61 62 func (b *backend) getOnCommitState(blkID ids.ID) (state.Diff, bool) { 63 state, ok := b.blkIDToState[blkID] 64 if !ok || state.onCommitState == nil { 65 return nil, false 66 } 67 return state.onCommitState, true 68 } 69 70 func (b *backend) GetBlock(blkID ids.ID) (block.Block, error) { 71 // See if the block is in memory. 72 if blk, ok := b.blkIDToState[blkID]; ok { 73 return blk.statelessBlock, nil 74 } 75 76 // The block isn't in memory. Check the database. 77 return b.state.GetStatelessBlock(blkID) 78 } 79 80 func (b *backend) LastAccepted() ids.ID { 81 return b.lastAccepted 82 } 83 84 func (b *backend) free(blkID ids.ID) { 85 delete(b.blkIDToState, blkID) 86 } 87 88 func (b *backend) getTimestamp(blkID ids.ID) time.Time { 89 // Check if the block is processing. 90 // If the block is processing, then we are guaranteed to have populated its 91 // timestamp in its state. 92 if blkState, ok := b.blkIDToState[blkID]; ok { 93 return blkState.timestamp 94 } 95 96 // The block isn't processing. 97 // According to the snowman.Block interface, the last accepted 98 // block is the only accepted block that must return a correct timestamp, 99 // so we just return the chain time. 100 return b.state.GetTimestamp() 101 } 102 103 // verifyUniqueInputs returns nil iff no blocks in the inclusive 104 // ancestry of [blkID] consume an input in [inputs]. 105 func (b *backend) verifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error { 106 if inputs.Len() == 0 { 107 return nil 108 } 109 110 // Check for conflicts in ancestors. 111 for { 112 state, ok := b.blkIDToState[blkID] 113 if !ok { 114 // The parent state isn't pinned in memory. 115 // This means the parent must be accepted already. 116 return nil 117 } 118 119 if state.inputs.Overlaps(inputs) { 120 return errConflictingParentTxs 121 } 122 123 blk := state.statelessBlock 124 blkID = blk.Parent() 125 } 126 }