github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/batched_vm.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package proposervm 5 6 import ( 7 "context" 8 "time" 9 10 "github.com/MetalBlockchain/metalgo/database" 11 "github.com/MetalBlockchain/metalgo/ids" 12 "github.com/MetalBlockchain/metalgo/snow/choices" 13 "github.com/MetalBlockchain/metalgo/snow/consensus/snowman" 14 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 15 "github.com/MetalBlockchain/metalgo/utils/wrappers" 16 17 statelessblock "github.com/MetalBlockchain/metalgo/vms/proposervm/block" 18 ) 19 20 var _ block.BatchedChainVM = (*VM)(nil) 21 22 func (vm *VM) GetAncestors( 23 ctx context.Context, 24 blkID ids.ID, 25 maxBlocksNum int, 26 maxBlocksSize int, 27 maxBlocksRetrievalTime time.Duration, 28 ) ([][]byte, error) { 29 if vm.batchedVM == nil { 30 return nil, block.ErrRemoteVMNotImplemented 31 } 32 33 res := make([][]byte, 0, maxBlocksNum) 34 currentByteLength := 0 35 startTime := vm.Clock.Time() 36 37 // hereinafter loop over proposerVM cache and DB, possibly till snowman++ 38 // fork is hit 39 for { 40 blk, err := vm.getStatelessBlk(blkID) 41 if err != nil { 42 // maybe we have hit the proposerVM fork here? 43 break 44 } 45 46 blkBytes := blk.Bytes() 47 48 // Ensure response size isn't too large. Include wrappers.IntLen because 49 // the size of the message is included with each container, and the size 50 // is repr. by an int. 51 currentByteLength += wrappers.IntLen + len(blkBytes) 52 elapsedTime := vm.Clock.Time().Sub(startTime) 53 if len(res) > 0 && (currentByteLength >= maxBlocksSize || maxBlocksRetrievalTime <= elapsedTime) { 54 return res, nil // reached maximum size or ran out of time 55 } 56 57 res = append(res, blkBytes) 58 blkID = blk.ParentID() 59 if len(res) >= maxBlocksNum { 60 return res, nil 61 } 62 } 63 64 // snowman++ fork may have been hit. 65 preMaxBlocksNum := maxBlocksNum - len(res) 66 preMaxBlocksSize := maxBlocksSize - currentByteLength 67 preMaxBlocksRetrivalTime := maxBlocksRetrievalTime - time.Since(startTime) 68 innerBytes, err := vm.batchedVM.GetAncestors( 69 ctx, 70 blkID, 71 preMaxBlocksNum, 72 preMaxBlocksSize, 73 preMaxBlocksRetrivalTime, 74 ) 75 if err != nil { 76 if len(res) == 0 { 77 return nil, err 78 } 79 return res, nil // return what we have 80 } 81 res = append(res, innerBytes...) 82 return res, nil 83 } 84 85 func (vm *VM) BatchedParseBlock(ctx context.Context, blks [][]byte) ([]snowman.Block, error) { 86 if vm.batchedVM == nil { 87 return nil, block.ErrRemoteVMNotImplemented 88 } 89 90 type partialData struct { 91 index int 92 block statelessblock.Block 93 } 94 var ( 95 blocksIndex int 96 blocks = make([]snowman.Block, len(blks)) 97 98 innerBlocksIndex int 99 statelessBlockDescs = make([]partialData, 0, len(blks)) 100 innerBlockBytes = make([][]byte, 0, len(blks)) 101 ) 102 for ; blocksIndex < len(blks); blocksIndex++ { 103 blkBytes := blks[blocksIndex] 104 statelessBlock, err := statelessblock.Parse(blkBytes, vm.ctx.ChainID) 105 if err != nil { 106 break 107 } 108 109 blkID := statelessBlock.ID() 110 block, exists := vm.verifiedBlocks[blkID] 111 if exists { 112 blocks[blocksIndex] = block 113 continue 114 } 115 116 statelessBlockDescs = append(statelessBlockDescs, partialData{ 117 index: blocksIndex, 118 block: statelessBlock, 119 }) 120 innerBlockBytes = append(innerBlockBytes, statelessBlock.Block()) 121 } 122 innerBlockBytes = append(innerBlockBytes, blks[blocksIndex:]...) 123 124 // parse all inner blocks at once 125 innerBlks, err := vm.batchedVM.BatchedParseBlock(ctx, innerBlockBytes) 126 if err != nil { 127 return nil, err 128 } 129 for ; innerBlocksIndex < len(statelessBlockDescs); innerBlocksIndex++ { 130 statelessBlockDesc := statelessBlockDescs[innerBlocksIndex] 131 statelessBlk := statelessBlockDesc.block 132 blkID := statelessBlk.ID() 133 134 _, status, err := vm.State.GetBlock(blkID) 135 if err == database.ErrNotFound { 136 status = choices.Processing 137 } else if err != nil { 138 return nil, err 139 } 140 141 if statelessSignedBlock, ok := statelessBlk.(statelessblock.SignedBlock); ok { 142 blocks[statelessBlockDesc.index] = &postForkBlock{ 143 SignedBlock: statelessSignedBlock, 144 postForkCommonComponents: postForkCommonComponents{ 145 vm: vm, 146 innerBlk: innerBlks[innerBlocksIndex], 147 status: status, 148 }, 149 } 150 } else { 151 blocks[statelessBlockDesc.index] = &postForkOption{ 152 Block: statelessBlk, 153 postForkCommonComponents: postForkCommonComponents{ 154 vm: vm, 155 innerBlk: innerBlks[innerBlocksIndex], 156 status: status, 157 }, 158 } 159 } 160 } 161 for ; blocksIndex < len(blocks); blocksIndex, innerBlocksIndex = blocksIndex+1, innerBlocksIndex+1 { 162 blocks[blocksIndex] = &preForkBlock{ 163 Block: innerBlks[innerBlocksIndex], 164 vm: vm, 165 } 166 } 167 return blocks, nil 168 } 169 170 func (vm *VM) getStatelessBlk(blkID ids.ID) (statelessblock.Block, error) { 171 if currentBlk, exists := vm.verifiedBlocks[blkID]; exists { 172 return currentBlk.getStatelessBlk(), nil 173 } 174 statelessBlock, _, err := vm.State.GetBlock(blkID) 175 return statelessBlock, err 176 }