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 }