diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/configuration-chain.go | 8 | ||||
-rw-r--r-- | core/configuration-chain_test.go | 213 | ||||
-rw-r--r-- | core/consensus.go | 2 | ||||
-rw-r--r-- | core/types/dkg.go | 7 |
4 files changed, 225 insertions, 5 deletions
diff --git a/core/configuration-chain.go b/core/configuration-chain.go index 88232c6..a6c0f39 100644 --- a/core/configuration-chain.go +++ b/core/configuration-chain.go @@ -168,7 +168,7 @@ func (cc *configurationChain) preparePartialSignature( } func (cc *configurationChain) runBlockTSig( - round uint64, hash common.Hash) error { + round uint64, hash common.Hash) (crypto.Signature, error) { gpk, exist := func() (*dkgGroupPublicKey, bool) { cc.dkgResult.RLock() defer cc.dkgResult.RUnlock() @@ -176,7 +176,7 @@ func (cc *configurationChain) runBlockTSig( return gpk, exist }() if !exist { - return ErrDKGNotReady + return nil, ErrDKGNotReady } cc.tsigReady.L.Lock() defer cc.tsigReady.L.Unlock() @@ -200,10 +200,10 @@ func (cc *configurationChain) runBlockTSig( } cc.tsig = nil if err != nil { - return err + return nil, err } log.Printf("[%s] TSIG: %s\n", cc.ID, signature) - return nil + return signature, nil } func (cc *configurationChain) processPrivateShare( diff --git a/core/configuration-chain_test.go b/core/configuration-chain_test.go new file mode 100644 index 0000000..ae82e42 --- /dev/null +++ b/core/configuration-chain_test.go @@ -0,0 +1,213 @@ +// 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 ( + "encoding/json" + "sync" + "testing" + "time" + + "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 ConfigurationChainTestSuite struct { + suite.Suite + + nIDs types.NodeIDs + dkgIDs map[types.NodeID]dkg.ID + prvKeys map[types.NodeID]crypto.PrivateKey +} + +type testCCReceiver struct { + s *ConfigurationChainTestSuite + + nodes map[types.NodeID]*configurationChain + govs map[types.NodeID]Governance +} + +func newTestCCReceiver( + s *ConfigurationChainTestSuite) *testCCReceiver { + return &testCCReceiver{ + s: s, + nodes: make(map[types.NodeID]*configurationChain), + govs: make(map[types.NodeID]Governance), + } +} + +func (r *testCCReceiver) ProposeDKGComplaint(complaint *types.DKGComplaint) { + prvKey, exist := r.s.prvKeys[complaint.ProposerID] + r.s.Require().True(exist) + var err error + complaint.Signature, err = prvKey.Sign(hashDKGComplaint(complaint)) + r.s.Require().NoError(err) + for _, gov := range r.govs { + // Use Marshal/Unmarshal to do deep copy. + data, err := json.Marshal(complaint) + r.s.Require().NoError(err) + complaintCopy := &types.DKGComplaint{} + r.s.Require().NoError(json.Unmarshal(data, complaintCopy)) + gov.AddDKGComplaint(complaintCopy) + } +} + +func (r *testCCReceiver) ProposeDKGMasterPublicKey( + mpk *types.DKGMasterPublicKey) { + prvKey, exist := r.s.prvKeys[mpk.ProposerID] + r.s.Require().True(exist) + var err error + mpk.Signature, err = prvKey.Sign(hashDKGMasterPublicKey(mpk)) + r.s.Require().NoError(err) + for _, gov := range r.govs { + // Use Marshal/Unmarshal to do deep copy. + data, err := json.Marshal(mpk) + r.s.Require().NoError(err) + mpkCopy := types.NewDKGMasterPublicKey() + r.s.Require().NoError(json.Unmarshal(data, mpkCopy)) + gov.AddDKGMasterPublicKey(mpkCopy) + } +} + +func (r *testCCReceiver) ProposeDKGPrivateShare( + prv *types.DKGPrivateShare) { + go func() { + prvKey, exist := r.s.prvKeys[prv.ProposerID] + r.s.Require().True(exist) + var err error + prv.Signature, err = prvKey.Sign(hashDKGPrivateShare(prv)) + r.s.Require().NoError(err) + receiver, exist := r.nodes[prv.ReceiverID] + r.s.Require().True(exist) + err = receiver.processPrivateShare(prv) + r.s.Require().NoError(err) + }() +} + +func (r *testCCReceiver) ProposeDKGAntiNackComplaint( + prv *types.DKGPrivateShare) { + go func() { + prvKey, exist := r.s.prvKeys[prv.ProposerID] + r.s.Require().True(exist) + var err error + prv.Signature, err = prvKey.Sign(hashDKGPrivateShare(prv)) + r.s.Require().NoError(err) + for _, cc := range r.nodes { + err = cc.processPrivateShare(prv) + r.s.Require().NoError(err) + } + }() +} + +func (s *ConfigurationChainTestSuite) setupNodes(n int) { + s.nIDs = make(types.NodeIDs, 0, n) + s.prvKeys = make(map[types.NodeID]crypto.PrivateKey, n) + s.dkgIDs = make(map[types.NodeID]dkg.ID) + ids := make(dkg.IDs, 0, n) + for i := 0; i < n; i++ { + prvKey, err := eth.NewPrivateKey() + s.Require().NoError(err) + nID := types.NewNodeID(prvKey.PublicKey()) + s.nIDs = append(s.nIDs, nID) + s.prvKeys[nID] = prvKey + id := dkg.NewID(nID.Hash[:]) + ids = append(ids, id) + s.dkgIDs[nID] = id + } +} + +// TestConfigurationChain 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 *ConfigurationChainTestSuite) TestConfigurationChain() { + k := 3 + n := 10 + round := uint64(1) + s.setupNodes(n) + + cfgChains := make(map[types.NodeID]*configurationChain) + recv := newTestCCReceiver(s) + + for _, nID := range s.nIDs { + gov, err := test.NewGovernance(0, 50*time.Millisecond) + s.Require().NoError(err) + cfgChains[nID] = newConfigurationChain(nID, recv, gov, eth.SigToPub) + recv.nodes[nID] = cfgChains[nID] + recv.govs[nID] = gov + } + + for _, cc := range cfgChains { + cc.registerDKG(round, k) + } + + for _, gov := range recv.govs { + s.Require().Len(gov.DKGMasterPublicKeys(round), n) + } + + wg := sync.WaitGroup{} + wg.Add(n) + for _, cc := range cfgChains { + go func(cc *configurationChain) { + defer wg.Done() + s.Require().NoError(cc.runDKG(round)) + }(cc) + } + wg.Wait() + + psigs := make([]*types.DKGPartialSignature, 0, n) + hash := crypto.Keccak256Hash([]byte("🌚🌝")) + for _, cc := range cfgChains { + psig, err := cc.preparePartialSignature(round, hash) + s.Require().NoError(err) + prvKey, exist := s.prvKeys[cc.ID] + s.Require().True(exist) + psig.Signature, err = prvKey.Sign(hashDKGPartialSignature(psig)) + s.Require().NoError(err) + psigs = append(psigs, psig) + } + + tsigs := make([]crypto.Signature, 0, n) + tsigChan := make(chan crypto.Signature) + for _, cc := range cfgChains { + go func(cc *configurationChain) { + tsig, err := cc.runBlockTSig(round, hash) + s.Require().NoError(err) + tsigChan <- tsig + }(cc) + for _, psig := range psigs { + err := cc.processPartialSignature(psig) + s.Require().NoError(err) + } + } + for range cfgChains { + tsig := <-tsigChan + for _, prevTsig := range tsigs { + s.Equal(prevTsig, tsig) + } + } +} + +func TestConfigurationChain(t *testing.T) { + suite.Run(t, new(ConfigurationChainTestSuite)) +} diff --git a/core/consensus.go b/core/consensus.go index f6ce3de..c7eea32 100644 --- a/core/consensus.go +++ b/core/consensus.go @@ -411,7 +411,7 @@ func (con *Consensus) runDKGTSIG() { panic(err) } con.network.BroadcastDKGPartialSignature(psig) - if err = con.cfgModule.runBlockTSig(round, hash); err != nil { + if _, err = con.cfgModule.runBlockTSig(round, hash); err != nil { panic(err) } }() diff --git a/core/types/dkg.go b/core/types/dkg.go index 10a31ee..a21c983 100644 --- a/core/types/dkg.go +++ b/core/types/dkg.go @@ -51,6 +51,13 @@ type DKGMasterPublicKey struct { Signature crypto.Signature `json:"signature"` } +// NewDKGMasterPublicKey returns a new DKGMasterPublicKey instance. +func NewDKGMasterPublicKey() *DKGMasterPublicKey { + return &DKGMasterPublicKey{ + PublicKeyShares: *dkg.NewEmptyPublicKeyShares(), + } +} + // DKGComplaint describe a complaint in DKG protocol. type DKGComplaint struct { ProposerID NodeID `json:"proposer_id"` |