github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/handler/blockstore.go (about)

     1  package handler
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	gethCommon "github.com/onflow/go-ethereum/common"
     8  
     9  	"github.com/onflow/flow-go/fvm/evm/types"
    10  	"github.com/onflow/flow-go/model/flow"
    11  )
    12  
    13  const (
    14  	BlockHashListCapacity    = 16
    15  	BlockStoreLatestBlockKey = "LatestBlock"
    16  	BlockStoreBlockHashesKey = "LatestBlockHashes"
    17  )
    18  
    19  type BlockStore struct {
    20  	backend       types.Backend
    21  	rootAddress   flow.Address
    22  	blockProposal *types.Block
    23  }
    24  
    25  var _ types.BlockStore = &BlockStore{}
    26  
    27  // NewBlockStore constructs a new block store
    28  func NewBlockStore(backend types.Backend, rootAddress flow.Address) *BlockStore {
    29  	return &BlockStore{
    30  		backend:     backend,
    31  		rootAddress: rootAddress,
    32  	}
    33  }
    34  
    35  // BlockProposal returns the block proposal to be updated by the handler
    36  func (bs *BlockStore) BlockProposal() (*types.Block, error) {
    37  	if bs.blockProposal != nil {
    38  		return bs.blockProposal, nil
    39  	}
    40  
    41  	cadenceHeight, err := bs.backend.GetCurrentBlockHeight()
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	cadenceBlock, found, err := bs.backend.GetBlockAtHeight(cadenceHeight)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	if !found {
    51  		return nil, fmt.Errorf("cadence block not found")
    52  	}
    53  
    54  	lastExecutedBlock, err := bs.LatestBlock()
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	parentHash, err := lastExecutedBlock.Hash()
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	// cadence block timestamp is unix nanoseconds but evm blocks
    65  	// expect timestamps in unix seconds so we convert here
    66  	timestamp := uint64(cadenceBlock.Timestamp / int64(time.Second))
    67  
    68  	bs.blockProposal = types.NewBlock(
    69  		parentHash,
    70  		lastExecutedBlock.Height+1,
    71  		timestamp,
    72  		lastExecutedBlock.TotalSupply,
    73  		gethCommon.Hash{},
    74  		make([]gethCommon.Hash, 0),
    75  	)
    76  	return bs.blockProposal, nil
    77  }
    78  
    79  // CommitBlockProposal commits the block proposal to the chain
    80  func (bs *BlockStore) CommitBlockProposal() error {
    81  	bp, err := bs.BlockProposal()
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	blockBytes, err := bp.ToBytes()
    87  	if err != nil {
    88  		return types.NewFatalError(err)
    89  	}
    90  
    91  	err = bs.backend.SetValue(bs.rootAddress[:], []byte(BlockStoreLatestBlockKey), blockBytes)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	err = bs.updateBlockHashList(bs.blockProposal)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	bs.blockProposal = nil
   102  	return nil
   103  }
   104  
   105  // ResetBlockProposal resets the block proposal
   106  func (bs *BlockStore) ResetBlockProposal() error {
   107  	bs.blockProposal = nil
   108  	return nil
   109  }
   110  
   111  // LatestBlock returns the latest executed block
   112  func (bs *BlockStore) LatestBlock() (*types.Block, error) {
   113  	data, err := bs.backend.GetValue(bs.rootAddress[:], []byte(BlockStoreLatestBlockKey))
   114  	if len(data) == 0 {
   115  		return types.GenesisBlock, err
   116  	}
   117  	if err != nil {
   118  		return nil, types.NewFatalError(err)
   119  	}
   120  	return types.NewBlockFromBytes(data)
   121  }
   122  
   123  // BlockHash returns the block hash for the last x blocks
   124  func (bs *BlockStore) BlockHash(height uint64) (gethCommon.Hash, error) {
   125  	bhl, err := bs.getBlockHashList()
   126  	if err != nil {
   127  		return gethCommon.Hash{}, err
   128  	}
   129  	_, hash := bhl.BlockHashByHeight(height)
   130  	return hash, nil
   131  }
   132  
   133  func (bs *BlockStore) getBlockHashList() (*types.BlockHashList, error) {
   134  	data, err := bs.backend.GetValue(bs.rootAddress[:], []byte(BlockStoreBlockHashesKey))
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	if len(data) == 0 {
   139  		bhl := types.NewBlockHashList(BlockHashListCapacity)
   140  		err = bhl.Push(types.GenesisBlock.Height, types.GenesisBlockHash)
   141  		return bhl, err
   142  	}
   143  	return types.NewBlockHashListFromEncoded(data)
   144  }
   145  
   146  func (bs *BlockStore) updateBlockHashList(block *types.Block) error {
   147  	bhl, err := bs.getBlockHashList()
   148  	if err != nil {
   149  		return err
   150  	}
   151  	hash, err := block.Hash()
   152  	if err != nil {
   153  		return err
   154  	}
   155  	err = bhl.Push(block.Height, hash)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	err = bs.backend.SetValue(
   160  		bs.rootAddress[:],
   161  		[]byte(BlockStoreBlockHashesKey),
   162  		bhl.Encode())
   163  	if err != nil {
   164  		return err
   165  	}
   166  	return nil
   167  }