[engine] refactored engine and added VerifyCrossLink method

pull/3633/head
Jacky Wang 4 years ago
parent 787592174c
commit f203a5b41c
No known key found for this signature in database
GPG Key ID: 1085CE5F4FF5842C
  1. 3
      consensus/engine/consensus_engine.go
  2. 3
      hmy/downloader/adapter_test.go
  3. 154
      internal/chain/engine.go
  4. 108
      node/node_cross_link.go

@ -87,6 +87,9 @@ type Engine interface {
chain ChainReader, header *block.Header, commitSig bls.SerializedSignature, commitBitmap []byte,
) error
// VerifyCrossLink verify cross link
VerifyCrossLink(ChainReader, types.CrossLink) error
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications (the order is that of

@ -131,6 +131,9 @@ func (e *dummyEngine) VerifyHeader(engine.ChainReader, *block.Header, bool) erro
func (e *dummyEngine) VerifyHeaderSignature(engine.ChainReader, *block.Header, bls.SerializedSignature, []byte) error {
return nil
}
func (e *dummyEngine) VerifyCrossLink(engine.ChainReader, types.CrossLink) error {
return nil
}
func (e *dummyEngine) VerifyHeaders(engine.ChainReader, []*block.Header, []bool) (chan<- struct{}, <-chan error) {
return nil, nil
}

@ -27,7 +27,7 @@ import (
)
const (
verifiedSigCache = 20
verifiedSigCache = 50
epochCtxCache = 20
)
@ -152,10 +152,13 @@ func (e *engineImpl) VerifySeal(chain engine.ChainReader, header *block.Header)
return errors.New("[VerifySeal] no parent header found")
}
sig := header.LastCommitSignature()
bitmap := header.LastCommitBitmap()
pas := payloadArgsFromHeader(parentHeader)
sas := sigArgs{
sig: header.LastCommitSignature(),
bitmap: header.LastCommitBitmap(),
}
if err := e.verifyHeaderSignatureCached(chain, parentHeader, sig, bitmap); err != nil {
if err := e.verifySignatureCached(chain, pas, sas); err != nil {
return errors.Wrapf(err, "verify signature for parent %s", parentHash.String())
}
return nil
@ -403,60 +406,84 @@ func (e *engineImpl) VerifyHeaderSignature(chain engine.ChainReader, header *blo
if chain.CurrentHeader().Number().Uint64() <= uint64(1) {
return nil
}
return e.verifyHeaderSignatureCached(chain, header, commitSig, commitBitmap)
pas := payloadArgsFromHeader(header)
sas := sigArgs{commitSig, commitBitmap}
err := e.verifySignatureCached(chain, pas, sas)
if err != nil {
return err
}
return nil
}
// VerifyCrossLink verifies the signature of the given CrossLink.
func (e *engineImpl) VerifyCrossLink(chain engine.ChainReader, cl types.CrossLink) error {
if cl.BlockNum() <= 1 {
return errors.New("crossLink BlockNumber should greater than 1")
}
if !chain.Config().IsCrossLink(cl.Epoch()) {
return errors.Errorf("not cross-link epoch: %v", cl.Epoch())
}
pas := payloadArgsFromCrossLink(cl)
sas := sigArgs{cl.Signature(), cl.Bitmap()}
return e.verifySignatureCached(chain, pas, sas)
}
func (e *engineImpl) verifyHeaderSignatureCached(chain engine.ChainReader, header *block.Header, commitSig bls_cosi.SerializedSignature, commitBitmap []byte) error {
key := newVerifiedSigKey(header.Hash(), commitSig, commitBitmap)
if _, ok := e.verifiedSigCache.Get(key); ok {
func (e *engineImpl) verifySignatureCached(chain engine.ChainReader, pas payloadArgs, sas sigArgs) error {
verifiedKey := newVerifiedSigKey(pas.blockHash, sas.sig, sas.bitmap)
if _, ok := e.verifiedSigCache.Get(verifiedKey); ok {
return nil
}
if err := e.verifyHeaderSignature(chain, header, commitSig, commitBitmap); err != nil {
// Not in cache, do verify.
if err := e.verifySignature(chain, pas, sas); err != nil {
return err
}
e.verifiedSigCache.Add(key, struct{}{})
e.verifiedSigCache.Add(verifiedKey, struct{}{})
return nil
}
func (e *engineImpl) verifyHeaderSignature(chain engine.ChainReader, header *block.Header, commitSig bls_cosi.SerializedSignature, commitBitmap []byte) error {
ec, ok := e.getCachedEpochCtx(header)
if !ok {
// Epoch context not in cache, read from chain
var err error
ec, err = readEpochCtxFromChain(chain, header.Epoch(), header.ShardID())
if err != nil {
return err
}
func (e *engineImpl) verifySignature(chain engine.ChainReader, pas payloadArgs, sas sigArgs) error {
ec, err := e.getEpochCtxCached(chain, pas.shardID, pas.epoch.Uint64())
if err != nil {
return err
}
var (
pubKeys = ec.pubKeys
qrVerifier = ec.qrVerifier
pubKeys = ec.pubKeys
qrVerifier = ec.qrVerifier
commitSig = sas.sig
commitBitmap = sas.bitmap
)
aggSig, mask, err := DecodeSigBitmap(commitSig, commitBitmap, pubKeys)
if err != nil {
return errors.Wrap(err, "deserialize signature and bitmap")
}
// Verify signature, mask against quorum.Verifier and publicKeys
if !qrVerifier.IsQuorumAchievedByMask(mask) {
return errors.New("not enough signature collected")
}
commitPayload := signature.ConstructCommitPayload(chain,
header.Epoch(), header.Hash(), header.Number().Uint64(), header.ViewID().Uint64())
commitPayload := pas.constructPayload(chain)
if !aggSig.VerifyHash(mask.AggregatePublic, commitPayload) {
return errors.New("Unable to verify aggregated signature for block")
}
return nil
}
func (e *engineImpl) getCachedEpochCtx(header *block.Header) (*epochCtx, bool) {
ecKey := newEpochCtxKeyFromHeader(header)
ec, ok := e.epochCtxCache.Get(ecKey)
if !ok || ec == nil {
return nil, false
func (e *engineImpl) getEpochCtxCached(chain engine.ChainReader, shardID uint32, epoch uint64) (epochCtx, error) {
ecKey := epochCtxKey{
shardID: shardID,
epoch: epoch,
}
cached, ok := e.epochCtxCache.Get(ecKey)
if ok && cached != nil {
return cached.(epochCtx), nil
}
return ec.(*epochCtx), true
ec, err := readEpochCtxFromChain(chain, ecKey)
if err != nil {
return epochCtx{}, nil
}
e.epochCtxCache.Add(ecKey, ec)
return ec, nil
}
// Support 512 at most validator nodes
@ -480,6 +507,44 @@ func newVerifiedSigKey(blockHash common.Hash, sig bls_cosi.SerializedSignature,
}
}
// payloadArgs is the arguments for constructing the payload for signature verification.
type payloadArgs struct {
blockHash common.Hash
shardID uint32
epoch *big.Int
number uint64
viewID uint64
}
func payloadArgsFromHeader(header *block.Header) payloadArgs {
return payloadArgs{
blockHash: header.Hash(),
shardID: header.ShardID(),
epoch: header.Epoch(),
number: header.Number().Uint64(),
viewID: header.ViewID().Uint64(),
}
}
func payloadArgsFromCrossLink(cl types.CrossLink) payloadArgs {
return payloadArgs{
blockHash: cl.Hash(),
shardID: cl.ShardID(),
epoch: cl.Epoch(),
number: cl.Number().Uint64(),
viewID: cl.ViewID().Uint64(),
}
}
func (args payloadArgs) constructPayload(chain engine.ChainReader) []byte {
return signature.ConstructCommitPayload(chain, args.epoch, args.blockHash, args.number, args.viewID)
}
type sigArgs struct {
sig bls_cosi.SerializedSignature
bitmap []byte
}
type (
// epochCtxKey is the key for caching epochCtx
epochCtxKey struct {
@ -495,39 +560,36 @@ type (
}
)
func newEpochCtxKeyFromHeader(header *block.Header) epochCtxKey {
return epochCtxKey{
shardID: header.ShardID(),
epoch: header.Epoch().Uint64(),
}
}
func readEpochCtxFromChain(chain engine.ChainReader, epoch *big.Int, targetShardID uint32) (*epochCtx, error) {
func readEpochCtxFromChain(chain engine.ChainReader, key epochCtxKey) (epochCtx, error) {
var (
epoch = new(big.Int).SetUint64(key.epoch)
targetShardID = key.shardID
)
ss, err := readShardState(chain, epoch, targetShardID)
if err != nil {
return nil, err
return epochCtx{}, err
}
shardComm, err := ss.FindCommitteeByID(targetShardID)
if err != nil {
return nil, err
return epochCtx{}, err
}
pubKeys, err := shardComm.BLSPublicKeys()
if err != nil {
return nil, err
return epochCtx{}, err
}
isStaking := chain.Config().IsStaking(epoch)
qrVerifier, err := quorum.NewVerifier(shardComm, epoch, isStaking)
if err != nil {
return nil, err
return epochCtx{}, err
}
return &epochCtx{
return epochCtx{
qrVerifier: qrVerifier,
pubKeys: pubKeys,
}, nil
}
func readShardState(chain engine.ChainReader, epoch *big.Int, targetShardID uint32) (*shard.State, error) {
// When doing cross shard, we need recalcualte the shard state since we don't have
// When doing cross shard, we need recalculate the shard state since we don't have
// shard state of other shards
if needRecalculateStateShard(chain, epoch, targetShardID) {
shardState, err := committee.WithStakingEnabled.Compute(epoch, chain)

@ -2,17 +2,12 @@ package node
import (
"fmt"
"math/big"
common2 "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/quorum"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/multibls"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking/verify"
"github.com/pkg/errors"
)
@ -33,8 +28,8 @@ func (node *Node) VerifyBlockCrossLinks(block *types.Block) error {
return nil
}
crossLinks := &types.CrossLinks{}
err := rlp.DecodeBytes(cxLinksData, crossLinks)
crossLinks := types.CrossLinks{}
err := rlp.DecodeBytes(cxLinksData, &crossLinks)
if err != nil {
return errors.Wrapf(
err, "[CrossLinkVerification] failed to decode cross links",
@ -45,7 +40,7 @@ func (node *Node) VerifyBlockCrossLinks(block *types.Block) error {
return errors.New("[CrossLinkVerification] cross links are not sorted")
}
for _, crossLink := range *crossLinks {
for _, crossLink := range crossLinks {
cl, err := node.Blockchain().ReadCrossLink(crossLink.ShardID(), crossLink.BlockNum())
if err == nil && cl != nil {
// Add slash for exist same blocknum but different crosslink
@ -129,98 +124,11 @@ func (node *Node) VerifyCrossLink(cl types.CrossLink) error {
if node.Blockchain().ShardID() != shard.BeaconChainShardID {
return errors.New("[VerifyCrossLink] Shard chains should not verify cross links")
}
engine := node.Blockchain().Engine()
if cl.BlockNum() <= 1 {
return errors.New("[VerifyCrossLink] CrossLink BlockNumber should greater than 1")
if err := engine.VerifyCrossLink(node.Blockchain(), cl); err != nil {
fmt.Println("verify crosslink failed", err)
return errors.Wrap(err, "[VerifyCrossLink]")
}
if !node.Blockchain().Config().IsCrossLink(cl.Epoch()) {
return errors.Errorf(
"[VerifyCrossLink] CrossLink Epoch should >= cross link starting epoch %v %v",
cl.Epoch(), node.Blockchain().Config().CrossLinkEpoch,
)
}
aggSig := &bls.Sign{}
sig := cl.Signature()
if err := aggSig.Deserialize(sig[:]); err != nil {
return errors.Wrapf(
err,
"[VerifyCrossLink] unable to deserialize multi-signature from payload",
)
}
committee, err := node.lookupCommittee(cl.Epoch(), cl.ShardID())
if err != nil {
return err
}
decider, err := node.lookupDecider(cl.Epoch(), cl.ShardID())
if err != nil {
return err
}
return verify.AggregateSigForCommittee(
node.Blockchain(), committee, decider, aggSig, cl.Hash(), cl.BlockNum(), cl.ViewID().Uint64(), cl.Epoch(), cl.Bitmap(),
)
}
func (node *Node) lookupDecider(
epoch *big.Int, shardID uint32,
) (quorum.Decider, error) {
// Look up
key := fmt.Sprintf("decider-%d-%d", epoch.Uint64(), shardID)
if b, ok := node.deciderCache.Get(key); ok {
return b.(quorum.Decider), nil
}
// If not found, construct
committee, err := node.lookupCommittee(epoch, shardID)
if err != nil {
return nil, err
}
decider := quorum.NewDecider(
quorum.SuperMajorityStake, committee.ShardID,
)
decider.SetMyPublicKeyProvider(func() (multibls.PublicKeys, error) {
return nil, nil
})
if _, err := decider.SetVoters(committee, epoch); err != nil {
return nil, err
}
// Put in cache
node.deciderCache.Add(key, decider)
return decider, nil
}
func (node *Node) lookupCommittee(
epoch *big.Int, shardID uint32,
) (*shard.Committee, error) {
// Look up
key := fmt.Sprintf("committee-%d-%d", epoch.Uint64(), shardID)
if b, ok := node.committeeCache.Get(key); ok {
return b.(*shard.Committee), nil
}
// If not found, construct
shardState, err := node.Blockchain().ReadShardState(epoch)
if err != nil {
return nil, err
}
committee, err := shardState.FindCommitteeByID(shardID)
if err != nil {
return nil, err
}
// Put in cache
node.committeeCache.Add(key, committee)
return committee, nil
return nil
}

Loading…
Cancel
Save