diff --git a/block/interface/header.go b/block/interface/header.go index da2f666ff..cf4f5f25f 100644 --- a/block/interface/header.go +++ b/block/interface/header.go @@ -223,6 +223,7 @@ type Header interface { Logger(logger *zerolog.Logger) *zerolog.Logger // GetShardState returns the deserialized shard state object. + // Note that header encoded shard state only exists in the first block of the epoch GetShardState() (shard.State, error) // Copy returns a copy of the header. diff --git a/consensus/engine/consensus_engine.go b/consensus/engine/consensus_engine.go index 1cbc77be1..a43cc7c6e 100644 --- a/consensus/engine/consensus_engine.go +++ b/consensus/engine/consensus_engine.go @@ -35,6 +35,8 @@ type ChainReader interface { GetBlock(hash common.Hash, number uint64) *types.Block // ReadShardState retrieves sharding state given the epoch number. + // This api reads the shard state cached or saved on the chaindb. + // Thus, only should be used to read the shard state of the current chain. ReadShardState(epoch *big.Int) (shard.State, error) } @@ -50,10 +52,11 @@ type Engine interface { // via the VerifySeal method. VerifyHeader(chain ChainReader, header *block.Header, seal bool) error - // Similiar to VerifyHeader, but used for verifying the block header against some commit signature for - // more flexibility on the api. Example of usage is the new block verification with cx transaction receipts proof - // where the bls signature is given by another shard through beacon chain, not from the child block header. - VerifyHeaderWithSignature(chain ChainReader, header *block.Header, commitSig []byte, commitBitmap []byte) error + // Similiar to VerifyHeader, which is only for verifying the block headers of one's own chain, this verification + // is used for verifying "incoming" block header against commit signature and bitmap sent from the other chain cross-shard via libp2p. + // i.e. this header verification api is more flexible since the caller specifies which commit signature and bitmap to use + // for verifying the block header, which is necessary for cross-shard block header verification. Example of such is cross-shard transaction. + VerifyHeaderWithSignature(header *block.Header, commitSig []byte, commitBitmap []byte) error // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers // concurrently. The method returns a quit channel to abort the operations and diff --git a/core/block_validator.go b/core/block_validator.go index df2224b49..b20d9544b 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -219,5 +219,5 @@ func (v *BlockValidator) ValidateCXReceiptsProof(cxp *types.CXReceiptsProof) err } // (4) verify blockHeader with seal - return v.engine.VerifyHeaderWithSignature(v.bc, cxp.Header, cxp.CommitSig, cxp.CommitBitmap) + return v.engine.VerifyHeaderWithSignature(cxp.Header, cxp.CommitSig, cxp.CommitBitmap) } diff --git a/core/resharding.go b/core/resharding.go index 4cd2885ba..76e1d7c0c 100644 --- a/core/resharding.go +++ b/core/resharding.go @@ -221,6 +221,8 @@ func GetInitShardState() shard.State { } // GetShardState returns the shard state based on epoch number +// This api for getting shard state is what should be used to get shard state regardless of +// current chain dependency (ex. getting shard state from block header received during cross-shard transaction) func GetShardState(epoch *big.Int) shard.State { utils.Logger().Info().Int64("epoch", epoch.Int64()).Msg("Get Shard State of Epoch.") shardingConfig := ShardingSchedule.InstanceForEpoch(epoch) diff --git a/internal/chain/engine.go b/internal/chain/engine.go index 9d14710a4..849c22d39 100644 --- a/internal/chain/engine.go +++ b/internal/chain/engine.go @@ -101,68 +101,8 @@ func (e *engineImpl) VerifyHeaders(chain engine.ChainReader, headers []*block.He return abort, results } -// Similiar to VerifyHeader, but used for verifying the block header against some commit signature for -// more flexibility on the api. Example of usage is the new block verification with cx transaction receipts proof -// where the bls signature is given by another shard through beacon chain, not from the child block header. -func (e *engineImpl) VerifyHeaderWithSignature(chain engine.ChainReader, header *block.Header, payload []byte, commitBitmap []byte) error { - if chain.CurrentHeader().Number().Uint64() <= uint64(1) { - return nil - } - publicKeys, err := retrievePublicKeys(chain, header) - if err != nil { - return ctxerror.New("[VerifySeal] Cannot retrieve publickeys for block").WithCause(err) - } - - aggSig, mask, err := ReadSignatureBitmapByPublicKeys(payload, publicKeys) - if err != nil { - return ctxerror.New("[VerifySeal] Unable to deserialize the commitSignature and commitBitmap in Block Header").WithCause(err) - } - hash := header.Hash() - quorum, err := QuorumForBlock(chain, header) - if err != nil { - return errors.Wrapf(err, - "cannot calculate quorum for block %s", header.Number()) - } - if count := utils.CountOneBits(mask.Bitmap); count < quorum { - return ctxerror.New("[VerifySeal] Not enough signature in commitSignature from Block Header", - "need", quorum, "got", count) - } - - blockNumHash := make([]byte, 8) - binary.LittleEndian.PutUint64(blockNumHash, header.Number().Uint64()-1) - commitPayload := append(blockNumHash, hash[:]...) - - if !aggSig.VerifyHash(mask.AggregatePublic, commitPayload) { - return ctxerror.New("[VerifySeal] Unable to verify aggregated signature for block", "blockNum", header.Number().Uint64()-1, "blockHash", hash) - } - return nil -} - -// retrievePublicKeys finds the public keys of current block's committee -func retrievePublicKeys(bc engine.ChainReader, header *block.Header) ([]*bls.PublicKey, error) { - shardState := core.GetShardState(header.Epoch()) - committee := shardState.FindCommitteeByID(header.ShardID()) - if committee == nil { - return nil, ctxerror.New("cannot find shard in the shard state", - "blockNumber", header.Number(), - "shardID", header.ShardID(), - ) - } - var committerKeys []*bls.PublicKey - for _, member := range committee.NodeList { - committerKey := new(bls.PublicKey) - err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey) - if err != nil { - return nil, ctxerror.New("cannot convert BLS public key", - "blsPublicKey", member.BlsPublicKey).WithCause(err) - } - committerKeys = append(committerKeys, committerKey) - } - return committerKeys, nil -} - -// retrievePublicKeysFromLastBlock finds the public keys of last block's committee -func retrievePublicKeysFromLastBlock(bc engine.ChainReader, header *block.Header) ([]*bls.PublicKey, error) { +// ReadPublicKeysFromLastBlock finds the public keys of last block's committee +func ReadPublicKeysFromLastBlock(bc engine.ChainReader, header *block.Header) ([]*bls.PublicKey, error) { parentHeader := bc.GetHeaderByHash(header.ParentHash()) if parentHeader == nil { return nil, ctxerror.New("cannot find parent block header in DB", @@ -196,7 +136,7 @@ func (e *engineImpl) VerifySeal(chain engine.ChainReader, header *block.Header) if chain.CurrentHeader().Number().Uint64() <= uint64(1) { return nil } - publicKeys, err := retrievePublicKeysFromLastBlock(chain, header) + publicKeys, err := ReadPublicKeysFromLastBlock(chain, header) if err != nil { return ctxerror.New("[VerifySeal] Cannot retrieve publickeys from last block").WithCause(err) } @@ -208,7 +148,7 @@ func (e *engineImpl) VerifySeal(chain engine.ChainReader, header *block.Header) } parentHash := header.ParentHash() parentHeader := chain.GetHeader(parentHash, header.Number().Uint64()-1) - parentQuorum, err := QuorumForBlock(chain, parentHeader) + parentQuorum, err := QuorumForBlock(parentHeader) if err != nil { return errors.Wrapf(err, "cannot calculate quorum for block %s", header.Number()) @@ -241,14 +181,8 @@ func (e *engineImpl) Finalize(chain engine.ChainReader, header *block.Header, st } // QuorumForBlock returns the quorum for the given block header. -func QuorumForBlock( - chain engine.ChainReader, h *block.Header, -) (quorum int, err error) { - ss, err := chain.ReadShardState(h.Epoch()) - if err != nil { - return 0, errors.Wrapf(err, - "cannot read shard state for epoch %s", h.Epoch()) - } +func QuorumForBlock(h *block.Header) (quorum int, err error) { + ss := core.GetShardState(h.Epoch()) c := ss.FindCommitteeByID(h.ShardID()) if c == nil { return 0, errors.Errorf( @@ -256,3 +190,63 @@ func QuorumForBlock( } return (len(c.NodeList))*2/3 + 1, nil } + +// Similiar to VerifyHeader, which is only for verifying the block headers of one's own chain, this verification +// is used for verifying "incoming" block header against commit signature and bitmap sent from the other chain cross-shard via libp2p. +// i.e. this header verification api is more flexible since the caller specifies which commit signature and bitmap to use +// for verifying the block header, which is necessary for cross-shard block header verification. Example of such is cross-shard transaction. +func (e *engineImpl) VerifyHeaderWithSignature(header *block.Header, commitSig []byte, commitBitmap []byte) error { + publicKeys, err := GetPublicKeys(header) + if err != nil { + return ctxerror.New("[VerifyHeaderWithSignature] Cannot get publickeys for block header").WithCause(err) + } + + payload := append(commitSig[:], commitBitmap[:]...) + aggSig, mask, err := ReadSignatureBitmapByPublicKeys(payload, publicKeys) + if err != nil { + return ctxerror.New("[VerifyHeaderWithSignature] Unable to deserialize the commitSignature and commitBitmap in Block Header").WithCause(err) + } + + hash := header.Hash() + quorum, err := QuorumForBlock(header) + if err != nil { + return errors.Wrapf(err, + "cannot calculate quorum for block %s", header.Number()) + } + if count := utils.CountOneBits(mask.Bitmap); count < quorum { + return ctxerror.New("[VerifyHeaderWithSignature] Not enough signature in commitSignature from Block Header", + "need", quorum, "got", count) + } + + blockNumHash := make([]byte, 8) + binary.LittleEndian.PutUint64(blockNumHash, header.Number().Uint64()) + commitPayload := append(blockNumHash, hash[:]...) + + if !aggSig.VerifyHash(mask.AggregatePublic, commitPayload) { + return ctxerror.New("[VerifySeal] Unable to verify aggregated signature for block", "blockNum", header.Number().Uint64()-1, "blockHash", hash) + } + return nil +} + +// GetPublicKeys finds the public keys of the committee that signed the block header +func GetPublicKeys(header *block.Header) ([]*bls.PublicKey, error) { + shardState := core.GetShardState(header.Epoch()) + committee := shardState.FindCommitteeByID(header.ShardID()) + if committee == nil { + return nil, ctxerror.New("cannot find shard in the shard state", + "blockNumber", header.Number(), + "shardID", header.ShardID(), + ) + } + var committerKeys []*bls.PublicKey + for _, member := range committee.NodeList { + committerKey := new(bls.PublicKey) + err := member.BlsPublicKey.ToLibBLSPublicKey(committerKey) + if err != nil { + return nil, ctxerror.New("cannot convert BLS public key", + "blsPublicKey", member.BlsPublicKey).WithCause(err) + } + committerKeys = append(committerKeys, committerKey) + } + return committerKeys, nil +}