// 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 dkg
import (
"math/rand"
"reflect"
"testing"
"github.com/stretchr/testify/suite"
"github.com/dexon-foundation/dexon-consensus/common"
"github.com/dexon-foundation/dexon-consensus/core/crypto"
cryptoDKG "github.com/dexon-foundation/dexon-consensus/core/crypto/dkg"
"github.com/dexon-foundation/dexon-consensus/core/types"
"github.com/dexon-foundation/dexon/rlp"
)
type DKGTestSuite struct {
suite.Suite
}
func (s *DKGTestSuite) genRandomBytes() []byte {
randomness := make([]byte, 32)
_, err := rand.Read(randomness)
s.Require().NoError(err)
return randomness
}
func (s *DKGTestSuite) genID() cryptoDKG.ID {
dID, err := cryptoDKG.BytesID(s.genRandomBytes())
s.Require().NoError(err)
return dID
}
func (s *DKGTestSuite) clone(src, dst interface{}) {
b, err := rlp.EncodeToBytes(src)
s.Require().NoError(err)
s.Require().NoError(rlp.DecodeBytes(b, dst))
}
func (s *DKGTestSuite) TestRLPEncodeDecode() {
dID := s.genID()
// Prepare master public key for testing.
d := MasterPublicKey{
ProposerID: types.NodeID{Hash: common.Hash{1, 2, 3}},
Round: 10,
DKGID: dID,
Signature: crypto.Signature{
Type: "123",
Signature: []byte{4, 5, 6},
},
}
b, err := rlp.EncodeToBytes(&d)
s.Require().NoError(err)
var dd MasterPublicKey
err = rlp.DecodeBytes(b, &dd)
s.Require().NoError(err)
bb, err := rlp.EncodeToBytes(&dd)
s.Require().NoError(err)
s.Require().True(reflect.DeepEqual(b, bb))
s.Require().True(d.ProposerID.Equal(dd.ProposerID))
s.Require().True(d.Round == dd.Round)
s.Require().True(reflect.DeepEqual(d.Signature, dd.Signature))
s.Require().Equal(d.DKGID.GetHexString(), dd.DKGID.GetHexString())
}
func (s *DKGTestSuite) TestMasterPublicKeyEquality() {
var req = s.Require()
// Prepare source master public key.
master1 := &MasterPublicKey{
ProposerID: types.NodeID{Hash: common.NewRandomHash()},
Round: 1234,
DKGID: s.genID(),
Signature: crypto.Signature{
Signature: s.genRandomBytes(),
},
}
prvKey := cryptoDKG.NewPrivateKey()
pubKey := prvKey.PublicKey().(cryptoDKG.PublicKey)
_, pubShares := cryptoDKG.NewPrivateKeyShares(2)
req.NoError(pubShares.AddShare(s.genID(), &pubKey))
master1.PublicKeyShares = *pubShares
// Prepare another master public key by copying every field.
master2 := &MasterPublicKey{}
s.clone(master1, master2)
// They should be equal.
req.True(master1.Equal(master2))
// Change round.
master2.Round = 2345
req.False(master1.Equal(master2))
master2.Round = 1234
// Change proposerID.
master2.ProposerID = types.NodeID{Hash: common.NewRandomHash()}
req.False(master1.Equal(master2))
master2.ProposerID = master1.ProposerID
// Change DKGID.
master2.DKGID = cryptoDKG.NewID(s.genRandomBytes())
req.False(master1.Equal(master2))
master2.DKGID = master1.DKGID
// Change signature.
master2.Signature = crypto.Signature{
Signature: s.genRandomBytes(),
}
req.False(master1.Equal(master2))
master2.Signature = master1.Signature
// Change public key share.
master2.PublicKeyShares = *cryptoDKG.NewEmptyPublicKeyShares()
req.False(master1.Equal(master2))
}
func (s *DKGTestSuite) TestPrivateShareEquality() {
var req = s.Require()
share1 := &PrivateShare{
ProposerID: types.NodeID{Hash: common.NewRandomHash()},
ReceiverID: types.NodeID{Hash: common.NewRandomHash()},
Round: 1,
PrivateShare: *cryptoDKG.NewPrivateKey(),
Signature: crypto.Signature{
Signature: s.genRandomBytes(),
},
}
// Make a copy.
share2 := &PrivateShare{}
s.clone(share1, share2)
req.True(share1.Equal(share2))
// Change proposer ID.
share2.ProposerID = types.NodeID{Hash: common.NewRandomHash()}
req.False(share1.Equal(share2))
share2.ProposerID = share1.ProposerID
// Change receiver ID.
share2.ReceiverID = types.NodeID{Hash: common.NewRandomHash()}
req.False(share1.Equal(share2))
share2.ReceiverID = share1.ReceiverID
// Change round.
share2.Round = share1.Round + 1
req.False(share1.Equal(share2))
share2.Round = share1.Round
// Change signature.
share2.Signature = crypto.Signature{
Signature: s.genRandomBytes(),
}
req.False(share1.Equal(share2))
share2.Signature = share1.Signature
// Change private share.
share2.PrivateShare = *cryptoDKG.NewPrivateKey()
req.False(share1.Equal(share2))
share2.PrivateShare = share1.PrivateShare
// They should be equal after chaning fields back.
req.True(share1.Equal(share2))
}
func (s *DKGTestSuite) TestComplaintEquality() {
var req = s.Require()
comp1 := &Complaint{
ProposerID: types.NodeID{Hash: common.NewRandomHash()},
Round: 1,
PrivateShare: PrivateShare{
ProposerID: types.NodeID{Hash: common.NewRandomHash()},
ReceiverID: types.NodeID{Hash: common.NewRandomHash()},
Round: 2,
PrivateShare: *cryptoDKG.NewPrivateKey(),
Signature: crypto.Signature{
Signature: s.genRandomBytes(),
},
},
Signature: crypto.Signature{
Signature: s.genRandomBytes(),
},
}
// Make a copy.
comp2 := &Complaint{}
s.clone(comp1, comp2)
req.True(comp1.Equal(comp2))
// Change proposer ID.
comp2.ProposerID = types.NodeID{Hash: common.NewRandomHash()}
req.False(comp1.Equal(comp2))
comp2.ProposerID = comp1.ProposerID
// Change round.
comp2.Round = comp1.Round + 1
req.False(comp1.Equal(comp2))
comp2.Round = comp1.Round
// Change signature.
comp2.Signature = crypto.Signature{
Signature: s.genRandomBytes(),
}
req.False(comp1.Equal(comp2))
comp2.Signature = comp1.Signature
// Change share's round.
comp2.PrivateShare.Round = comp1.PrivateShare.Round + 1
req.False(comp1.Equal(comp2))
comp2.PrivateShare.Round = comp1.PrivateShare.Round
// After changing every field back, should be equal.
req.True(comp1.Equal(comp2))
}
func (s *DKGTestSuite) TestMPKReadyEquality() {
var req = s.Require()
ready1 := &MPKReady{
ProposerID: types.NodeID{Hash: common.NewRandomHash()},
Round: 1,
Signature: crypto.Signature{
Signature: s.genRandomBytes(),
},
}
// Make a copy
ready2 := &MPKReady{}
s.clone(ready1, ready2)
req.True(ready1.Equal(ready2))
// Change proposer ID.
ready2.ProposerID = types.NodeID{Hash: common.NewRandomHash()}
req.False(ready1.Equal(ready2))
ready2.ProposerID = ready1.ProposerID
// Change round.
ready2.Round = ready1.Round + 1
req.False(ready1.Equal(ready2))
ready2.Round = ready1.Round
// Change signature.
ready2.Signature = crypto.Signature{
Signature: s.genRandomBytes(),
}
req.False(ready1.Equal(ready2))
ready2.Signature = ready1.Signature
// After changing every field back, they should be equal.
req.True(ready1.Equal(ready2))
}
func (s *DKGTestSuite) TestFinalizeEquality() {
var req = s.Require()
final1 := &Finalize{
ProposerID: types.NodeID{Hash: common.NewRandomHash()},
Round: 1,
Signature: crypto.Signature{
Signature: s.genRandomBytes(),
},
}
// Make a copy
final2 := &Finalize{}
s.clone(final1, final2)
req.True(final1.Equal(final2))
// Change proposer ID.
final2.ProposerID = types.NodeID{Hash: common.NewRandomHash()}
req.False(final1.Equal(final2))
final2.ProposerID = final1.ProposerID
// Change round.
final2.Round = final1.Round + 1
req.False(final1.Equal(final2))
final2.Round = final1.Round
// Change signature.
final2.Signature = crypto.Signature{
Signature: s.genRandomBytes(),
}
req.False(final1.Equal(final2))
final2.Signature = final1.Signature
// After changing every field back, they should be equal.
req.True(final1.Equal(final2))
}
func TestDKG(t *testing.T) {
suite.Run(t, new(DKGTestSuite))
}