aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/byzantine-lab/dexon-consensus/core/utils
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/byzantine-lab/dexon-consensus/core/utils')
-rw-r--r--vendor/github.com/byzantine-lab/dexon-consensus/core/utils/crypto.go376
-rw-r--r--vendor/github.com/byzantine-lab/dexon-consensus/core/utils/nodeset-cache.go245
-rw-r--r--vendor/github.com/byzantine-lab/dexon-consensus/core/utils/penalty-helper.go131
-rw-r--r--vendor/github.com/byzantine-lab/dexon-consensus/core/utils/round-based-config.go112
-rw-r--r--vendor/github.com/byzantine-lab/dexon-consensus/core/utils/round-event.go358
-rw-r--r--vendor/github.com/byzantine-lab/dexon-consensus/core/utils/signer.go154
-rw-r--r--vendor/github.com/byzantine-lab/dexon-consensus/core/utils/utils.go207
-rw-r--r--vendor/github.com/byzantine-lab/dexon-consensus/core/utils/vote-filter.go72
8 files changed, 1655 insertions, 0 deletions
diff --git a/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/crypto.go b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/crypto.go
new file mode 100644
index 000000000..161c1d495
--- /dev/null
+++ b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/crypto.go
@@ -0,0 +1,376 @@
+// Copyright 2018 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package utils
+
+import (
+ "bytes"
+ "encoding/binary"
+
+ "github.com/byzantine-lab/dexon-consensus/common"
+ "github.com/byzantine-lab/dexon-consensus/core/crypto"
+ "github.com/byzantine-lab/dexon-consensus/core/types"
+ typesDKG "github.com/byzantine-lab/dexon-consensus/core/types/dkg"
+)
+
+func hashWitness(witness *types.Witness) (common.Hash, error) {
+ binaryHeight := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryHeight, witness.Height)
+ return crypto.Keccak256Hash(
+ binaryHeight,
+ witness.Data), nil
+}
+
+// HashBlock generates hash of a types.Block.
+func HashBlock(block *types.Block) (common.Hash, error) {
+ hashPosition := HashPosition(block.Position)
+ binaryTimestamp, err := block.Timestamp.UTC().MarshalBinary()
+ if err != nil {
+ return common.Hash{}, err
+ }
+ binaryWitness, err := hashWitness(&block.Witness)
+ if err != nil {
+ return common.Hash{}, err
+ }
+
+ hash := crypto.Keccak256Hash(
+ block.ProposerID.Hash[:],
+ block.ParentHash[:],
+ hashPosition[:],
+ binaryTimestamp[:],
+ block.PayloadHash[:],
+ binaryWitness[:])
+ return hash, nil
+}
+
+// VerifyBlockSignature verifies the signature of types.Block.
+func VerifyBlockSignature(b *types.Block) (err error) {
+ payloadHash := crypto.Keccak256Hash(b.Payload)
+ if payloadHash != b.PayloadHash {
+ err = ErrIncorrectHash
+ return
+ }
+ return VerifyBlockSignatureWithoutPayload(b)
+}
+
+// VerifyBlockSignatureWithoutPayload verifies the signature of types.Block but
+// does not check if PayloadHash is correct.
+func VerifyBlockSignatureWithoutPayload(b *types.Block) (err error) {
+ hash, err := HashBlock(b)
+ if err != nil {
+ return
+ }
+ if hash != b.Hash {
+ err = ErrIncorrectHash
+ return
+ }
+ pubKey, err := crypto.SigToPub(b.Hash, b.Signature)
+ if err != nil {
+ return
+ }
+ if !b.ProposerID.Equal(types.NewNodeID(pubKey)) {
+ err = ErrIncorrectSignature
+ return
+ }
+ return
+
+}
+
+// HashVote generates hash of a types.Vote.
+func HashVote(vote *types.Vote) common.Hash {
+ binaryPeriod := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryPeriod, vote.Period)
+
+ hashPosition := HashPosition(vote.Position)
+
+ hash := crypto.Keccak256Hash(
+ vote.ProposerID.Hash[:],
+ vote.BlockHash[:],
+ binaryPeriod,
+ hashPosition[:],
+ vote.PartialSignature.Signature[:],
+ []byte{byte(vote.Type)},
+ )
+ return hash
+}
+
+// VerifyVoteSignature verifies the signature of types.Vote.
+func VerifyVoteSignature(vote *types.Vote) (bool, error) {
+ hash := HashVote(vote)
+ pubKey, err := crypto.SigToPub(hash, vote.Signature)
+ if err != nil {
+ return false, err
+ }
+ if vote.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+func hashCRS(block *types.Block, crs common.Hash) common.Hash {
+ hashPos := HashPosition(block.Position)
+ if block.Position.Round < dkgDelayRound {
+ return crypto.Keccak256Hash(crs[:], hashPos[:], block.ProposerID.Hash[:])
+ }
+ return crypto.Keccak256Hash(crs[:], hashPos[:])
+}
+
+// VerifyCRSSignature verifies the CRS signature of types.Block.
+func VerifyCRSSignature(
+ block *types.Block, crs common.Hash, npks *typesDKG.NodePublicKeys) bool {
+ hash := hashCRS(block, crs)
+ if block.Position.Round < dkgDelayRound {
+ return bytes.Compare(block.CRSSignature.Signature[:], hash[:]) == 0
+ }
+ if npks == nil {
+ return false
+ }
+ pubKey, exist := npks.PublicKeys[block.ProposerID]
+ if !exist {
+ return false
+ }
+ return pubKey.VerifySignature(hash, block.CRSSignature)
+}
+
+// HashPosition generates hash of a types.Position.
+func HashPosition(position types.Position) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, position.Round)
+
+ binaryHeight := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryHeight, position.Height)
+
+ return crypto.Keccak256Hash(
+ binaryRound,
+ binaryHeight,
+ )
+}
+
+func hashDKGPrivateShare(prvShare *typesDKG.PrivateShare) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, prvShare.Round)
+ binaryReset := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryReset, prvShare.Reset)
+
+ return crypto.Keccak256Hash(
+ prvShare.ProposerID.Hash[:],
+ prvShare.ReceiverID.Hash[:],
+ binaryRound,
+ binaryReset,
+ prvShare.PrivateShare.Bytes(),
+ )
+}
+
+// VerifyDKGPrivateShareSignature verifies the signature of
+// typesDKG.PrivateShare.
+func VerifyDKGPrivateShareSignature(
+ prvShare *typesDKG.PrivateShare) (bool, error) {
+ hash := hashDKGPrivateShare(prvShare)
+ pubKey, err := crypto.SigToPub(hash, prvShare.Signature)
+ if err != nil {
+ return false, err
+ }
+ if prvShare.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+func hashDKGMasterPublicKey(mpk *typesDKG.MasterPublicKey) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, mpk.Round)
+ binaryReset := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryReset, mpk.Reset)
+
+ return crypto.Keccak256Hash(
+ mpk.ProposerID.Hash[:],
+ mpk.DKGID.GetLittleEndian(),
+ mpk.PublicKeyShares.MasterKeyBytes(),
+ binaryRound,
+ binaryReset,
+ )
+}
+
+// VerifyDKGMasterPublicKeySignature verifies DKGMasterPublicKey signature.
+func VerifyDKGMasterPublicKeySignature(
+ mpk *typesDKG.MasterPublicKey) (bool, error) {
+ hash := hashDKGMasterPublicKey(mpk)
+ pubKey, err := crypto.SigToPub(hash, mpk.Signature)
+ if err != nil {
+ return false, err
+ }
+ if mpk.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+func hashDKGComplaint(complaint *typesDKG.Complaint) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, complaint.Round)
+ binaryReset := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryReset, complaint.Reset)
+
+ hashPrvShare := hashDKGPrivateShare(&complaint.PrivateShare)
+
+ return crypto.Keccak256Hash(
+ complaint.ProposerID.Hash[:],
+ binaryRound,
+ binaryReset,
+ hashPrvShare[:],
+ )
+}
+
+// VerifyDKGComplaintSignature verifies DKGCompliant signature.
+func VerifyDKGComplaintSignature(
+ complaint *typesDKG.Complaint) (bool, error) {
+ if complaint.Round != complaint.PrivateShare.Round {
+ return false, nil
+ }
+ if complaint.Reset != complaint.PrivateShare.Reset {
+ return false, nil
+ }
+ hash := hashDKGComplaint(complaint)
+ pubKey, err := crypto.SigToPub(hash, complaint.Signature)
+ if err != nil {
+ return false, err
+ }
+ if complaint.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ if !complaint.IsNack() {
+ return VerifyDKGPrivateShareSignature(&complaint.PrivateShare)
+ }
+ return true, nil
+}
+
+func hashDKGPartialSignature(psig *typesDKG.PartialSignature) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, psig.Round)
+
+ return crypto.Keccak256Hash(
+ psig.ProposerID.Hash[:],
+ binaryRound,
+ psig.Hash[:],
+ psig.PartialSignature.Signature[:],
+ )
+}
+
+// VerifyDKGPartialSignatureSignature verifies the signature of
+// typesDKG.PartialSignature.
+func VerifyDKGPartialSignatureSignature(
+ psig *typesDKG.PartialSignature) (bool, error) {
+ hash := hashDKGPartialSignature(psig)
+ pubKey, err := crypto.SigToPub(hash, psig.Signature)
+ if err != nil {
+ return false, err
+ }
+ if psig.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+func hashDKGMPKReady(ready *typesDKG.MPKReady) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, ready.Round)
+ binaryReset := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryReset, ready.Reset)
+
+ return crypto.Keccak256Hash(
+ ready.ProposerID.Hash[:],
+ binaryRound,
+ binaryReset,
+ )
+}
+
+// VerifyDKGMPKReadySignature verifies DKGMPKReady signature.
+func VerifyDKGMPKReadySignature(
+ ready *typesDKG.MPKReady) (bool, error) {
+ hash := hashDKGMPKReady(ready)
+ pubKey, err := crypto.SigToPub(hash, ready.Signature)
+ if err != nil {
+ return false, err
+ }
+ if ready.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+func hashDKGFinalize(final *typesDKG.Finalize) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, final.Round)
+ binaryReset := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryReset, final.Reset)
+
+ return crypto.Keccak256Hash(
+ final.ProposerID.Hash[:],
+ binaryRound,
+ binaryReset,
+ )
+}
+
+func hashDKGSuccess(success *typesDKG.Success) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, success.Round)
+ binaryReset := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryReset, success.Reset)
+
+ return crypto.Keccak256Hash(
+ success.ProposerID.Hash[:],
+ binaryRound,
+ binaryReset,
+ )
+}
+
+// VerifyDKGFinalizeSignature verifies DKGFinalize signature.
+func VerifyDKGFinalizeSignature(
+ final *typesDKG.Finalize) (bool, error) {
+ hash := hashDKGFinalize(final)
+ pubKey, err := crypto.SigToPub(hash, final.Signature)
+ if err != nil {
+ return false, err
+ }
+ if final.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+// VerifyDKGSuccessSignature verifies DKGSuccess signature.
+func VerifyDKGSuccessSignature(
+ success *typesDKG.Success) (bool, error) {
+ hash := hashDKGSuccess(success)
+ pubKey, err := crypto.SigToPub(hash, success.Signature)
+ if err != nil {
+ return false, err
+ }
+ if success.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+// Rehash hashes the hash again and again and again...
+func Rehash(hash common.Hash, count uint) common.Hash {
+ result := hash
+ for i := uint(0); i < count; i++ {
+ result = crypto.Keccak256Hash(result[:])
+ }
+ return result
+}
diff --git a/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/nodeset-cache.go b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/nodeset-cache.go
new file mode 100644
index 000000000..028690e18
--- /dev/null
+++ b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/nodeset-cache.go
@@ -0,0 +1,245 @@
+// Copyright 2018 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package utils
+
+import (
+ "errors"
+ "sync"
+
+ "github.com/byzantine-lab/dexon-consensus/common"
+ "github.com/byzantine-lab/dexon-consensus/core/crypto"
+ "github.com/byzantine-lab/dexon-consensus/core/types"
+)
+
+var (
+ // ErrNodeSetNotReady means we got nil empty node set.
+ ErrNodeSetNotReady = errors.New("node set is not ready")
+ // ErrCRSNotReady means we got empty CRS.
+ ErrCRSNotReady = errors.New("crs is not ready")
+ // ErrConfigurationNotReady means we go nil configuration.
+ ErrConfigurationNotReady = errors.New("configuration is not ready")
+)
+
+type sets struct {
+ crs common.Hash
+ nodeSet *types.NodeSet
+ notarySet map[types.NodeID]struct{}
+}
+
+// NodeSetCacheInterface interface specifies interface used by NodeSetCache.
+type NodeSetCacheInterface interface {
+ // Configuration returns the configuration at a given round.
+ // Return the genesis configuration if round == 0.
+ Configuration(round uint64) *types.Config
+
+ // CRS returns the CRS for a given round.
+ // Return the genesis CRS if round == 0.
+ CRS(round uint64) common.Hash
+
+ // NodeSet returns the node set at a given round.
+ // Return the genesis node set if round == 0.
+ NodeSet(round uint64) []crypto.PublicKey
+}
+
+// NodeSetCache caches node set information.
+//
+// NOTE: this module doesn't handle DKG resetting and can only be used along
+// with utils.RoundEvent.
+type NodeSetCache struct {
+ lock sync.RWMutex
+ nsIntf NodeSetCacheInterface
+ rounds map[uint64]*sets
+ keyPool map[types.NodeID]*struct {
+ pubKey crypto.PublicKey
+ refCnt int
+ }
+}
+
+// NewNodeSetCache constructs an NodeSetCache instance.
+func NewNodeSetCache(nsIntf NodeSetCacheInterface) *NodeSetCache {
+ return &NodeSetCache{
+ nsIntf: nsIntf,
+ rounds: make(map[uint64]*sets),
+ keyPool: make(map[types.NodeID]*struct {
+ pubKey crypto.PublicKey
+ refCnt int
+ }),
+ }
+}
+
+// Exists checks if a node is in node set of that round.
+func (cache *NodeSetCache) Exists(
+ round uint64, nodeID types.NodeID) (exists bool, err error) {
+
+ nIDs, exists := cache.get(round)
+ if !exists {
+ if nIDs, err = cache.update(round); err != nil {
+ return
+ }
+ }
+ _, exists = nIDs.nodeSet.IDs[nodeID]
+ return
+}
+
+// GetPublicKey return public key for that node:
+func (cache *NodeSetCache) GetPublicKey(
+ nodeID types.NodeID) (key crypto.PublicKey, exists bool) {
+
+ cache.lock.RLock()
+ defer cache.lock.RUnlock()
+
+ rec, exists := cache.keyPool[nodeID]
+ if exists {
+ key = rec.pubKey
+ }
+ return
+}
+
+// GetNodeSet returns IDs of nodes set of this round as map.
+func (cache *NodeSetCache) GetNodeSet(round uint64) (*types.NodeSet, error) {
+ IDs, exists := cache.get(round)
+ if !exists {
+ var err error
+ if IDs, err = cache.update(round); err != nil {
+ return nil, err
+ }
+ }
+ return IDs.nodeSet.Clone(), nil
+}
+
+// GetNotarySet returns of notary set of this round.
+func (cache *NodeSetCache) GetNotarySet(
+ round uint64) (map[types.NodeID]struct{}, error) {
+ IDs, err := cache.getOrUpdate(round)
+ if err != nil {
+ return nil, err
+ }
+ return cache.cloneMap(IDs.notarySet), nil
+}
+
+// Purge a specific round.
+func (cache *NodeSetCache) Purge(rID uint64) {
+ cache.lock.Lock()
+ defer cache.lock.Unlock()
+ nIDs, exist := cache.rounds[rID]
+ if !exist {
+ return
+ }
+ for nID := range nIDs.nodeSet.IDs {
+ rec := cache.keyPool[nID]
+ if rec.refCnt--; rec.refCnt == 0 {
+ delete(cache.keyPool, nID)
+ }
+ }
+ delete(cache.rounds, rID)
+}
+
+// Touch updates the internal cache of round.
+func (cache *NodeSetCache) Touch(round uint64) (err error) {
+ _, err = cache.update(round)
+ return
+}
+
+func (cache *NodeSetCache) cloneMap(
+ nIDs map[types.NodeID]struct{}) map[types.NodeID]struct{} {
+ nIDsCopy := make(map[types.NodeID]struct{}, len(nIDs))
+ for k := range nIDs {
+ nIDsCopy[k] = struct{}{}
+ }
+ return nIDsCopy
+}
+
+func (cache *NodeSetCache) getOrUpdate(round uint64) (nIDs *sets, err error) {
+ s, exists := cache.get(round)
+ if !exists {
+ if s, err = cache.update(round); err != nil {
+ return
+ }
+ }
+ nIDs = s
+ return
+}
+
+// update node set for that round.
+//
+// This cache would maintain 10 rounds before the updated round and purge
+// rounds not in this range.
+func (cache *NodeSetCache) update(round uint64) (nIDs *sets, err error) {
+ cache.lock.Lock()
+ defer cache.lock.Unlock()
+ // Get information for the requested round.
+ keySet := cache.nsIntf.NodeSet(round)
+ if keySet == nil {
+ err = ErrNodeSetNotReady
+ return
+ }
+ crs := cache.nsIntf.CRS(round)
+ if (crs == common.Hash{}) {
+ err = ErrCRSNotReady
+ return
+ }
+ // Cache new round.
+ nodeSet := types.NewNodeSet()
+ for _, key := range keySet {
+ nID := types.NewNodeID(key)
+ nodeSet.Add(nID)
+ if rec, exists := cache.keyPool[nID]; exists {
+ rec.refCnt++
+ } else {
+ cache.keyPool[nID] = &struct {
+ pubKey crypto.PublicKey
+ refCnt int
+ }{key, 1}
+ }
+ }
+ cfg := cache.nsIntf.Configuration(round)
+ if cfg == nil {
+ err = ErrConfigurationNotReady
+ return
+ }
+ nIDs = &sets{
+ crs: crs,
+ nodeSet: nodeSet,
+ notarySet: make(map[types.NodeID]struct{}),
+ }
+ nIDs.notarySet = nodeSet.GetSubSet(
+ int(cfg.NotarySetSize), types.NewNotarySetTarget(crs))
+ cache.rounds[round] = nIDs
+ // Purge older rounds.
+ for rID, nIDs := range cache.rounds {
+ nodeSet := nIDs.nodeSet
+ if round-rID <= 5 {
+ continue
+ }
+ for nID := range nodeSet.IDs {
+ rec := cache.keyPool[nID]
+ if rec.refCnt--; rec.refCnt == 0 {
+ delete(cache.keyPool, nID)
+ }
+ }
+ delete(cache.rounds, rID)
+ }
+ return
+}
+
+func (cache *NodeSetCache) get(round uint64) (nIDs *sets, exists bool) {
+ cache.lock.RLock()
+ defer cache.lock.RUnlock()
+ nIDs, exists = cache.rounds[round]
+ return
+}
diff --git a/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/penalty-helper.go b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/penalty-helper.go
new file mode 100644
index 000000000..658fe79a9
--- /dev/null
+++ b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/penalty-helper.go
@@ -0,0 +1,131 @@
+// Copyright 2019 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package utils
+
+import (
+ "errors"
+
+ "github.com/byzantine-lab/dexon-consensus/core/types"
+ typesDKG "github.com/byzantine-lab/dexon-consensus/core/types/dkg"
+)
+
+var (
+ // ErrInvalidDKGMasterPublicKey means the DKG MasterPublicKey is invalid.
+ ErrInvalidDKGMasterPublicKey = errors.New("invalid DKG master public key")
+ // ErrPayloadNotEmpty means the payload of block is not empty.
+ ErrPayloadNotEmpty = errors.New("payload not empty")
+)
+
+// NeedPenaltyDKGPrivateShare checks if the proposer of dkg private share
+// should be penalized.
+func NeedPenaltyDKGPrivateShare(
+ complaint *typesDKG.Complaint, mpk *typesDKG.MasterPublicKey) (bool, error) {
+ if complaint.IsNack() {
+ return false, nil
+ }
+ if mpk.ProposerID != complaint.PrivateShare.ProposerID {
+ return false, nil
+ }
+ ok, err := VerifyDKGMasterPublicKeySignature(mpk)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, ErrInvalidDKGMasterPublicKey
+ }
+ ok, err = VerifyDKGComplaintSignature(complaint)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, nil
+ }
+ ok, err = mpk.PublicKeyShares.VerifyPrvShare(
+ typesDKG.NewID(complaint.PrivateShare.ReceiverID),
+ &complaint.PrivateShare.PrivateShare)
+ if err != nil {
+ return false, err
+ }
+ return !ok, nil
+}
+
+// NeedPenaltyForkVote checks if two votes are fork vote.
+func NeedPenaltyForkVote(vote1, vote2 *types.Vote) (bool, error) {
+ if vote1.ProposerID != vote2.ProposerID ||
+ vote1.Type != vote2.Type ||
+ vote1.Period != vote2.Period ||
+ vote1.Position != vote2.Position ||
+ vote1.BlockHash == vote2.BlockHash {
+ return false, nil
+ }
+ ok, err := VerifyVoteSignature(vote1)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, nil
+ }
+ ok, err = VerifyVoteSignature(vote2)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, nil
+ }
+ return true, nil
+}
+
+// NeedPenaltyForkBlock checks if two blocks are fork block.
+func NeedPenaltyForkBlock(block1, block2 *types.Block) (bool, error) {
+ if block1.ProposerID != block2.ProposerID ||
+ block1.Position != block2.Position ||
+ block1.Hash == block2.Hash {
+ return false, nil
+ }
+ if len(block1.Payload) != 0 || len(block2.Payload) != 0 {
+ return false, ErrPayloadNotEmpty
+ }
+ verifyBlock := func(block *types.Block) (bool, error) {
+ err := VerifyBlockSignatureWithoutPayload(block)
+ switch err {
+ case nil:
+ return true, nil
+ case ErrIncorrectSignature:
+ return false, nil
+ case ErrIncorrectHash:
+ return false, nil
+ default:
+ return false, err
+ }
+ }
+ ok, err := verifyBlock(block1)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, nil
+ }
+ ok, err = verifyBlock(block2)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, nil
+ }
+ return true, nil
+}
diff --git a/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/round-based-config.go b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/round-based-config.go
new file mode 100644
index 000000000..88842cacf
--- /dev/null
+++ b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/round-based-config.go
@@ -0,0 +1,112 @@
+// Copyright 2018 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package utils
+
+import (
+ "fmt"
+
+ "github.com/byzantine-lab/dexon-consensus/core/types"
+)
+
+// RoundBasedConfig is based config for rounds and provide boundary checking
+// for rounds.
+type RoundBasedConfig struct {
+ roundID uint64
+ roundBeginHeight uint64
+ roundEndHeight uint64
+ roundLength uint64
+}
+
+// SetupRoundBasedFields setup round based fields, including round ID, the
+// length of rounds.
+func (c *RoundBasedConfig) SetupRoundBasedFields(
+ roundID uint64, cfg *types.Config) {
+ if c.roundLength > 0 {
+ panic(fmt.Errorf("duplicated set round based fields: %d",
+ c.roundLength))
+ }
+ c.roundID = roundID
+ c.roundLength = cfg.RoundLength
+}
+
+// SetRoundBeginHeight gives the beginning height for the initial round provided
+// when constructed.
+func (c *RoundBasedConfig) SetRoundBeginHeight(begin uint64) {
+ if c.roundBeginHeight != 0 {
+ panic(fmt.Errorf("duplicated set round begin height: %d",
+ c.roundBeginHeight))
+ }
+ c.roundBeginHeight = begin
+ c.roundEndHeight = begin + c.roundLength
+}
+
+// IsLastBlock checks if a block is the last block of this round.
+func (c *RoundBasedConfig) IsLastBlock(b *types.Block) bool {
+ if b.Position.Round != c.roundID {
+ panic(fmt.Errorf("attempt to compare by different round: %s, %d",
+ b, c.roundID))
+ }
+ return b.Position.Height+1 == c.roundEndHeight
+}
+
+// ExtendLength extends round ending height by the length of current round.
+func (c *RoundBasedConfig) ExtendLength() {
+ c.roundEndHeight += c.roundLength
+}
+
+// Contains checks if a block height is in this round.
+func (c *RoundBasedConfig) Contains(h uint64) bool {
+ return c.roundBeginHeight <= h && c.roundEndHeight > h
+}
+
+// RoundID returns the round ID of this config.
+func (c *RoundBasedConfig) RoundID() uint64 {
+ if c.roundLength == 0 {
+ panic(fmt.Errorf("config is not initialized: %d", c.roundID))
+ }
+ return c.roundID
+}
+
+// RoundEndHeight returns next checkpoint to varify if this round is ended.
+func (c *RoundBasedConfig) RoundEndHeight() uint64 {
+ if c.roundLength == 0 {
+ panic(fmt.Errorf("config is not initialized: %d", c.roundID))
+ }
+ return c.roundEndHeight
+}
+
+// AppendTo a config from previous round.
+func (c *RoundBasedConfig) AppendTo(other RoundBasedConfig) {
+ if c.roundID != other.roundID+1 {
+ panic(fmt.Errorf("round IDs of configs not continuous: %d %d",
+ c.roundID, other.roundID))
+ }
+ c.SetRoundBeginHeight(other.roundEndHeight)
+}
+
+// LastPeriodBeginHeight returns the begin height of last period. For example,
+// if a round is extended twice, then the return from this method is:
+//
+// begin + 2 * roundLength - roundLength
+//
+func (c *RoundBasedConfig) LastPeriodBeginHeight() uint64 {
+ if c.roundLength == 0 {
+ panic(fmt.Errorf("config is not initialized: %d", c.roundID))
+ }
+ return c.roundEndHeight - c.roundLength
+}
diff --git a/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/round-event.go b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/round-event.go
new file mode 100644
index 000000000..4f4b04542
--- /dev/null
+++ b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/round-event.go
@@ -0,0 +1,358 @@
+// Copyright 2019 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package utils
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ "github.com/byzantine-lab/dexon-consensus/common"
+ "github.com/byzantine-lab/dexon-consensus/core/types"
+ typesDKG "github.com/byzantine-lab/dexon-consensus/core/types/dkg"
+)
+
+// ErrUnmatchedBlockHeightWithConfig is for invalid parameters for NewRoundEvent.
+type ErrUnmatchedBlockHeightWithConfig struct {
+ round uint64
+ reset uint64
+ blockHeight uint64
+}
+
+func (e ErrUnmatchedBlockHeightWithConfig) Error() string {
+ return fmt.Sprintf("unsynced block height and cfg: round:%d reset:%d h:%d",
+ e.round, e.reset, e.blockHeight)
+}
+
+// RoundEventParam defines the parameters passed to event handlers of
+// RoundEvent.
+type RoundEventParam struct {
+ // 'Round' of next checkpoint, might be identical to previous checkpoint.
+ Round uint64
+ // the count of reset DKG for 'Round+1'.
+ Reset uint64
+ // the begin block height of this event, the end block height of this event
+ // would be BeginHeight + config.RoundLength.
+ BeginHeight uint64
+ // The configuration for 'Round'.
+ Config *types.Config
+ // The CRS for 'Round'.
+ CRS common.Hash
+}
+
+// NextRoundValidationHeight returns the height to check if the next round is
+// ready.
+func (e RoundEventParam) NextRoundValidationHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength*9/10
+}
+
+// NextCRSProposingHeight returns the height to propose CRS for next round.
+func (e RoundEventParam) NextCRSProposingHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength/2
+}
+
+// NextDKGPreparationHeight returns the height to prepare DKG set for next
+// round.
+func (e RoundEventParam) NextDKGPreparationHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength*2/3
+}
+
+// NextRoundHeight returns the height of the beginning of next round.
+func (e RoundEventParam) NextRoundHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength
+}
+
+// NextTouchNodeSetCacheHeight returns the height to touch the node set cache.
+func (e RoundEventParam) NextTouchNodeSetCacheHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength/2
+}
+
+// NextDKGResetHeight returns the height to reset DKG for next period.
+func (e RoundEventParam) NextDKGResetHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength*85/100
+}
+
+// NextDKGRegisterHeight returns the height to register DKG.
+func (e RoundEventParam) NextDKGRegisterHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength/2
+}
+
+// RoundEndHeight returns the round ending height of this round event.
+func (e RoundEventParam) RoundEndHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength
+}
+
+func (e RoundEventParam) String() string {
+ return fmt.Sprintf("roundEvtParam{Round:%d Reset:%d Height:%d}",
+ e.Round,
+ e.Reset,
+ e.BeginHeight)
+}
+
+// roundEventFn defines the fingerprint of handlers of round events.
+type roundEventFn func([]RoundEventParam)
+
+// governanceAccessor is a subset of core.Governance to break the dependency
+// between core and utils package.
+type governanceAccessor interface {
+ // Configuration returns the configuration at a given round.
+ // Return the genesis configuration if round == 0.
+ Configuration(round uint64) *types.Config
+
+ // CRS returns the CRS for a given round.
+ // Return the genesis CRS if round == 0.
+ CRS(round uint64) common.Hash
+
+ // DKGComplaints gets all the DKGComplaints of round.
+ DKGComplaints(round uint64) []*typesDKG.Complaint
+
+ // DKGMasterPublicKeys gets all the DKGMasterPublicKey of round.
+ DKGMasterPublicKeys(round uint64) []*typesDKG.MasterPublicKey
+
+ // IsDKGFinal checks if DKG is final.
+ IsDKGFinal(round uint64) bool
+
+ // IsDKGSuccess checks if DKG is success.
+ IsDKGSuccess(round uint64) bool
+
+ // DKGResetCount returns the reset count for DKG of given round.
+ DKGResetCount(round uint64) uint64
+
+ // Get the begin height of a round.
+ GetRoundHeight(round uint64) uint64
+}
+
+// RoundEventRetryHandlerGenerator generates a handler to common.Event, which
+// would register itself to retry next round validation if round event is not
+// triggered.
+func RoundEventRetryHandlerGenerator(
+ rEvt *RoundEvent, hEvt *common.Event) func(uint64) {
+ var hEvtHandler func(uint64)
+ hEvtHandler = func(h uint64) {
+ if rEvt.ValidateNextRound(h) == 0 {
+ // Retry until at least one round event is triggered.
+ hEvt.RegisterHeight(h+1, hEvtHandler)
+ }
+ }
+ return hEvtHandler
+}
+
+// RoundEvent would be triggered when either:
+// - the next DKG set setup is ready.
+// - the next DKG set setup is failed, and previous DKG set already reset the
+// CRS.
+type RoundEvent struct {
+ gov governanceAccessor
+ logger common.Logger
+ lock sync.Mutex
+ handlers []roundEventFn
+ config RoundBasedConfig
+ lastTriggeredRound uint64
+ lastTriggeredResetCount uint64
+ roundShift uint64
+ gpkInvalid bool
+ ctx context.Context
+ ctxCancel context.CancelFunc
+}
+
+// NewRoundEvent creates an RoundEvent instance.
+func NewRoundEvent(parentCtx context.Context, gov governanceAccessor,
+ logger common.Logger, initPos types.Position, roundShift uint64) (
+ *RoundEvent, error) {
+ // We need to generate valid ending block height of this round (taken
+ // DKG reset count into consideration).
+ logger.Info("new RoundEvent", "position", initPos, "shift", roundShift)
+ initConfig := GetConfigWithPanic(gov, initPos.Round, logger)
+ e := &RoundEvent{
+ gov: gov,
+ logger: logger,
+ lastTriggeredRound: initPos.Round,
+ roundShift: roundShift,
+ }
+ e.ctx, e.ctxCancel = context.WithCancel(parentCtx)
+ e.config = RoundBasedConfig{}
+ e.config.SetupRoundBasedFields(initPos.Round, initConfig)
+ e.config.SetRoundBeginHeight(GetRoundHeight(gov, initPos.Round))
+ // Make sure the DKG reset count in current governance can cover the initial
+ // block height.
+ if initPos.Height >= types.GenesisHeight {
+ resetCount := gov.DKGResetCount(initPos.Round + 1)
+ remains := resetCount
+ for ; remains > 0 && !e.config.Contains(initPos.Height); remains-- {
+ e.config.ExtendLength()
+ }
+ if !e.config.Contains(initPos.Height) {
+ return nil, ErrUnmatchedBlockHeightWithConfig{
+ round: initPos.Round,
+ reset: resetCount,
+ blockHeight: initPos.Height,
+ }
+ }
+ e.lastTriggeredResetCount = resetCount - remains
+ }
+ return e, nil
+}
+
+// Register a handler to be called when new round is confirmed or new DKG reset
+// is detected.
+//
+// The earlier registered handler has higher priority.
+func (e *RoundEvent) Register(h roundEventFn) {
+ e.lock.Lock()
+ defer e.lock.Unlock()
+ e.handlers = append(e.handlers, h)
+}
+
+// TriggerInitEvent triggers event from the initial setting.
+func (e *RoundEvent) TriggerInitEvent() {
+ e.lock.Lock()
+ defer e.lock.Unlock()
+ events := []RoundEventParam{RoundEventParam{
+ Round: e.lastTriggeredRound,
+ Reset: e.lastTriggeredResetCount,
+ BeginHeight: e.config.LastPeriodBeginHeight(),
+ CRS: GetCRSWithPanic(e.gov, e.lastTriggeredRound, e.logger),
+ Config: GetConfigWithPanic(e.gov, e.lastTriggeredRound, e.logger),
+ }}
+ for _, h := range e.handlers {
+ h(events)
+ }
+}
+
+// ValidateNextRound validate if the DKG set for next round is ready to go or
+// failed to setup, all registered handlers would be called once some decision
+// is made on chain.
+//
+// The count of triggered events would be returned.
+func (e *RoundEvent) ValidateNextRound(blockHeight uint64) (count uint) {
+ // To make triggers continuous and sequential, the next validation should
+ // wait for previous one finishing. That's why I use mutex here directly.
+ var events []RoundEventParam
+ e.lock.Lock()
+ defer e.lock.Unlock()
+ e.logger.Trace("ValidateNextRound",
+ "height", blockHeight,
+ "round", e.lastTriggeredRound,
+ "count", e.lastTriggeredResetCount)
+ defer func() {
+ count = uint(len(events))
+ if count == 0 {
+ return
+ }
+ for _, h := range e.handlers {
+ // To make sure all handlers receive triggers sequentially, we can't
+ // raise go routines here.
+ h(events)
+ }
+ }()
+ var (
+ triggered bool
+ param RoundEventParam
+ beginHeight = blockHeight
+ startRound = e.lastTriggeredRound
+ )
+ for {
+ param, triggered = e.check(beginHeight, startRound)
+ if !triggered {
+ break
+ }
+ events = append(events, param)
+ beginHeight = param.BeginHeight
+ }
+ return
+}
+
+func (e *RoundEvent) check(blockHeight, startRound uint64) (
+ param RoundEventParam, triggered bool) {
+ defer func() {
+ if !triggered {
+ return
+ }
+ // A simple assertion to make sure we didn't pick the wrong round.
+ if e.config.RoundID() != e.lastTriggeredRound {
+ panic(fmt.Errorf("Triggered round not matched: %d, %d",
+ e.config.RoundID(), e.lastTriggeredRound))
+ }
+ param.Round = e.lastTriggeredRound
+ param.Reset = e.lastTriggeredResetCount
+ param.BeginHeight = e.config.LastPeriodBeginHeight()
+ param.CRS = GetCRSWithPanic(e.gov, e.lastTriggeredRound, e.logger)
+ param.Config = GetConfigWithPanic(e.gov, e.lastTriggeredRound, e.logger)
+ e.logger.Info("New RoundEvent triggered",
+ "round", e.lastTriggeredRound,
+ "reset", e.lastTriggeredResetCount,
+ "begin-height", e.config.LastPeriodBeginHeight(),
+ "crs", param.CRS.String()[:6],
+ )
+ }()
+ nextRound := e.lastTriggeredRound + 1
+ if nextRound >= startRound+e.roundShift {
+ // Avoid access configuration newer than last confirmed one over
+ // 'roundShift' rounds. Fullnode might crash if we access it before it
+ // knows.
+ return
+ }
+ nextCfg := GetConfigWithPanic(e.gov, nextRound, e.logger)
+ resetCount := e.gov.DKGResetCount(nextRound)
+ if resetCount > e.lastTriggeredResetCount {
+ e.lastTriggeredResetCount++
+ e.config.ExtendLength()
+ e.gpkInvalid = false
+ triggered = true
+ return
+ }
+ if e.gpkInvalid {
+ // We know that DKG already failed, now wait for the DKG set from
+ // previous round to reset DKG and don't have to reconstruct the
+ // group public key again.
+ return
+ }
+ if nextRound >= dkgDelayRound {
+ var ok bool
+ ok, e.gpkInvalid = IsDKGValid(
+ e.gov, e.logger, nextRound, e.lastTriggeredResetCount)
+ if !ok {
+ return
+ }
+ }
+ // The DKG set for next round is well prepared.
+ e.lastTriggeredRound = nextRound
+ e.lastTriggeredResetCount = 0
+ e.gpkInvalid = false
+ rCfg := RoundBasedConfig{}
+ rCfg.SetupRoundBasedFields(nextRound, nextCfg)
+ rCfg.AppendTo(e.config)
+ e.config = rCfg
+ triggered = true
+ return
+}
+
+// Stop the event source and block until last trigger returns.
+func (e *RoundEvent) Stop() {
+ e.ctxCancel()
+}
+
+// LastPeriod returns block height related info of the last period, including
+// begin height and round length.
+func (e *RoundEvent) LastPeriod() (begin uint64, length uint64) {
+ e.lock.Lock()
+ defer e.lock.Unlock()
+ begin = e.config.LastPeriodBeginHeight()
+ length = e.config.RoundEndHeight() - e.config.LastPeriodBeginHeight()
+ return
+}
diff --git a/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/signer.go b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/signer.go
new file mode 100644
index 000000000..9128e264c
--- /dev/null
+++ b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/signer.go
@@ -0,0 +1,154 @@
+// Copyright 2018 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package utils
+
+import (
+ "errors"
+
+ "github.com/byzantine-lab/dexon-consensus/common"
+ "github.com/byzantine-lab/dexon-consensus/core/crypto"
+ "github.com/byzantine-lab/dexon-consensus/core/types"
+ typesDKG "github.com/byzantine-lab/dexon-consensus/core/types/dkg"
+)
+
+// Errors for signer.
+var (
+ ErrInvalidProposerID = errors.New("invalid proposer id")
+ ErrIncorrectHash = errors.New("hash of block is incorrect")
+ ErrIncorrectSignature = errors.New("signature of block is incorrect")
+ ErrNoBLSSigner = errors.New("bls signer not set")
+)
+
+type blsSigner func(round uint64, hash common.Hash) (crypto.Signature, error)
+
+// Signer signs a segment of data.
+type Signer struct {
+ prvKey crypto.PrivateKey
+ pubKey crypto.PublicKey
+ proposerID types.NodeID
+ blsSign blsSigner
+}
+
+// NewSigner constructs an Signer instance.
+func NewSigner(prvKey crypto.PrivateKey) (s *Signer) {
+ s = &Signer{
+ prvKey: prvKey,
+ pubKey: prvKey.PublicKey(),
+ }
+ s.proposerID = types.NewNodeID(s.pubKey)
+ return
+}
+
+// SetBLSSigner for signing CRSSignature
+func (s *Signer) SetBLSSigner(signer blsSigner) {
+ s.blsSign = signer
+}
+
+// SignBlock signs a types.Block.
+func (s *Signer) SignBlock(b *types.Block) (err error) {
+ b.ProposerID = s.proposerID
+ b.PayloadHash = crypto.Keccak256Hash(b.Payload)
+ if b.Hash, err = HashBlock(b); err != nil {
+ return
+ }
+ if b.Signature, err = s.prvKey.Sign(b.Hash); err != nil {
+ return
+ }
+ return
+}
+
+// SignVote signs a types.Vote.
+func (s *Signer) SignVote(v *types.Vote) (err error) {
+ v.ProposerID = s.proposerID
+ v.Signature, err = s.prvKey.Sign(HashVote(v))
+ return
+}
+
+// SignCRS signs CRS signature of types.Block.
+func (s *Signer) SignCRS(b *types.Block, crs common.Hash) (err error) {
+ if b.ProposerID != s.proposerID {
+ err = ErrInvalidProposerID
+ return
+ }
+ if b.Position.Round < dkgDelayRound {
+ hash := hashCRS(b, crs)
+ b.CRSSignature = crypto.Signature{
+ Type: "bls",
+ Signature: hash[:],
+ }
+ return
+ }
+ if s.blsSign == nil {
+ err = ErrNoBLSSigner
+ return
+ }
+ b.CRSSignature, err = s.blsSign(b.Position.Round, hashCRS(b, crs))
+ return
+}
+
+// SignDKGComplaint signs a DKG complaint.
+func (s *Signer) SignDKGComplaint(complaint *typesDKG.Complaint) (err error) {
+ complaint.ProposerID = s.proposerID
+ complaint.Signature, err = s.prvKey.Sign(hashDKGComplaint(complaint))
+ return
+}
+
+// SignDKGMasterPublicKey signs a DKG master public key.
+func (s *Signer) SignDKGMasterPublicKey(
+ mpk *typesDKG.MasterPublicKey) (err error) {
+ mpk.ProposerID = s.proposerID
+ mpk.Signature, err = s.prvKey.Sign(hashDKGMasterPublicKey(mpk))
+ return
+}
+
+// SignDKGPrivateShare signs a DKG private share.
+func (s *Signer) SignDKGPrivateShare(
+ prvShare *typesDKG.PrivateShare) (err error) {
+ prvShare.ProposerID = s.proposerID
+ prvShare.Signature, err = s.prvKey.Sign(hashDKGPrivateShare(prvShare))
+ return
+}
+
+// SignDKGPartialSignature signs a DKG partial signature.
+func (s *Signer) SignDKGPartialSignature(
+ pSig *typesDKG.PartialSignature) (err error) {
+ pSig.ProposerID = s.proposerID
+ pSig.Signature, err = s.prvKey.Sign(hashDKGPartialSignature(pSig))
+ return
+}
+
+// SignDKGMPKReady signs a DKG ready message.
+func (s *Signer) SignDKGMPKReady(ready *typesDKG.MPKReady) (err error) {
+ ready.ProposerID = s.proposerID
+ ready.Signature, err = s.prvKey.Sign(hashDKGMPKReady(ready))
+ return
+}
+
+// SignDKGFinalize signs a DKG finalize message.
+func (s *Signer) SignDKGFinalize(final *typesDKG.Finalize) (err error) {
+ final.ProposerID = s.proposerID
+ final.Signature, err = s.prvKey.Sign(hashDKGFinalize(final))
+ return
+}
+
+// SignDKGSuccess signs a DKG success message.
+func (s *Signer) SignDKGSuccess(success *typesDKG.Success) (err error) {
+ success.ProposerID = s.proposerID
+ success.Signature, err = s.prvKey.Sign(hashDKGSuccess(success))
+ return
+}
diff --git a/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/utils.go b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/utils.go
new file mode 100644
index 000000000..6ff5bb62f
--- /dev/null
+++ b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/utils.go
@@ -0,0 +1,207 @@
+// Copyright 2018 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package utils
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/byzantine-lab/dexon-consensus/common"
+ "github.com/byzantine-lab/dexon-consensus/core/types"
+ typesDKG "github.com/byzantine-lab/dexon-consensus/core/types/dkg"
+)
+
+var dkgDelayRound uint64
+
+// SetDKGDelayRound sets the variable.
+func SetDKGDelayRound(delay uint64) {
+ dkgDelayRound = delay
+}
+
+type configAccessor interface {
+ Configuration(round uint64) *types.Config
+}
+
+// GetConfigWithPanic is a helper to access configs, and panic when config for
+// that round is not ready yet.
+func GetConfigWithPanic(accessor configAccessor, round uint64,
+ logger common.Logger) *types.Config {
+ if logger != nil {
+ logger.Debug("Calling Governance.Configuration", "round", round)
+ }
+ c := accessor.Configuration(round)
+ if c == nil {
+ panic(fmt.Errorf("configuration is not ready %v", round))
+ }
+ return c
+}
+
+type crsAccessor interface {
+ CRS(round uint64) common.Hash
+}
+
+// GetCRSWithPanic is a helper to access CRS, and panic when CRS for that
+// round is not ready yet.
+func GetCRSWithPanic(accessor crsAccessor, round uint64,
+ logger common.Logger) common.Hash {
+ if logger != nil {
+ logger.Debug("Calling Governance.CRS", "round", round)
+ }
+ crs := accessor.CRS(round)
+ if (crs == common.Hash{}) {
+ panic(fmt.Errorf("CRS is not ready %v", round))
+ }
+ return crs
+}
+
+// VerifyDKGComplaint verifies if its a valid DKGCompliant.
+func VerifyDKGComplaint(
+ complaint *typesDKG.Complaint, mpk *typesDKG.MasterPublicKey) (bool, error) {
+ ok, err := VerifyDKGComplaintSignature(complaint)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, nil
+ }
+ if complaint.IsNack() {
+ return true, nil
+ }
+ if complaint.Round != mpk.Round {
+ return false, nil
+ }
+ ok, err = VerifyDKGMasterPublicKeySignature(mpk)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, nil
+ }
+ ok, err = mpk.PublicKeyShares.VerifyPrvShare(
+ typesDKG.NewID(complaint.PrivateShare.ReceiverID),
+ &complaint.PrivateShare.PrivateShare)
+ if err != nil {
+ return false, err
+ }
+ return !ok, nil
+}
+
+// LaunchDummyReceiver launches a go routine to receive from the receive
+// channel of a network module. An context is required to stop the go routine
+// automatically. An optinal message handler could be provided.
+func LaunchDummyReceiver(
+ ctx context.Context, recv <-chan types.Msg, handler func(types.Msg)) (
+ context.CancelFunc, <-chan struct{}) {
+ var (
+ dummyCtx, dummyCancel = context.WithCancel(ctx)
+ finishedChan = make(chan struct{}, 1)
+ )
+ go func() {
+ defer func() {
+ finishedChan <- struct{}{}
+ }()
+ loop:
+ for {
+ select {
+ case <-dummyCtx.Done():
+ break loop
+ case v, ok := <-recv:
+ if !ok {
+ panic(fmt.Errorf(
+ "receive channel is closed before dummy receiver"))
+ }
+ if handler != nil {
+ handler(v)
+ }
+ }
+ }
+ }()
+ return dummyCancel, finishedChan
+}
+
+// GetDKGThreshold return expected threshold for given DKG set size.
+func GetDKGThreshold(config *types.Config) int {
+ return int(config.NotarySetSize*2/3) + 1
+}
+
+// GetDKGValidThreshold return threshold for DKG set to considered valid.
+func GetDKGValidThreshold(config *types.Config) int {
+ return int(config.NotarySetSize * 5 / 6)
+}
+
+// GetBAThreshold return threshold for BA votes.
+func GetBAThreshold(config *types.Config) int {
+ return int(config.NotarySetSize*2/3 + 1)
+}
+
+// GetNextRoundValidationHeight returns the block height to check if the next
+// round is ready.
+func GetNextRoundValidationHeight(begin, length uint64) uint64 {
+ return begin + length*9/10
+}
+
+// GetRoundHeight wraps the workaround for the round height logic in fullnode.
+func GetRoundHeight(accessor interface{}, round uint64) uint64 {
+ type roundHeightAccessor interface {
+ GetRoundHeight(round uint64) uint64
+ }
+ accessorInst := accessor.(roundHeightAccessor)
+ height := accessorInst.GetRoundHeight(round)
+ if round == 0 && height < types.GenesisHeight {
+ return types.GenesisHeight
+ }
+ return height
+}
+
+// IsDKGValid check if DKG is correctly prepared.
+func IsDKGValid(
+ gov governanceAccessor, logger common.Logger, round, reset uint64) (
+ valid bool, gpkInvalid bool) {
+ if !gov.IsDKGFinal(round) {
+ logger.Debug("DKG is not final", "round", round, "reset", reset)
+ return
+ }
+ if !gov.IsDKGSuccess(round) {
+ logger.Debug("DKG is not successful", "round", round, "reset", reset)
+ return
+ }
+ cfg := GetConfigWithPanic(gov, round, logger)
+ gpk, err := typesDKG.NewGroupPublicKey(
+ round,
+ gov.DKGMasterPublicKeys(round),
+ gov.DKGComplaints(round),
+ GetDKGThreshold(cfg))
+ if err != nil {
+ logger.Debug("Group public key setup failed",
+ "round", round,
+ "reset", reset,
+ "error", err)
+ gpkInvalid = true
+ return
+ }
+ if len(gpk.QualifyNodeIDs) < GetDKGValidThreshold(cfg) {
+ logger.Debug("Group public key threshold not reach",
+ "round", round,
+ "reset", reset,
+ "qualified", len(gpk.QualifyNodeIDs))
+ gpkInvalid = true
+ return
+ }
+ valid = true
+ return
+}
diff --git a/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/vote-filter.go b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/vote-filter.go
new file mode 100644
index 000000000..556c2489a
--- /dev/null
+++ b/vendor/github.com/byzantine-lab/dexon-consensus/core/utils/vote-filter.go
@@ -0,0 +1,72 @@
+// Copyright 2019 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package utils
+
+import (
+ "github.com/byzantine-lab/dexon-consensus/core/types"
+)
+
+// VoteFilter filters votes that are useless for now.
+// To maximize performance, this structure is not thread-safe and will never be.
+type VoteFilter struct {
+ Voted map[types.VoteHeader]struct{}
+ Position types.Position
+ LockIter uint64
+ Period uint64
+ Confirm bool
+}
+
+// NewVoteFilter creates a new vote filter instance.
+func NewVoteFilter() *VoteFilter {
+ return &VoteFilter{
+ Voted: make(map[types.VoteHeader]struct{}),
+ }
+}
+
+// Filter checks if the vote should be filtered out.
+func (vf *VoteFilter) Filter(vote *types.Vote) bool {
+ if vote.Type == types.VoteInit {
+ return true
+ }
+ if vote.Position.Older(vf.Position) {
+ return true
+ } else if vote.Position.Newer(vf.Position) {
+ // It's impossible to check the vote of other height.
+ return false
+ }
+ if vf.Confirm {
+ return true
+ }
+ if vote.Type == types.VotePreCom && vote.Period < vf.LockIter {
+ return true
+ }
+ if vote.Type == types.VoteCom &&
+ vote.Period < vf.Period &&
+ vote.BlockHash == types.SkipBlockHash {
+ return true
+ }
+ if _, exist := vf.Voted[vote.VoteHeader]; exist {
+ return true
+ }
+ return false
+}
+
+// AddVote to the filter so the same vote will be filtered.
+func (vf *VoteFilter) AddVote(vote *types.Vote) {
+ vf.Voted[vote.VoteHeader] = struct{}{}
+}