aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/crypto.go99
-rw-r--r--core/crypto_test.go53
-rw-r--r--core/dkg-tsig-protocol.go341
-rw-r--r--core/dkg-tsig-protocol_test.go191
-rw-r--r--core/types/dkg.go19
5 files changed, 698 insertions, 5 deletions
diff --git a/core/crypto.go b/core/crypto.go
index 402fd2e..8fcb118 100644
--- a/core/crypto.go
+++ b/core/crypto.go
@@ -143,3 +143,102 @@ func hashPosition(position types.Position) common.Hash {
binaryHeight,
)
}
+
+func hashDKGPrivateShare(prvShare *types.DKGPrivateShare) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, prvShare.Round)
+
+ return crypto.Keccak256Hash(
+ prvShare.ProposerID.Hash[:],
+ binaryRound,
+ prvShare.PrivateShare.Bytes(),
+ )
+}
+
+func verifyDKGPrivateShareSignature(
+ prvShare *types.DKGPrivateShare, sigToPub SigToPubFn) (bool, error) {
+ hash := hashDKGPrivateShare(prvShare)
+ pubKey, err := sigToPub(hash, prvShare.Signature)
+ if err != nil {
+ return false, err
+ }
+ if prvShare.ProposerID != types.NewValidatorID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+func hashDKGMasterPublicKey(mpk *types.DKGMasterPublicKey) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, mpk.Round)
+
+ return crypto.Keccak256Hash(
+ mpk.ProposerID.Hash[:],
+ mpk.DKGID.GetLittleEndian(),
+ mpk.PublicKeyShares.MasterKeyBytes(),
+ binaryRound,
+ )
+}
+
+func verifyDKGMasterPublicKeySignature(
+ mpk *types.DKGMasterPublicKey, sigToPub SigToPubFn) (bool, error) {
+ hash := hashDKGMasterPublicKey(mpk)
+ pubKey, err := sigToPub(hash, mpk.Signature)
+ if err != nil {
+ return false, err
+ }
+ if mpk.ProposerID != types.NewValidatorID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+func hashDKGComplaint(complaint *types.DKGComplaint) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, complaint.Round)
+
+ hashPrvShare := hashDKGPrivateShare(&complaint.PrivateShare)
+
+ return crypto.Keccak256Hash(
+ complaint.ProposerID.Hash[:],
+ binaryRound,
+ hashPrvShare[:],
+ )
+}
+
+func verifyDKGComplaintSignature(
+ complaint *types.DKGComplaint, sigToPub SigToPubFn) (bool, error) {
+ hash := hashDKGComplaint(complaint)
+ pubKey, err := sigToPub(hash, complaint.Signature)
+ if err != nil {
+ return false, err
+ }
+ if complaint.ProposerID != types.NewValidatorID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
+func hashDKGPartialSignature(psig *types.DKGPartialSignature) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, psig.Round)
+
+ return crypto.Keccak256Hash(
+ psig.ProposerID.Hash[:],
+ binaryRound,
+ psig.PartialSignature[:],
+ )
+}
+
+func verifyDKGPartialSignatureSignature(
+ psig *types.DKGPartialSignature, sigToPub SigToPubFn) (bool, error) {
+ hash := hashDKGPartialSignature(psig)
+ pubKey, err := sigToPub(hash, psig.Signature)
+ if err != nil {
+ return false, err
+ }
+ if psig.ProposerID != types.NewValidatorID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
diff --git a/core/crypto_test.go b/core/crypto_test.go
index b96b0bd..7f3c3f3 100644
--- a/core/crypto_test.go
+++ b/core/crypto_test.go
@@ -24,6 +24,7 @@ import (
"github.com/dexon-foundation/dexon-consensus-core/common"
"github.com/dexon-foundation/dexon-consensus-core/core/types"
"github.com/dexon-foundation/dexon-consensus-core/crypto"
+ "github.com/dexon-foundation/dexon-consensus-core/crypto/dkg"
"github.com/dexon-foundation/dexon-consensus-core/crypto/eth"
"github.com/stretchr/testify/suite"
)
@@ -210,6 +211,58 @@ func (s *CryptoTestSuite) TestCRSSignature() {
s.False(verifyCRSSignature(block, crs, eth.SigToPub))
}
+func (s *CryptoTestSuite) TestDKGSignature() {
+ prv, err := eth.NewPrivateKey()
+ s.Require().Nil(err)
+ vID := types.NewValidatorID(prv.PublicKey())
+ prvShare := &types.DKGPrivateShare{
+ ProposerID: vID,
+ Round: 5,
+ PrivateShare: *dkg.NewPrivateKey(),
+ }
+ prvShare.Signature, err = prv.Sign(hashDKGPrivateShare(prvShare))
+ s.Require().Nil(err)
+ s.True(verifyDKGPrivateShareSignature(prvShare, eth.SigToPub))
+ prvShare.Round++
+ s.False(verifyDKGPrivateShareSignature(prvShare, eth.SigToPub))
+
+ id := dkg.NewID([]byte{13})
+ _, pkShare := dkg.NewPrivateKeyShares(1)
+ mpk := &types.DKGMasterPublicKey{
+ ProposerID: vID,
+ Round: 5,
+ DKGID: id,
+ PublicKeyShares: *pkShare,
+ }
+ mpk.Signature, err = prv.Sign(hashDKGMasterPublicKey(mpk))
+ s.Require().Nil(err)
+ s.True(verifyDKGMasterPublicKeySignature(mpk, eth.SigToPub))
+ mpk.Round++
+ s.False(verifyDKGMasterPublicKeySignature(mpk, eth.SigToPub))
+
+ complaint := &types.DKGComplaint{
+ ProposerID: vID,
+ Round: 5,
+ PrivateShare: *prvShare,
+ }
+ complaint.Signature, err = prv.Sign(hashDKGComplaint(complaint))
+ s.Require().Nil(err)
+ s.True(verifyDKGComplaintSignature(complaint, eth.SigToPub))
+ complaint.Round++
+ s.False(verifyDKGComplaintSignature(complaint, eth.SigToPub))
+
+ sig := &types.DKGPartialSignature{
+ ProposerID: vID,
+ Round: 5,
+ PartialSignature: dkg.PartialSignature{},
+ }
+ sig.Signature, err = prv.Sign(hashDKGPartialSignature(sig))
+ s.Require().Nil(err)
+ s.True(verifyDKGPartialSignatureSignature(sig, eth.SigToPub))
+ sig.Round++
+ s.False(verifyDKGPartialSignatureSignature(sig, eth.SigToPub))
+}
+
func TestCrypto(t *testing.T) {
suite.Run(t, new(CryptoTestSuite))
}
diff --git a/core/dkg-tsig-protocol.go b/core/dkg-tsig-protocol.go
new file mode 100644
index 0000000..c549ecb
--- /dev/null
+++ b/core/dkg-tsig-protocol.go
@@ -0,0 +1,341 @@
+// Copyright 2018 The dexon-consensus-core Authors
+// This file is part of the dexon-consensus-core library.
+//
+// The dexon-consensus-core 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-core 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-core library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package core
+
+import (
+ "fmt"
+
+ "github.com/dexon-foundation/dexon-consensus-core/common"
+ "github.com/dexon-foundation/dexon-consensus-core/core/types"
+ "github.com/dexon-foundation/dexon-consensus-core/crypto"
+ "github.com/dexon-foundation/dexon-consensus-core/crypto/dkg"
+)
+
+// Errors for dkg module.
+var (
+ ErrNotDKGParticipant = fmt.Errorf(
+ "not a DKG participant")
+ ErrNotQualifyDKGParticipant = fmt.Errorf(
+ "not a qualified DKG participant")
+ ErrIDShareNotFound = fmt.Errorf(
+ "private share not found for specific ID")
+ ErrNotReachThreshold = fmt.Errorf(
+ "threshold not reach")
+ ErrIncorrectPrivateShareSignature = fmt.Errorf(
+ "incorrect private share signature")
+ ErrIncorrectPartialSignatureSignature = fmt.Errorf(
+ "incorrect partialSignature signature")
+ ErrIncorrectPartialSignature = fmt.Errorf(
+ "incorrect partialSignature")
+ ErrNotEnoughtPartialSignatures = fmt.Errorf(
+ "not enough of partial signatures")
+)
+
+type dkgComplaintReceiver interface {
+ // ProposeDKGComplaint proposes a DKGComplaint.
+ ProposeDKGComplaint(complaint *types.DKGComplaint)
+
+ // ProposeDKGMasterPublicKey propose a DKGMasterPublicKey.
+ ProposeDKGMasterPublicKey(mpk *types.DKGMasterPublicKey)
+
+ // ProposeDKGPrivateShare propose a DKGPrivateShare.
+ ProposeDKGPrivateShare(to types.ValidatorID, prv *types.DKGPrivateShare)
+}
+
+type dkgProtocol struct {
+ ID types.ValidatorID
+ recv dkgComplaintReceiver
+ round uint64
+ threshold int
+ sigToPub SigToPubFn
+ idMap map[types.ValidatorID]dkg.ID
+ mpkMap map[types.ValidatorID]*dkg.PublicKeyShares
+ masterPrivateShare *dkg.PrivateKeyShares
+ prvShares *dkg.PrivateKeyShares
+}
+
+type dkgShareSecret struct {
+ privateKey *dkg.PrivateKey
+}
+
+type dkgGroupPublicKey struct {
+ round uint64
+ qualifyIDs dkg.IDs
+ idMap map[types.ValidatorID]dkg.ID
+ publicKeys map[types.ValidatorID]*dkg.PublicKey
+ groupPublicKey *dkg.PublicKey
+ threshold int
+ sigToPub SigToPubFn
+}
+
+type tsigProtocol struct {
+ groupPublicKey *dkgGroupPublicKey
+ sigs map[dkg.ID]dkg.PartialSignature
+ threshold int
+}
+
+func newDKGID(ID types.ValidatorID) dkg.ID {
+ return dkg.NewID(ID.Hash[:])
+}
+
+func newDKGProtocol(
+ ID types.ValidatorID,
+ recv dkgComplaintReceiver,
+ round uint64,
+ threshold int,
+ sigToPub SigToPubFn) *dkgProtocol {
+
+ prvShare, pubShare := dkg.NewPrivateKeyShares(threshold)
+
+ recv.ProposeDKGMasterPublicKey(&types.DKGMasterPublicKey{
+ ProposerID: ID,
+ Round: round,
+ DKGID: newDKGID(ID),
+ PublicKeyShares: *pubShare,
+ })
+
+ return &dkgProtocol{
+ ID: ID,
+ recv: recv,
+ round: round,
+ threshold: threshold,
+ sigToPub: sigToPub,
+ idMap: make(map[types.ValidatorID]dkg.ID),
+ mpkMap: make(map[types.ValidatorID]*dkg.PublicKeyShares),
+ masterPrivateShare: prvShare,
+ prvShares: dkg.NewEmptyPrivateKeyShares(),
+ }
+}
+
+func (d *dkgProtocol) processMasterPublicKeys(
+ mpks []*types.DKGMasterPublicKey) error {
+ d.idMap = make(map[types.ValidatorID]dkg.ID, len(mpks))
+ d.mpkMap = make(map[types.ValidatorID]*dkg.PublicKeyShares, len(mpks))
+ ids := make(dkg.IDs, len(mpks))
+ for i := range mpks {
+ vID := mpks[i].ProposerID
+ d.idMap[vID] = mpks[i].DKGID
+ d.mpkMap[vID] = &mpks[i].PublicKeyShares
+ ids[i] = mpks[i].DKGID
+ }
+ d.masterPrivateShare.SetParticipants(ids)
+ for _, mpk := range mpks {
+ share, ok := d.masterPrivateShare.Share(mpk.DKGID)
+ if !ok {
+ return ErrIDShareNotFound
+ }
+ d.recv.ProposeDKGPrivateShare(mpk.ProposerID, &types.DKGPrivateShare{
+ ProposerID: d.ID,
+ Round: d.round,
+ PrivateShare: *share,
+ })
+ }
+ return nil
+}
+
+func (d *dkgProtocol) sanityCheck(prvShare *types.DKGPrivateShare) error {
+ if _, exist := d.idMap[prvShare.ProposerID]; !exist {
+ return ErrNotDKGParticipant
+ }
+ ok, err := verifyDKGPrivateShareSignature(prvShare, d.sigToPub)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return ErrIncorrectPrivateShareSignature
+ }
+ return nil
+}
+
+func (d *dkgProtocol) processPrivateShare(
+ prvShare *types.DKGPrivateShare) error {
+ if d.round != prvShare.Round {
+ return nil
+ }
+ self, exist := d.idMap[d.ID]
+ // This validator is not a DKG participant, ignore the private share.
+ if !exist {
+ return nil
+ }
+ if err := d.sanityCheck(prvShare); err != nil {
+ return err
+ }
+ mpk := d.mpkMap[prvShare.ProposerID]
+ ok, err := mpk.VerifyPrvShare(self, &prvShare.PrivateShare)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ complaint := &types.DKGComplaint{
+ ProposerID: d.ID,
+ Round: d.round,
+ PrivateShare: *prvShare,
+ }
+ d.recv.ProposeDKGComplaint(complaint)
+ } else {
+ sender := d.idMap[prvShare.ProposerID]
+ if err := d.prvShares.AddShare(sender, &prvShare.PrivateShare); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (d *dkgProtocol) recoverShareSecret(qualifyIDs dkg.IDs) (
+ *dkgShareSecret, error) {
+ if len(qualifyIDs) <= d.threshold {
+ return nil, ErrNotReachThreshold
+ }
+ prvKey, err := d.prvShares.RecoverPrivateKey(qualifyIDs)
+ if err != nil {
+ return nil, err
+ }
+ return &dkgShareSecret{
+ privateKey: prvKey,
+ }, nil
+}
+
+func (ss *dkgShareSecret) sign(hash common.Hash) dkg.PartialSignature {
+ // DKG sign will always success.
+ sig, _ := ss.privateKey.Sign(hash)
+ return dkg.PartialSignature(sig)
+}
+
+func newDKGGroupPublicKey(
+ round uint64,
+ mpks []*types.DKGMasterPublicKey, complaints []*types.DKGComplaint,
+ threshold int, sigToPub SigToPubFn) (
+ *dkgGroupPublicKey, error) {
+ // Calculate qualify members.
+ complaintsByID := map[types.ValidatorID]int{}
+ for _, complaint := range complaints {
+ complaintsByID[complaint.PrivateShare.ProposerID]++
+ }
+ disqualifyIDs := map[types.ValidatorID]struct{}{}
+ for vID, num := range complaintsByID {
+ if num > threshold {
+ disqualifyIDs[vID] = struct{}{}
+ }
+ }
+ qualifyIDs := make(dkg.IDs, 0, len(mpks)-len(disqualifyIDs))
+ mpkMap := make(map[dkg.ID]*types.DKGMasterPublicKey, cap(qualifyIDs))
+ idMap := make(map[types.ValidatorID]dkg.ID)
+ for _, mpk := range mpks {
+ if _, exist := disqualifyIDs[mpk.ProposerID]; exist {
+ continue
+ }
+ mpkMap[mpk.DKGID] = mpk
+ idMap[mpk.ProposerID] = mpk.DKGID
+ qualifyIDs = append(qualifyIDs, mpk.DKGID)
+ }
+ // Recover qualify members' public key.
+ pubKeys := make(map[types.ValidatorID]*dkg.PublicKey, len(qualifyIDs))
+ for _, recvID := range qualifyIDs {
+ pubShares := dkg.NewEmptyPublicKeyShares()
+ for _, id := range qualifyIDs {
+ pubShare, err := mpkMap[id].PublicKeyShares.Share(recvID)
+ if err != nil {
+ return nil, err
+ }
+ if err := pubShares.AddShare(id, pubShare); err != nil {
+ return nil, err
+ }
+ }
+ pubKey, err := pubShares.RecoverPublicKey(qualifyIDs)
+ if err != nil {
+ return nil, err
+ }
+ pubKeys[mpkMap[recvID].ProposerID] = pubKey
+ }
+ // Recover Group Public Key.
+ pubShares := make([]*dkg.PublicKeyShares, 0, len(qualifyIDs))
+ for _, id := range qualifyIDs {
+ pubShares = append(pubShares, &mpkMap[id].PublicKeyShares)
+ }
+ groupPK := dkg.RecoverGroupPublicKey(pubShares)
+ return &dkgGroupPublicKey{
+ round: round,
+ qualifyIDs: qualifyIDs,
+ idMap: idMap,
+ publicKeys: pubKeys,
+ threshold: threshold,
+ groupPublicKey: groupPK,
+ sigToPub: sigToPub,
+ }, nil
+}
+
+func (gpk *dkgGroupPublicKey) verifySignature(
+ hash common.Hash, sig crypto.Signature) bool {
+ return gpk.groupPublicKey.VerifySignature(hash, sig)
+}
+
+func newTSigProtocol(gpk *dkgGroupPublicKey) *tsigProtocol {
+ return &tsigProtocol{
+ groupPublicKey: gpk,
+ sigs: make(map[dkg.ID]dkg.PartialSignature, gpk.threshold+1),
+ }
+}
+
+func (tsig *tsigProtocol) sanityCheck(psig *types.DKGPartialSignature) error {
+ _, exist := tsig.groupPublicKey.publicKeys[psig.ProposerID]
+ if !exist {
+ return ErrNotQualifyDKGParticipant
+ }
+ ok, err := verifyDKGPartialSignatureSignature(
+ psig, tsig.groupPublicKey.sigToPub)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return ErrIncorrectPartialSignatureSignature
+ }
+ return nil
+}
+
+func (tsig *tsigProtocol) processPartialSignature(
+ hash common.Hash, psig *types.DKGPartialSignature) error {
+ if psig.Round != tsig.groupPublicKey.round {
+ return nil
+ }
+ id, exist := tsig.groupPublicKey.idMap[psig.ProposerID]
+ if !exist {
+ return ErrNotQualifyDKGParticipant
+ }
+ if err := tsig.sanityCheck(psig); err != nil {
+ return err
+ }
+ pubKey := tsig.groupPublicKey.publicKeys[psig.ProposerID]
+ if !pubKey.VerifySignature(hash, crypto.Signature(psig.PartialSignature)) {
+ return ErrIncorrectPartialSignature
+ }
+ tsig.sigs[id] = psig.PartialSignature
+ return nil
+}
+
+func (tsig *tsigProtocol) signature() (crypto.Signature, error) {
+ if len(tsig.sigs) <= tsig.groupPublicKey.threshold {
+ return nil, ErrNotEnoughtPartialSignatures
+ }
+ ids := make(dkg.IDs, 0, len(tsig.sigs))
+ psigs := make([]dkg.PartialSignature, 0, len(tsig.sigs))
+ for id, psig := range tsig.sigs {
+ ids = append(ids, id)
+ psigs = append(psigs, psig)
+ }
+ return dkg.RecoverSignature(psigs, ids)
+}
diff --git a/core/dkg-tsig-protocol_test.go b/core/dkg-tsig-protocol_test.go
new file mode 100644
index 0000000..f1c0017
--- /dev/null
+++ b/core/dkg-tsig-protocol_test.go
@@ -0,0 +1,191 @@
+// Copyright 2018 The dexon-consensus-core Authors
+// This file is part of the dexon-consensus-core library.
+//
+// The dexon-consensus-core 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-core 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-core library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package core
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+
+ "github.com/dexon-foundation/dexon-consensus-core/core/test"
+ "github.com/dexon-foundation/dexon-consensus-core/core/types"
+ "github.com/dexon-foundation/dexon-consensus-core/crypto"
+ "github.com/dexon-foundation/dexon-consensus-core/crypto/dkg"
+ "github.com/dexon-foundation/dexon-consensus-core/crypto/eth"
+)
+
+type DKGTSIGProtocolTestSuite struct {
+ suite.Suite
+
+ vIDs types.ValidatorIDs
+ dkgIDs map[types.ValidatorID]dkg.ID
+ prvKeys map[types.ValidatorID]crypto.PrivateKey
+}
+
+type testDKGReceiver struct {
+ s *DKGTSIGProtocolTestSuite
+
+ prvKey crypto.PrivateKey
+ complaints map[types.ValidatorID]*types.DKGComplaint
+ mpk *types.DKGMasterPublicKey
+ prvShare map[types.ValidatorID]*types.DKGPrivateShare
+}
+
+func newTestDKGReceiver(
+ s *DKGTSIGProtocolTestSuite, prvKey crypto.PrivateKey) *testDKGReceiver {
+ return &testDKGReceiver{
+ s: s,
+ prvKey: prvKey,
+ complaints: make(map[types.ValidatorID]*types.DKGComplaint),
+ prvShare: make(map[types.ValidatorID]*types.DKGPrivateShare),
+ }
+}
+
+func (r *testDKGReceiver) ProposeDKGComplaint(complaint *types.DKGComplaint) {
+ var err error
+ complaint.Signature, err = r.prvKey.Sign(hashDKGComplaint(complaint))
+ r.s.Require().NoError(err)
+ r.complaints[complaint.ProposerID] = complaint
+}
+
+func (r *testDKGReceiver) ProposeDKGMasterPublicKey(
+ mpk *types.DKGMasterPublicKey) {
+ var err error
+ mpk.Signature, err = r.prvKey.Sign(hashDKGMasterPublicKey(mpk))
+ r.s.Require().NoError(err)
+ r.mpk = mpk
+}
+func (r *testDKGReceiver) ProposeDKGPrivateShare(
+ to types.ValidatorID, prv *types.DKGPrivateShare) {
+ var err error
+ prv.Signature, err = r.prvKey.Sign(hashDKGPrivateShare(prv))
+ r.s.Require().NoError(err)
+ r.prvShare[to] = prv
+}
+
+func (s *DKGTSIGProtocolTestSuite) setupDKGParticipants(n int) {
+ s.vIDs = make(types.ValidatorIDs, 0, n)
+ s.prvKeys = make(map[types.ValidatorID]crypto.PrivateKey, n)
+ s.dkgIDs = make(map[types.ValidatorID]dkg.ID)
+ ids := make(dkg.IDs, 0, n)
+ for i := 0; i < n; i++ {
+ prvKey, err := eth.NewPrivateKey()
+ s.Require().NoError(err)
+ vID := types.NewValidatorID(prvKey.PublicKey())
+ s.vIDs = append(s.vIDs, vID)
+ s.prvKeys[vID] = prvKey
+ id := dkg.NewID(vID.Hash[:])
+ ids = append(ids, id)
+ s.dkgIDs[vID] = id
+ }
+}
+
+// TestDKGTSIGProtocol will test the entire DKG+TISG protocol including
+// exchanging private shares, recovering share secret, creating partial sign and
+// recovering threshold signature.
+// All participants are good people in this test.
+func (s *DKGTSIGProtocolTestSuite) TestDKGTSIGProtocol() {
+ k := 3
+ n := 10
+ round := uint64(1)
+ gov, err := test.NewGovernance(5, 100)
+ s.Require().NoError(err)
+ s.setupDKGParticipants(n)
+
+ receivers := make(map[types.ValidatorID]*testDKGReceiver, n)
+ protocols := make(map[types.ValidatorID]*dkgProtocol, n)
+ for _, vID := range s.vIDs {
+ receivers[vID] = newTestDKGReceiver(s, s.prvKeys[vID])
+ protocols[vID] = newDKGProtocol(
+ vID,
+ receivers[vID],
+ round,
+ k,
+ eth.SigToPub,
+ )
+ s.Require().NotNil(receivers[vID].mpk)
+ }
+
+ for _, receiver := range receivers {
+ gov.AddDKGMasterPublicKey(receiver.mpk)
+ }
+
+ for _, protocol := range protocols {
+ s.Require().NoError(
+ protocol.processMasterPublicKeys(gov.DKGMasterPublicKeys(round)))
+ }
+
+ for _, receiver := range receivers {
+ s.Require().Len(receiver.prvShare, n)
+ for vID, prvShare := range receiver.prvShare {
+ s.Require().NoError(protocols[vID].processPrivateShare(prvShare))
+ }
+ }
+
+ for _, recv := range receivers {
+ s.Require().Len(recv.complaints, 0)
+ }
+
+ // DKG is fininished.
+ gpk, err := newDKGGroupPublicKey(round,
+ gov.DKGMasterPublicKeys(round), gov.DKGComplaints(round),
+ k, eth.SigToPub,
+ )
+ s.Require().NoError(err)
+ s.Require().Len(gpk.qualifyIDs, n)
+ qualifyIDs := make(map[dkg.ID]struct{}, len(gpk.qualifyIDs))
+ for _, id := range gpk.qualifyIDs {
+ qualifyIDs[id] = struct{}{}
+ }
+
+ shareSecrets := make(
+ map[types.ValidatorID]*dkgShareSecret, len(qualifyIDs))
+
+ for vID, protocol := range protocols {
+ _, exist := qualifyIDs[s.dkgIDs[vID]]
+ s.Require().True(exist)
+ var err error
+ shareSecrets[vID], err = protocol.recoverShareSecret(gpk.qualifyIDs)
+ s.Require().NoError(err)
+ }
+
+ tsig := newTSigProtocol(gpk)
+ msgHash := crypto.Keccak256Hash([]byte("🏖🍹"))
+ for vID, shareSecret := range shareSecrets {
+ psig := &types.DKGPartialSignature{
+ ProposerID: vID,
+ Round: round,
+ PartialSignature: shareSecret.sign(msgHash),
+ }
+ var err error
+ psig.Signature, err = s.prvKeys[vID].Sign(hashDKGPartialSignature(psig))
+ s.Require().NoError(err)
+ s.Require().NoError(tsig.processPartialSignature(msgHash, psig))
+ if len(tsig.sigs) > k {
+ break
+ }
+ }
+
+ sig, err := tsig.signature()
+ s.Require().NoError(err)
+ s.True(gpk.verifySignature(msgHash, sig))
+}
+
+func TestDKGTSIGProtocol(t *testing.T) {
+ suite.Run(t, new(DKGTSIGProtocolTestSuite))
+}
diff --git a/core/types/dkg.go b/core/types/dkg.go
index 7fee404..7fb686c 100644
--- a/core/types/dkg.go
+++ b/core/types/dkg.go
@@ -24,17 +24,18 @@ import (
// DKGPrivateShare describe a secret share in DKG protocol.
type DKGPrivateShare struct {
- ProposerID ValidatorID `json:"proposer_id"`
- Round uint64 `json:"round"`
- PrivateKeyShare dkg.PrivateKey `json:"private_key_share"`
- Signature crypto.Signature `json:"signature"`
+ ProposerID ValidatorID `json:"proposer_id"`
+ Round uint64 `json:"round"`
+ PrivateShare dkg.PrivateKey `json:"private_share"`
+ Signature crypto.Signature `json:"signature"`
}
// DKGMasterPublicKey decrtibe a master public key in DKG protocol.
type DKGMasterPublicKey struct {
ProposerID ValidatorID `json:"proposer_id"`
Round uint64 `json:"round"`
- PublicKeyShares dkg.PublicKeyShares `json:"private_key_share"`
+ DKGID dkg.ID `json:"dkg_id"`
+ PublicKeyShares dkg.PublicKeyShares `json:"public_key_shares"`
Signature crypto.Signature `json:"signature"`
}
@@ -45,3 +46,11 @@ type DKGComplaint struct {
PrivateShare DKGPrivateShare `json:"private_share"`
Signature crypto.Signature `json:"signature"`
}
+
+// DKGPartialSignature describe a partial signature in DKG protocol.
+type DKGPartialSignature struct {
+ ProposerID ValidatorID `json:"proposerID"`
+ Round uint64 `json:"round"`
+ PartialSignature dkg.PartialSignature `json:"partial_signature"`
+ Signature crypto.Signature `json:"signature"`
+}