// Copyright 2015 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package core import ( "bytes" "encoding/binary" "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/internal/ctxerror" consensus_engine "github.com/harmony-one/harmony/consensus/engine" "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/internal/params" ) // BlockValidator is responsible for validating block headers, uncles and // processed state. // // BlockValidator implements Validator. type BlockValidator struct { config *params.ChainConfig // Chain configuration options bc *BlockChain // Canonical block chain engine consensus_engine.Engine // Consensus engine used for validating } // NewBlockValidator returns a new block validator which is safe for re-use func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus_engine.Engine) *BlockValidator { validator := &BlockValidator{ config: config, engine: engine, bc: blockchain, } return validator } // ValidateBody verifies the block header's transaction root. // The headers are assumed to be already validated at this point. func (v *BlockValidator) ValidateBody(block *types.Block) error { // Check whether the block's known, and if not, that it's linkable if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { return ErrKnownBlock } if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { return consensus_engine.ErrUnknownAncestor } return consensus_engine.ErrPrunedAncestor } // Header validity is known at this point, check the uncles and transactions header := block.Header() //if err := v.engine.VerifyUncles(v.bc, block); err != nil { // return err //} if hash := types.DeriveSha( block.Transactions(), block.StakingTransactions(), ); hash != header.TxHash() { return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash()) } return nil } // ValidateState validates the various changes that happen after a state // transition, such as amount of used gas, the receipt roots and the state root // itself. ValidateState returns a database batch if the validation was a success // otherwise nil and an error is returned. func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.DB, receipts types.Receipts, cxReceipts types.CXReceipts, usedGas uint64) error { header := block.Header() if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) } // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. rbloom := types.CreateBloom(receipts) if rbloom != header.Bloom() { return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom(), rbloom) } // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) receiptSha := types.DeriveSha(receipts) if receiptSha != header.ReceiptHash() { return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash(), receiptSha) } if v.config.AcceptsCrossTx(block.Epoch()) { cxsSha := cxReceipts.ComputeMerkleRoot() if cxsSha != header.OutgoingReceiptHash() { legacySha := types.DeriveMultipleShardsSha(cxReceipts) if legacySha != header.OutgoingReceiptHash() { return fmt.Errorf("invalid cross shard receipt root hash (remote: %x local: %x, legacy: %x)", header.OutgoingReceiptHash(), cxsSha, legacySha) } } } // Validate the state root against the received state root and throw // an error if they don't match. if root := statedb.IntermediateRoot(v.config.IsS3(header.Epoch())); header.Root() != root { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root(), root) } return nil } // ValidateHeader checks whether a header conforms to the consensus rules of a // given engine. Verifying the seal may be done optionally here, or explicitly // via the VerifySeal method. func (v *BlockValidator) ValidateHeader(block *types.Block, seal bool) error { return v.engine.VerifyHeader(v.bc, block.Header(), true) } // ValidateHeaders verifies a batch of blocks' headers concurrently. The method returns a quit channel // to abort the operations and a results channel to retrieve the async verifications func (v *BlockValidator) ValidateHeaders(chain []*types.Block) (chan<- struct{}, <-chan error) { // Start the parallel header verifier headers := make([]*block.Header, len(chain)) seals := make([]bool, len(chain)) for i, block := range chain { headers[i] = block.Header() seals[i] = true } return v.engine.VerifyHeaders(v.bc, headers, seals) } // CalcGasLimit computes the gas limit of the next block after parent. It aims // to keep the baseline gas above the provided floor, and increase it towards the // ceil if the blocks are full. If the ceil is exceeded, it will always decrease // the gas allowance. func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 { // contrib = (parentGasUsed * 3 / 2) / 1024 contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor // decay = parentGasLimit / 1024 -1 decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1 /* strategy: gasLimit of block-to-mine is set based on parent's gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we increase it, otherwise lower it (or leave it unchanged if it's right at that usage) the amount increased/decreased depends on how far away from parentGasLimit * (2/3) parentGasUsed is. */ limit := parent.GasLimit() - decay + contrib if limit < params.MinGasLimit { limit = params.MinGasLimit } // If we're outside our allowed gas range, we try to hone towards them if limit < gasFloor { limit = parent.GasLimit() + decay if limit > gasFloor { limit = gasFloor } } else if limit > gasCeil { limit = parent.GasLimit() - decay if limit < gasCeil { limit = gasCeil } } return limit } // ValidateCXReceiptsProof checks whether the given CXReceiptsProof is consistency with itself func (v *BlockValidator) ValidateCXReceiptsProof(cxp *types.CXReceiptsProof) error { if !v.config.AcceptsCrossTx(cxp.Header.Epoch()) { return ctxerror.New("[ValidateCXReceiptsProof] cross shard receipt received before cx fork") } toShardID, err := cxp.GetToShardID() if err != nil { return ctxerror.New("[ValidateCXReceiptsProof] invalid shardID").WithCause(err) } merkleProof := cxp.MerkleProof shardRoot := common.Hash{} foundMatchingShardID := false byteBuffer := bytes.Buffer{} // prepare to calculate source shard outgoing cxreceipts root hash for j := 0; j < len(merkleProof.ShardIDs); j++ { sKey := make([]byte, 4) binary.BigEndian.PutUint32(sKey, merkleProof.ShardIDs[j]) byteBuffer.Write(sKey) byteBuffer.Write(merkleProof.CXShardHashes[j][:]) if merkleProof.ShardIDs[j] == toShardID { shardRoot = merkleProof.CXShardHashes[j] foundMatchingShardID = true } } if !foundMatchingShardID { return ctxerror.New("[ValidateCXReceiptsProof] Didn't find matching toShardID (no receipts for my shard)") } sourceShardID := merkleProof.ShardID sourceBlockNum := merkleProof.BlockNum sha := types.DeriveSha(cxp.Receipts) // (1) verify the CXReceipts trie root match if sha != shardRoot { return ctxerror.New("[ValidateCXReceiptsProof] Trie Root of ReadCXReceipts Not Match", "sourceShardID", sourceShardID, "sourceBlockNum", sourceBlockNum, "calculated", sha, "got", shardRoot) } // (2) verify the outgoingCXReceiptsHash match outgoingHashFromSourceShard := crypto.Keccak256Hash(byteBuffer.Bytes()) if byteBuffer.Len() == 0 { outgoingHashFromSourceShard = types.EmptyRootHash } if outgoingHashFromSourceShard != merkleProof.CXReceiptHash { return ctxerror.New("[ValidateCXReceiptsProof] IncomingReceiptRootHash from source shard not match", "sourceShardID", sourceShardID, "sourceBlockNum", sourceBlockNum, "calculated", outgoingHashFromSourceShard, "got", merkleProof.CXReceiptHash) } // (3) verify the block hash matches if cxp.Header.Hash() != merkleProof.BlockHash || cxp.Header.OutgoingReceiptHash() != merkleProof.CXReceiptHash { return ctxerror.New("[ValidateCXReceiptsProof] BlockHash or OutgoingReceiptHash not match in block Header", "blockHash", cxp.Header.Hash(), "merkleProofBlockHash", merkleProof.BlockHash, "headerOutReceiptHash", cxp.Header.OutgoingReceiptHash(), "merkleOutReceiptHash", merkleProof.CXReceiptHash) } // (4) verify blockHeader with seal return v.engine.VerifyHeaderWithSignature(v.bc, cxp.Header, cxp.CommitSig, cxp.CommitBitmap, true) }