code: func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, 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)
  	}
  	// Validate the state root against the received state root and throw
  	// an error if they don't match.
  	if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
  		return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
  	}
  	return nil
  }
docs: // 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.