github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/state_syncable_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  	"fmt"
     9  
    10  	"go.uber.org/zap"
    11  
    12  	"github.com/MetalBlockchain/metalgo/database"
    13  	"github.com/MetalBlockchain/metalgo/snow/engine/snowman/block"
    14  	"github.com/MetalBlockchain/metalgo/vms/proposervm/summary"
    15  )
    16  
    17  func (vm *VM) StateSyncEnabled(ctx context.Context) (bool, error) {
    18  	if vm.ssVM == nil {
    19  		return false, nil
    20  	}
    21  
    22  	return vm.ssVM.StateSyncEnabled(ctx)
    23  }
    24  
    25  func (vm *VM) GetOngoingSyncStateSummary(ctx context.Context) (block.StateSummary, error) {
    26  	if vm.ssVM == nil {
    27  		return nil, block.ErrStateSyncableVMNotImplemented
    28  	}
    29  
    30  	innerSummary, err := vm.ssVM.GetOngoingSyncStateSummary(ctx)
    31  	if err != nil {
    32  		return nil, err // includes database.ErrNotFound case
    33  	}
    34  
    35  	return vm.buildStateSummary(ctx, innerSummary)
    36  }
    37  
    38  func (vm *VM) GetLastStateSummary(ctx context.Context) (block.StateSummary, error) {
    39  	if vm.ssVM == nil {
    40  		return nil, block.ErrStateSyncableVMNotImplemented
    41  	}
    42  
    43  	// Extract inner vm's last state summary
    44  	innerSummary, err := vm.ssVM.GetLastStateSummary(ctx)
    45  	if err != nil {
    46  		return nil, err // including database.ErrNotFound case
    47  	}
    48  
    49  	return vm.buildStateSummary(ctx, innerSummary)
    50  }
    51  
    52  // Note: it's important that ParseStateSummary do not use any index or state
    53  // to allow summaries being parsed also by freshly started node with no previous state.
    54  func (vm *VM) ParseStateSummary(ctx context.Context, summaryBytes []byte) (block.StateSummary, error) {
    55  	if vm.ssVM == nil {
    56  		return nil, block.ErrStateSyncableVMNotImplemented
    57  	}
    58  
    59  	statelessSummary, err := summary.Parse(summaryBytes)
    60  	if err != nil {
    61  		// it may be a preFork summary
    62  		return vm.ssVM.ParseStateSummary(ctx, summaryBytes)
    63  	}
    64  
    65  	innerSummary, err := vm.ssVM.ParseStateSummary(ctx, statelessSummary.InnerSummaryBytes())
    66  	if err != nil {
    67  		return nil, fmt.Errorf("could not parse inner summary due to: %w", err)
    68  	}
    69  	block, err := vm.parsePostForkBlock(ctx, statelessSummary.BlockBytes())
    70  	if err != nil {
    71  		return nil, fmt.Errorf("could not parse proposervm block bytes from summary due to: %w", err)
    72  	}
    73  
    74  	return &stateSummary{
    75  		StateSummary: statelessSummary,
    76  		innerSummary: innerSummary,
    77  		block:        block,
    78  		vm:           vm,
    79  	}, nil
    80  }
    81  
    82  func (vm *VM) GetStateSummary(ctx context.Context, height uint64) (block.StateSummary, error) {
    83  	if vm.ssVM == nil {
    84  		return nil, block.ErrStateSyncableVMNotImplemented
    85  	}
    86  
    87  	innerSummary, err := vm.ssVM.GetStateSummary(ctx, height)
    88  	if err != nil {
    89  		return nil, err // including database.ErrNotFound case
    90  	}
    91  
    92  	return vm.buildStateSummary(ctx, innerSummary)
    93  }
    94  
    95  // Note: building state summary requires a well formed height index.
    96  func (vm *VM) buildStateSummary(ctx context.Context, innerSummary block.StateSummary) (block.StateSummary, error) {
    97  	forkHeight, err := vm.GetForkHeight()
    98  	switch err {
    99  	case nil:
   100  		if innerSummary.Height() < forkHeight {
   101  			return innerSummary, nil
   102  		}
   103  	case database.ErrNotFound:
   104  		// fork has not been reached since there is not fork height
   105  		// just return innerSummary
   106  		vm.ctx.Log.Debug("built pre-fork summary",
   107  			zap.Stringer("summaryID", innerSummary.ID()),
   108  			zap.Uint64("summaryHeight", innerSummary.Height()),
   109  		)
   110  		return innerSummary, nil
   111  	default:
   112  		return nil, err
   113  	}
   114  
   115  	height := innerSummary.Height()
   116  	blkID, err := vm.GetBlockIDAtHeight(ctx, height)
   117  	if err != nil {
   118  		vm.ctx.Log.Debug("failed to fetch proposervm block ID",
   119  			zap.Uint64("height", height),
   120  			zap.Error(err),
   121  		)
   122  		return nil, err
   123  	}
   124  	block, err := vm.getPostForkBlock(ctx, blkID)
   125  	if err != nil {
   126  		vm.ctx.Log.Warn("failed to fetch proposervm block",
   127  			zap.Stringer("blkID", blkID),
   128  			zap.Uint64("height", height),
   129  			zap.Error(err),
   130  		)
   131  		return nil, err
   132  	}
   133  
   134  	statelessSummary, err := summary.Build(forkHeight, block.Bytes(), innerSummary.Bytes())
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	vm.ctx.Log.Debug("built post-fork summary",
   140  		zap.Stringer("summaryID", statelessSummary.ID()),
   141  		zap.Uint64("summaryHeight", forkHeight),
   142  	)
   143  	return &stateSummary{
   144  		StateSummary: statelessSummary,
   145  		innerSummary: innerSummary,
   146  		block:        block,
   147  		vm:           vm,
   148  	}, nil
   149  }