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 }