github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/block/builder/builder.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package builder 5 6 import ( 7 "context" 8 "errors" 9 10 "github.com/MetalBlockchain/metalgo/ids" 11 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 12 "github.com/MetalBlockchain/metalgo/utils/set" 13 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 14 "github.com/MetalBlockchain/metalgo/utils/units" 15 "github.com/MetalBlockchain/metalgo/vms/avm/block" 16 "github.com/MetalBlockchain/metalgo/vms/avm/state" 17 "github.com/MetalBlockchain/metalgo/vms/avm/txs" 18 "github.com/MetalBlockchain/metalgo/vms/avm/txs/mempool" 19 20 blockexecutor "github.com/MetalBlockchain/metalgo/vms/avm/block/executor" 21 txexecutor "github.com/MetalBlockchain/metalgo/vms/avm/txs/executor" 22 ) 23 24 // targetBlockSize is the max block size we aim to produce 25 const targetBlockSize = 128 * units.KiB 26 27 var ( 28 _ Builder = (*builder)(nil) 29 30 ErrNoTransactions = errors.New("no transactions") 31 ) 32 33 type Builder interface { 34 // BuildBlock can be called to attempt to create a new block 35 BuildBlock(context.Context) (snowman.Block, error) 36 } 37 38 // builder implements a simple builder to convert txs into valid blocks 39 type builder struct { 40 backend *txexecutor.Backend 41 manager blockexecutor.Manager 42 clk *mockable.Clock 43 44 // Pool of all txs that may be able to be added 45 mempool mempool.Mempool 46 } 47 48 func New( 49 backend *txexecutor.Backend, 50 manager blockexecutor.Manager, 51 clk *mockable.Clock, 52 mempool mempool.Mempool, 53 ) Builder { 54 return &builder{ 55 backend: backend, 56 manager: manager, 57 clk: clk, 58 mempool: mempool, 59 } 60 } 61 62 // BuildBlock builds a block to be added to consensus. 63 func (b *builder) BuildBlock(context.Context) (snowman.Block, error) { 64 defer b.mempool.RequestBuildBlock() 65 66 ctx := b.backend.Ctx 67 ctx.Log.Debug("starting to attempt to build a block") 68 69 // Get the block to build on top of and retrieve the new block's context. 70 preferredID := b.manager.Preferred() 71 preferred, err := b.manager.GetStatelessBlock(preferredID) 72 if err != nil { 73 return nil, err 74 } 75 76 preferredHeight := preferred.Height() 77 preferredTimestamp := preferred.Timestamp() 78 79 nextHeight := preferredHeight + 1 80 nextTimestamp := b.clk.Time() // [timestamp] = max(now, parentTime) 81 if preferredTimestamp.After(nextTimestamp) { 82 nextTimestamp = preferredTimestamp 83 } 84 85 stateDiff, err := state.NewDiff(preferredID, b.manager) 86 if err != nil { 87 return nil, err 88 } 89 90 var ( 91 blockTxs []*txs.Tx 92 inputs set.Set[ids.ID] 93 remainingSize = targetBlockSize 94 ) 95 for { 96 tx, exists := b.mempool.Peek() 97 // Invariant: [mempool.MaxTxSize] < [targetBlockSize]. This guarantees 98 // that we will only stop building a block once there are no 99 // transactions in the mempool or the block is at least 100 // [targetBlockSize - mempool.MaxTxSize] bytes full. 101 if !exists || len(tx.Bytes()) > remainingSize { 102 break 103 } 104 b.mempool.Remove(tx) 105 106 // Invariant: [tx] has already been syntactically verified. 107 108 txDiff, err := state.NewDiffOn(stateDiff) 109 if err != nil { 110 return nil, err 111 } 112 113 err = tx.Unsigned.Visit(&txexecutor.SemanticVerifier{ 114 Backend: b.backend, 115 State: txDiff, 116 Tx: tx, 117 }) 118 if err != nil { 119 txID := tx.ID() 120 b.mempool.MarkDropped(txID, err) 121 continue 122 } 123 124 executor := &txexecutor.Executor{ 125 Codec: b.backend.Codec, 126 State: txDiff, 127 Tx: tx, 128 } 129 err = tx.Unsigned.Visit(executor) 130 if err != nil { 131 txID := tx.ID() 132 b.mempool.MarkDropped(txID, err) 133 continue 134 } 135 136 if inputs.Overlaps(executor.Inputs) { 137 txID := tx.ID() 138 b.mempool.MarkDropped(txID, blockexecutor.ErrConflictingBlockTxs) 139 continue 140 } 141 err = b.manager.VerifyUniqueInputs(preferredID, inputs) 142 if err != nil { 143 txID := tx.ID() 144 b.mempool.MarkDropped(txID, err) 145 continue 146 } 147 inputs.Union(executor.Inputs) 148 149 txDiff.AddTx(tx) 150 txDiff.Apply(stateDiff) 151 152 remainingSize -= len(tx.Bytes()) 153 blockTxs = append(blockTxs, tx) 154 } 155 156 if len(blockTxs) == 0 { 157 return nil, ErrNoTransactions 158 } 159 160 statelessBlk, err := block.NewStandardBlock( 161 preferredID, 162 nextHeight, 163 nextTimestamp, 164 blockTxs, 165 b.backend.Codec, 166 ) 167 if err != nil { 168 return nil, err 169 } 170 171 return b.manager.NewBlock(statelessBlk), nil 172 }