The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
woop/hmy/downloader/inserthelper.go

220 lines
6.5 KiB

package downloader
import (
"fmt"
"hash"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
"golang.org/x/crypto/sha3"
bls_core "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/quorum"
"github.com/harmony-one/harmony/consensus/signature"
"github.com/harmony-one/harmony/core/types"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/chain"
"github.com/harmony-one/harmony/multibls"
"github.com/harmony-one/harmony/shard"
)
// sigVerifyError is the error type of failing verify the signature of the current block.
// Since this is a sanity field and is not included the block hash, it needs extra verification.
// The error types is used to differentiate the error of signature verification VS insert error.
type sigVerifyError struct {
err error
}
func (err *sigVerifyError) Error() string {
return fmt.Sprintf("failed verify signature: %v", err.err.Error())
}
// insertHelperImpl helps to verify and insert blocks, along with some caching mechanism.
type insertHelperImpl struct {
bc blockChain
deciderCache *lru.Cache // Epoch -> quorum.Decider
shardStateCache *lru.Cache // Epoch -> *shard.State
verifiedSigCache *lru.Cache // verifiedSigKey -> struct{}{}
}
func newInsertHelper(bc blockChain) insertHelper {
deciderCache, _ := lru.New(5)
shardStateCache, _ := lru.New(5)
sigCache, _ := lru.New(20)
return &insertHelperImpl{
bc: bc,
deciderCache: deciderCache,
shardStateCache: shardStateCache,
verifiedSigCache: sigCache,
}
}
func (ch *insertHelperImpl) verifyAndInsertBlocks(blocks types.Blocks) (int, error) {
for i, block := range blocks {
if err := ch.verifyAndInsertBlock(block); err != nil {
return i, err
}
}
return len(blocks), nil
}
func (ch *insertHelperImpl) verifyAndInsertBlock(block *types.Block) error {
// verify the commit sig of current block
if err := ch.verifyBlockSignature(block); err != nil {
return &sigVerifyError{err}
}
ch.markBlockSigVerified(block, block.GetCurrentCommitSig())
// verify header. Skip verify the previous seal if we have already verified
verifySeal := !ch.isBlockLastSigVerified(block)
if err := ch.bc.Engine().VerifyHeader(ch.bc, block.Header(), verifySeal); err != nil {
return err
}
// Insert chain.
if _, err := ch.bc.InsertChain(types.Blocks{block}, false); err != nil {
return err
}
// Write commit sig data
return ch.bc.WriteCommitSig(block.NumberU64(), block.GetCurrentCommitSig())
}
func (ch *insertHelperImpl) verifyBlockSignature(block *types.Block) error {
// TODO: This is the duplicate logic to the implementation of verifySeal and consensus.
// Better refactor to the blockchain or engine structure
decider, err := ch.readDeciderByEpoch(block.Epoch())
if err != nil {
return err
}
sig, mask, err := decodeCommitSig(block.GetCurrentCommitSig(), decider.Participants())
if err != nil {
return err
}
if !decider.IsQuorumAchievedByMask(mask) {
return errors.New("quorum not achieved")
}
commitSigBytes := signature.ConstructCommitPayload(ch.bc, block.Epoch(), block.Hash(),
block.NumberU64(), block.Header().ViewID().Uint64())
if !sig.VerifyHash(mask.AggregatePublic, commitSigBytes) {
return errors.New("aggregate signature failed verification")
}
return nil
}
func (ch *insertHelperImpl) writeBlockSignature(block *types.Block) error {
return ch.bc.WriteCommitSig(block.NumberU64(), block.GetCurrentCommitSig())
}
func (ch *insertHelperImpl) getDeciderByEpoch(epoch *big.Int) (quorum.Decider, error) {
epochUint := epoch.Uint64()
if decider, ok := ch.deciderCache.Get(epochUint); ok && decider != nil {
return decider.(quorum.Decider), nil
}
decider, err := ch.getDeciderByEpoch(epoch)
if err != nil {
return nil, errors.Wrapf(err, "unable to read quorum of epoch %v", epoch.Uint64())
}
ch.deciderCache.Add(epochUint, decider)
return decider, nil
}
func (ch *insertHelperImpl) readDeciderByEpoch(epoch *big.Int) (quorum.Decider, error) {
isStaking := ch.bc.Config().IsStaking(epoch)
decider := ch.getNewDecider(isStaking)
ss, err := ch.getShardState(epoch)
if err != nil {
return nil, err
}
subComm, err := ss.FindCommitteeByID(ch.shardID())
if err != nil {
return nil, err
}
pubKeys, err := subComm.BLSPublicKeys()
if err != nil {
return nil, err
}
decider.UpdateParticipants(pubKeys)
if _, err := decider.SetVoters(subComm, epoch); err != nil {
return nil, err
}
return decider, nil
}
func (ch *insertHelperImpl) getNewDecider(isStaking bool) quorum.Decider {
if isStaking {
return quorum.NewDecider(quorum.SuperMajorityVote, ch.bc.ShardID())
} else {
return quorum.NewDecider(quorum.SuperMajorityStake, ch.bc.ShardID())
}
}
func (ch *insertHelperImpl) getShardState(epoch *big.Int) (*shard.State, error) {
if ss, ok := ch.shardStateCache.Get(epoch.Uint64()); ok && ss != nil {
return ss.(*shard.State), nil
}
ss, err := ch.bc.ReadShardState(epoch)
if err != nil {
return nil, err
}
ch.shardStateCache.Add(epoch.Uint64(), ss)
return ss, nil
}
func (ch *insertHelperImpl) markBlockSigVerified(block *types.Block, sigAndBitmap []byte) {
key := newVerifiedSigKey(block.Hash(), sigAndBitmap)
ch.verifiedSigCache.Add(key, struct{}{})
}
func (ch *insertHelperImpl) isBlockLastSigVerified(block *types.Block) bool {
lastSig := block.Header().LastCommitSignature()
lastBM := block.Header().LastCommitBitmap()
lastSigAndBM := append(lastSig[:], lastBM...)
key := newVerifiedSigKey(block.Hash(), lastSigAndBM)
_, ok := ch.verifiedSigCache.Get(key)
return ok
}
func (ch *insertHelperImpl) shardID() uint32 {
return ch.bc.ShardID()
}
func decodeCommitSig(commitBytes []byte, publicKeys multibls.PublicKeys) (*bls_core.Sign, *bls_cosi.Mask, error) {
if len(commitBytes) < bls_cosi.BLSSignatureSizeInBytes {
return nil, nil, fmt.Errorf("unexpected signature bytes size: %v / %v", len(commitBytes),
bls_cosi.BLSSignatureSizeInBytes)
}
return chain.ReadSignatureBitmapByPublicKeys(commitBytes, publicKeys)
}
type verifiedSigKey struct {
blockHash common.Hash
sbHash common.Hash // hash of block signature + bitmap
}
var hasherPool = sync.Pool{
New: func() interface{} {
return sha3.New256()
},
}
func newVerifiedSigKey(blockHash common.Hash, sigAndBitmap []byte) verifiedSigKey {
hasher := hasherPool.Get().(hash.Hash)
defer func() {
hasher.Reset()
hasherPool.Put(hasher)
}()
var sbHash common.Hash
hasher.Write(sigAndBitmap)
hasher.Sum(sbHash[0:])
return verifiedSigKey{
blockHash: blockHash,
sbHash: sbHash,
}
}