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  }