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  }