diff options
Diffstat (limited to 'core/crypto')
-rw-r--r-- | core/crypto/dkg/dkg.go | 116 | ||||
-rw-r--r-- | core/crypto/dkg/dkg_test.go | 65 |
2 files changed, 155 insertions, 26 deletions
diff --git a/core/crypto/dkg/dkg.go b/core/crypto/dkg/dkg.go index 066b070..184a94f 100644 --- a/core/crypto/dkg/dkg.go +++ b/core/crypto/dkg/dkg.go @@ -101,30 +101,84 @@ type PrivateKeyShares struct { masterPrivateKey []bls.SecretKey } +// Equal check equality between two PrivateKeyShares instances. +func (prvs *PrivateKeyShares) Equal(other *PrivateKeyShares) bool { + // Check shares. + if len(prvs.shareIndex) != len(other.shareIndex) { + return false + } + for dID, idx := range prvs.shareIndex { + otherIdx, exists := other.shareIndex[dID] + if !exists { + return false + } + if !prvs.shares[idx].privateKey.IsEqual( + &other.shares[otherIdx].privateKey) { + return false + } + } + // Check master private keys. + if len(prvs.masterPrivateKey) != len(other.masterPrivateKey) { + return false + } + for idx, m := range prvs.masterPrivateKey { + if m.GetHexString() != other.masterPrivateKey[idx].GetHexString() { + return false + } + } + return true +} + // PublicKeyShares represents a public key shares for DKG protocol. type PublicKeyShares struct { - shares []PublicKey - shareIndex map[ID]int + shareCaches []PublicKey + shareCacheIndex map[ID]int masterPublicKey []bls.PublicKey } type rlpPublicKeyShares struct { - Shares [][]byte - ShareIndexK [][]byte - ShareIndexV []uint32 - MasterPublicKey [][]byte + ShareCaches [][]byte + ShareCacheIndexK [][]byte + ShareCacheIndexV []uint32 + MasterPublicKey [][]byte +} + +// Equal checks equality of two PublicKeyShares instance. +func (pubs *PublicKeyShares) Equal(other *PublicKeyShares) bool { + // Check shares. + for dID, idx := range pubs.shareCacheIndex { + otherIdx, exists := other.shareCacheIndex[dID] + if !exists { + continue + } + if !pubs.shareCaches[idx].publicKey.IsEqual( + &other.shareCaches[otherIdx].publicKey) { + return false + } + } + // Check master public keys. + if len(pubs.masterPublicKey) != len(other.masterPublicKey) { + return false + } + for idx, m := range pubs.masterPublicKey { + if m.GetHexString() != other.masterPublicKey[idx].GetHexString() { + return false + } + } + return true } // EncodeRLP implements rlp.Encoder func (pubs *PublicKeyShares) EncodeRLP(w io.Writer) error { var rps rlpPublicKeyShares - for _, share := range pubs.shares { - rps.Shares = append(rps.Shares, share.Serialize()) + for _, share := range pubs.shareCaches { + rps.ShareCaches = append(rps.ShareCaches, share.Serialize()) } - for id, v := range pubs.shareIndex { - rps.ShareIndexK = append(rps.ShareIndexK, id.GetLittleEndian()) - rps.ShareIndexV = append(rps.ShareIndexV, uint32(v)) + for id, v := range pubs.shareCacheIndex { + rps.ShareCacheIndexK = append( + rps.ShareCacheIndexK, id.GetLittleEndian()) + rps.ShareCacheIndexV = append(rps.ShareCacheIndexV, uint32(v)) } for _, m := range pubs.masterPublicKey { @@ -141,25 +195,25 @@ func (pubs *PublicKeyShares) DecodeRLP(s *rlp.Stream) error { return err } - if len(dec.ShareIndexK) != len(dec.ShareIndexV) { + if len(dec.ShareCacheIndexK) != len(dec.ShareCacheIndexV) { return fmt.Errorf("invalid shareIndex") } ps := NewEmptyPublicKeyShares() - for _, share := range dec.Shares { + for _, share := range dec.ShareCaches { var publicKey PublicKey if err := publicKey.Deserialize(share); err != nil { return err } - ps.shares = append(ps.shares, publicKey) + ps.shareCaches = append(ps.shareCaches, publicKey) } - for i, k := range dec.ShareIndexK { + for i, k := range dec.ShareCacheIndexK { id, err := BytesID(k) if err != nil { return err } - ps.shareIndex[id] = int(dec.ShareIndexV[i]) + ps.shareCacheIndex[id] = int(dec.ShareCacheIndexV[i]) } for _, k := range dec.MasterPublicKey { @@ -205,6 +259,20 @@ func (pubs *PublicKeyShares) UnmarshalJSON(data []byte) error { return nil } +// Clone clones every fields of PublicKeyShares. This method is mainly +// for testing purpose thus would panic when error. +func (pubs *PublicKeyShares) Clone() *PublicKeyShares { + b, err := rlp.EncodeToBytes(pubs) + if err != nil { + panic(err) + } + pubsCopy := NewEmptyPublicKeyShares() + if err := rlp.DecodeBytes(b, pubsCopy); err != nil { + panic(err) + } + return pubsCopy +} + // NewID creates a ew ID structure. func NewID(id []byte) ID { var blsID bls.ID @@ -240,7 +308,7 @@ func NewPrivateKeyShares(t int) (*PrivateKeyShares, *PublicKeyShares) { masterPrivateKey: msk, shareIndex: make(map[ID]int), }, &PublicKeyShares{ - shareIndex: make(map[ID]int), + shareCacheIndex: make(map[ID]int), masterPublicKey: mpk, } } @@ -329,15 +397,15 @@ func (prvs *PrivateKeyShares) Share(ID ID) (*PrivateKey, bool) { // NewEmptyPublicKeyShares creates an empty public key shares. func NewEmptyPublicKeyShares() *PublicKeyShares { return &PublicKeyShares{ - shareIndex: make(map[ID]int), + shareCacheIndex: make(map[ID]int), } } // Share returns the share for the ID. func (pubs *PublicKeyShares) Share(ID ID) (*PublicKey, error) { - idx, exist := pubs.shareIndex[ID] + idx, exist := pubs.shareCacheIndex[ID] if exist { - return &pubs.shares[idx], nil + return &pubs.shareCaches[idx], nil } var pk PublicKey if err := pk.publicKey.Set(pubs.masterPublicKey, &ID); err != nil { @@ -349,14 +417,14 @@ func (pubs *PublicKeyShares) Share(ID ID) (*PublicKey, error) { // AddShare adds a share. func (pubs *PublicKeyShares) AddShare(ID ID, share *PublicKey) error { - if idx, exist := pubs.shareIndex[ID]; exist { - if !share.publicKey.IsEqual(&pubs.shares[idx].publicKey) { + if idx, exist := pubs.shareCacheIndex[ID]; exist { + if !share.publicKey.IsEqual(&pubs.shareCaches[idx].publicKey) { return ErrDuplicatedShare } return nil } - pubs.shareIndex[ID] = len(pubs.shares) - pubs.shares = append(pubs.shares, *share) + pubs.shareCacheIndex[ID] = len(pubs.shareCaches) + pubs.shareCaches = append(pubs.shareCaches, *share) return nil } diff --git a/core/crypto/dkg/dkg_test.go b/core/crypto/dkg/dkg_test.go index 44e68d8..cb167e4 100644 --- a/core/crypto/dkg/dkg_test.go +++ b/core/crypto/dkg/dkg_test.go @@ -25,6 +25,7 @@ import ( "sync" "testing" + "github.com/Spiderpowa/bls/ffi/go/bls" "github.com/dexon-foundation/dexon/rlp" "github.com/stretchr/testify/suite" @@ -309,8 +310,8 @@ func (s *DKGTestSuite) TestPublicKeySharesRLPEncodeDecode() { for i, id := range s.genID(1) { privkey := NewPrivateKey() pubkey := privkey.PublicKey().(PublicKey) - p.shares = append(p.shares, pubkey) - p.shareIndex[id] = i + p.shareCaches = append(p.shareCaches, pubkey) + p.shareCacheIndex[id] = i p.masterPublicKey = append(p.masterPublicKey, pubkey.publicKey) } @@ -327,6 +328,66 @@ func (s *DKGTestSuite) TestPublicKeySharesRLPEncodeDecode() { s.Require().True(reflect.DeepEqual(b, bb)) } +func (s *DKGTestSuite) TestPublicKeySharesEquality() { + var req = s.Require() + IDs := s.genID(2) + _, pubShares1 := NewPrivateKeyShares(4) + // Make a copy from an empty share. + pubShares2 := pubShares1.Clone() + req.True(pubShares1.Equal(pubShares2)) + // Add two shares. + prvKey1 := NewPrivateKey() + pubKey1 := prvKey1.PublicKey().(PublicKey) + req.NoError(pubShares1.AddShare(IDs[0], &pubKey1)) + prvKey2 := NewPrivateKey() + pubKey2 := prvKey2.PublicKey().(PublicKey) + req.True(pubShares1.Equal(pubShares2)) + // Clone the shares. + req.NoError(pubShares2.AddShare(IDs[0], &pubKey1)) + req.NoError(pubShares2.AddShare(IDs[1], &pubKey2)) + // They should be equal now. + req.True(pubShares1.Equal(pubShares2)) + req.True(pubShares2.Equal(pubShares1)) +} + +func (s *DKGTestSuite) TestPrivateKeySharesEquality() { + var req = s.Require() + IDs := s.genID(2) + prvShares1, _ := NewPrivateKeyShares(4) + // Make a copy of empty share. + prvShares2 := NewEmptyPrivateKeyShares() + req.False(prvShares1.Equal(prvShares2)) + // Clone the master private key. + for _, m := range prvShares1.masterPrivateKey { + var key bls.SecretKey + req.NoError(key.SetLittleEndian(m.GetLittleEndian())) + prvShares2.masterPrivateKey = append(prvShares2.masterPrivateKey, key) + } + // Add two shares. + prvKey1 := NewPrivateKey() + req.NoError(prvShares1.AddShare(IDs[0], prvKey1)) + prvKey2 := NewPrivateKey() + req.NoError(prvShares1.AddShare(IDs[1], prvKey2)) + // They are not equal now. + req.False(prvShares1.Equal(prvShares2)) + // Clone the shares. + req.NoError(prvShares2.AddShare(IDs[0], prvKey1)) + req.NoError(prvShares2.AddShare(IDs[1], prvKey2)) + // They should be equal now. + req.True(prvShares1.Equal(prvShares2)) + req.True(prvShares2.Equal(prvShares1)) +} + +func (s *DKGTestSuite) TestPublicKeySharesClone() { + _, pubShares1 := NewPrivateKeyShares(4) + IDs := s.genID(2) + prvKey1 := NewPrivateKey() + pubKey1 := prvKey1.PublicKey().(PublicKey) + s.Require().NoError(pubShares1.AddShare(IDs[0], &pubKey1)) + pubShares2 := pubShares1.Clone() + s.Require().True(pubShares1.Equal(pubShares2)) +} + func TestDKG(t *testing.T) { suite.Run(t, new(DKGTestSuite)) } |