consolidate and use block validator functions properly

pull/1539/head
Dennis Won 5 years ago
parent 9f4e6a7e9e
commit 479c9ad60f
  1. 2
      api/service/syncing/syncing.go
  2. 99
      core/block_validator.go
  3. 2
      core/blockchain.go
  4. 12
      core/types.go
  5. 16
      internal/chain/engine.go
  6. 2
      node/node_cross_shard.go
  7. 10
      node/node_handler.go
  8. 2
      node/node_newblock.go

@ -540,7 +540,7 @@ func (ss *StateSync) updateBlockAndStatus(block *types.Block, bc *core.BlockChai
// Verify block signatures
// TODO chao: only when block is verified against last commit sigs, we can update the block and status
if block.NumberU64() > 1 {
err := core.VerifyBlockLastCommitSigs(bc, block.Header())
err := bc.Engine().VerifyHeader(bc, block.Header(), true)
if err != nil {
utils.Logger().Error().Err(err).Msgf("[SYNC] failed verifying signatures for new block %d", block.NumberU64())
return false

@ -23,11 +23,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/harmony-one/bls/ffi/go/bls"
bls2 "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/block"
consensus_engine "github.com/harmony-one/harmony/consensus/engine"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
@ -113,70 +111,25 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
return nil
}
// VerifyHeaderWithSignature verifies the header with corresponding commit sigs
func VerifyHeaderWithSignature(header *block.Header, commitSig []byte, commitBitmap []byte) error {
if header == nil || len(commitSig) != 96 || len(commitBitmap) == 0 {
return ctxerror.New("[VerifyHeaderWithSignature] Invalid header/commitSig/commitBitmap", "header", header, "commitSigLen", len(commitSig), "commitBitmapLen", len(commitBitmap))
}
shardState := GetShardState(header.Epoch())
committee := shardState.FindCommitteeByID(header.ShardID())
var err error
if committee == nil {
return ctxerror.New("[VerifyHeaderWithSignature] Failed to read shard state", "shardID", header.ShardID(), "blockNum", header.Number())
}
var committerKeys []*bls.PublicKey
parseKeysSuccess := true
for _, member := range committee.NodeList {
committerKey := new(bls.PublicKey)
err = member.BlsPublicKey.ToLibBLSPublicKey(committerKey)
if err != nil {
parseKeysSuccess = false
break
}
committerKeys = append(committerKeys, committerKey)
}
if !parseKeysSuccess {
return ctxerror.New("[VerifyBlockWithSignature] cannot convert BLS public key", "shardID", header.ShardID(), "blockNum", header.Number()).WithCause(err)
}
mask, err := bls2.NewMask(committerKeys, nil)
if err != nil {
return ctxerror.New("[VerifyHeaderWithSignature] cannot create group sig mask", "shardID", header.ShardID(), "blockNum", header.Number()).WithCause(err)
}
if err := mask.SetMask(commitBitmap); err != nil {
return ctxerror.New("[VerifyHeaderWithSignature] cannot set group sig mask bits", "shardID", header.ShardID(), "blockNum", header.Number()).WithCause(err)
}
aggSig := bls.Sign{}
err = aggSig.Deserialize(commitSig[:])
if err != nil {
return ctxerror.New("[VerifyNewBlock] unable to deserialize multi-signature from payload").WithCause(err)
}
blockNumBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(blockNumBytes, header.Number().Uint64())
hash := header.Hash()
commitPayload := append(blockNumBytes, hash[:]...)
if !aggSig.VerifyHash(mask.AggregatePublic, commitPayload) {
return ctxerror.New("[VerifyHeaderWithSignature] Failed to verify the signature for last commit sig", "shardID", header.ShardID(), "blockNum", header.Number())
}
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)
}
// VerifyBlockLastCommitSigs verifies the last commit sigs of the block
func VerifyBlockLastCommitSigs(bc *BlockChain, header *block.Header) error {
parentBlock := bc.GetBlockByNumber(header.Number().Uint64() - 1)
if parentBlock == nil {
return ctxerror.New("[VerifyBlockLastCommitSigs] Failed to get parent block", "shardID", header.ShardID(), "blockNum", header.Number())
}
parentHeader := parentBlock.Header()
lastCommitSig := header.LastCommitSignature()
lastCommitBitmap := header.LastCommitBitmap()
// 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))
return VerifyHeaderWithSignature(parentHeader, lastCommitSig[:], lastCommitBitmap)
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
@ -216,11 +169,11 @@ func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
return limit
}
// IsValidCXReceiptsProof checks whether the given CXReceiptsProof is consistency with itself
func IsValidCXReceiptsProof(cxp *types.CXReceiptsProof) error {
// ValidateCXReceiptsProof checks whether the given CXReceiptsProof is consistency with itself
func (v *BlockValidator) ValidateCXReceiptsProof(cxp *types.CXReceiptsProof) error {
toShardID, err := cxp.GetToShardID()
if err != nil {
return ctxerror.New("[IsValidCXReceiptsProof] invalid shardID").WithCause(err)
return ctxerror.New("[ValidateCXReceiptsProof] invalid shardID").WithCause(err)
}
merkleProof := cxp.MerkleProof
@ -241,7 +194,7 @@ func IsValidCXReceiptsProof(cxp *types.CXReceiptsProof) error {
}
if !foundMatchingShardID {
return ctxerror.New("[IsValidCXReceiptsProof] Didn't find matching shardID")
return ctxerror.New("[ValidateCXReceiptsProof] Didn't find matching shardID")
}
sourceShardID := merkleProof.ShardID
@ -251,20 +204,20 @@ func IsValidCXReceiptsProof(cxp *types.CXReceiptsProof) error {
// (1) verify the CXReceipts trie root match
if sha != shardRoot {
return ctxerror.New("[IsValidCXReceiptsProof] Trie Root of ReadCXReceipts Not Match", "sourceShardID", sourceShardID, "sourceBlockNum", sourceBlockNum, "calculated", sha, "got", 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 outgoingHashFromSourceShard != merkleProof.CXReceiptHash {
return ctxerror.New("[IsValidCXReceiptsProof] IncomingReceiptRootHash from source shard not match", "sourceShardID", sourceShardID, "sourceBlockNum", sourceBlockNum, "calculated", outgoingHashFromSourceShard, "got", 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("[IsValidCXReceiptsProof] BlockHash or OutgoingReceiptHash not match in block Header", "blockHash", cxp.Header.Hash(), "merkleProofBlockHash", merkleProof.BlockHash, "headerOutReceiptHash", cxp.Header.OutgoingReceiptHash(), "merkleOutReceiptHash", 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 signatures of blockHeader
return VerifyHeaderWithSignature(cxp.Header, cxp.CommitSig, cxp.CommitBitmap)
// (4) verify blockHeader with seal
return v.engine.VerifyHeader(v.bc, cxp.Header, true)
}

@ -1225,7 +1225,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
headers[i] = block.Header()
seals[i] = true
}
abort, results := bc.engine.VerifyHeaders(bc, headers, seals)
abort, results := bc.Engine().VerifyHeaders(bc, headers, seals)
defer close(abort)
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)

@ -33,6 +33,18 @@ type Validator interface {
// ValidateState validates the given statedb and optionally the receipts and
// gas used.
ValidateState(block, parent *types.Block, state *state.DB, receipts types.Receipts, cxs types.CXReceipts, usedGas uint64) error
// 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.
ValidateHeader(block *types.Block, seal bool) error
// 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
ValidateHeaders(chain []*types.Block) (chan<- struct{}, <-chan error)
// ValidateCXReceiptsProof checks whether the given CXReceiptsProof is consistency with itself
ValidateCXReceiptsProof(cxp *types.CXReceiptsProof) error
}
// Processor is an interface for processing blocks using a given initial state.

@ -83,9 +83,19 @@ func (e *engineImpl) VerifyHeader(chain engine.ChainReader, header *block.Header
// a results channel to retrieve the async verifications.
func (e *engineImpl) VerifyHeaders(chain engine.ChainReader, headers []*block.Header, seals []bool) (chan<- struct{}, <-chan error) {
abort, results := make(chan struct{}), make(chan error, len(headers))
for i := 0; i < len(headers); i++ {
results <- nil
}
go func() {
for i, header := range headers {
err := e.VerifyHeader(chain, header, seals[i])
select {
case <-abort:
return
case results <- err:
}
}
}()
return abort, results
}

@ -227,7 +227,7 @@ func (node *Node) verifyIncomingReceipts(block *types.Block) error {
}
}
if err := core.IsValidCXReceiptsProof(cxp); err != nil {
if err := node.Blockchain().Validator().ValidateCXReceiptsProof(cxp); err != nil {
return ctxerror.New("[verifyIncomingReceipts] verification failed").WithCause(err)
}
}

@ -302,18 +302,16 @@ func (node *Node) VerifyNewBlock(newBlock *types.Block) error {
// TODO ek – where do we verify parent-child invariants,
// e.g. "child.Number == child.IsGenesis() ? 0 : parent.Number+1"?
if newBlock.NumberU64() > 1 {
err := core.VerifyBlockLastCommitSigs(node.Blockchain(), newBlock.Header())
if err != nil {
return err
}
err := node.Blockchain().Validator().ValidateHeader(newBlock, true)
if err != nil {
return ctxerror.New("cannot ValidateHeader for the new block", "blockHash", newBlock.Hash()).WithCause(err)
}
if newBlock.ShardID() != node.Blockchain().ShardID() {
return ctxerror.New("wrong shard ID",
"my shard ID", node.Blockchain().ShardID(),
"new block's shard ID", newBlock.ShardID())
}
err := node.Blockchain().ValidateNewBlock(newBlock)
err = node.Blockchain().ValidateNewBlock(newBlock)
if err != nil {
return ctxerror.New("cannot ValidateNewBlock",
"blockHash", newBlock.Hash(),

@ -232,7 +232,7 @@ Loop:
}
}
if err := core.IsValidCXReceiptsProof(cxp); err != nil {
if err := node.Blockchain().Validator().ValidateCXReceiptsProof(cxp); err != nil {
utils.Logger().Error().Err(err).Msg("[proposeReceiptsProof] Invalid CXReceiptsProof")
continue
}

Loading…
Cancel
Save