github.com/MetalBlockchain/subnet-evm@v0.4.9/plugin/evm/syncervm_server.go (about) 1 // (c) 2021-2022, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package evm 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/MetalBlockchain/metalgo/database" 11 "github.com/MetalBlockchain/metalgo/snow/engine/snowman/block" 12 13 "github.com/MetalBlockchain/subnet-evm/core" 14 "github.com/MetalBlockchain/subnet-evm/plugin/evm/message" 15 "github.com/ethereum/go-ethereum/log" 16 ) 17 18 type stateSyncServerConfig struct { 19 Chain *core.BlockChain 20 21 // SyncableInterval is the interval at which blocks are eligible to provide syncable block summaries. 22 SyncableInterval uint64 23 } 24 25 type stateSyncServer struct { 26 chain *core.BlockChain 27 28 syncableInterval uint64 29 } 30 31 type StateSyncServer interface { 32 GetLastStateSummary(context.Context) (block.StateSummary, error) 33 GetStateSummary(context.Context, uint64) (block.StateSummary, error) 34 } 35 36 func NewStateSyncServer(config *stateSyncServerConfig) StateSyncServer { 37 return &stateSyncServer{ 38 chain: config.Chain, 39 syncableInterval: config.SyncableInterval, 40 } 41 } 42 43 // stateSummaryAtHeight returns the SyncSummary at [height] if valid and available. 44 func (server *stateSyncServer) stateSummaryAtHeight(height uint64) (message.SyncSummary, error) { 45 blk := server.chain.GetBlockByNumber(height) 46 if blk == nil { 47 return message.SyncSummary{}, fmt.Errorf("block not found for height (%d)", height) 48 } 49 50 if !server.chain.HasState(blk.Root()) { 51 return message.SyncSummary{}, fmt.Errorf("block root does not exist for height (%d), root (%s)", height, blk.Root()) 52 } 53 54 summary, err := message.NewSyncSummary(blk.Hash(), height, blk.Root()) 55 if err != nil { 56 return message.SyncSummary{}, fmt.Errorf("failed to construct syncable block at height %d: %w", height, err) 57 } 58 return summary, nil 59 } 60 61 // GetLastStateSummary returns the latest state summary. 62 // State summary is calculated by the block nearest to last accepted 63 // that is divisible by [syncableInterval] 64 // If no summary is available, [database.ErrNotFound] must be returned. 65 func (server *stateSyncServer) GetLastStateSummary(context.Context) (block.StateSummary, error) { 66 lastHeight := server.chain.LastAcceptedBlock().NumberU64() 67 lastSyncSummaryNumber := lastHeight - lastHeight%server.syncableInterval 68 69 summary, err := server.stateSummaryAtHeight(lastSyncSummaryNumber) 70 if err != nil { 71 log.Debug("could not get latest state summary", "err", err) 72 return nil, database.ErrNotFound 73 } 74 log.Debug("Serving syncable block at latest height", "summary", summary) 75 return summary, nil 76 } 77 78 // GetStateSummary implements StateSyncableVM and returns a summary corresponding 79 // to the provided [height] if the node can serve state sync data for that key. 80 // If not, [database.ErrNotFound] must be returned. 81 func (server *stateSyncServer) GetStateSummary(_ context.Context, height uint64) (block.StateSummary, error) { 82 summaryBlock := server.chain.GetBlockByNumber(height) 83 if summaryBlock == nil || 84 summaryBlock.NumberU64() > server.chain.LastAcceptedBlock().NumberU64() || 85 summaryBlock.NumberU64()%server.syncableInterval != 0 { 86 return nil, database.ErrNotFound 87 } 88 89 summary, err := server.stateSummaryAtHeight(summaryBlock.NumberU64()) 90 if err != nil { 91 log.Debug("could not get state summary", "height", height, "err", err) 92 return nil, database.ErrNotFound 93 } 94 95 log.Debug("Serving syncable block at requested height", "height", height, "summary", summary) 96 return summary, nil 97 }