diff options
author | Sonic <sonic@dexon.org> | 2018-10-20 16:07:37 +0800 |
---|---|---|
committer | Wei-Ning Huang <aitjcize@gmail.com> | 2018-10-20 16:07:37 +0800 |
commit | 9fbe8f9e561a64429eb15431782eb4280d0ffbad (patch) | |
tree | 035495fdc970b7ad6f2cb1c6b9cc42cbd36526cb | |
parent | 1f7491df37caf974ffa0c824c4c02a8fe2aafcd9 (diff) | |
download | dexon-consensus-9fbe8f9e561a64429eb15431782eb4280d0ffbad.tar dexon-consensus-9fbe8f9e561a64429eb15431782eb4280d0ffbad.tar.gz dexon-consensus-9fbe8f9e561a64429eb15431782eb4280d0ffbad.tar.bz2 dexon-consensus-9fbe8f9e561a64429eb15431782eb4280d0ffbad.tar.lz dexon-consensus-9fbe8f9e561a64429eb15431782eb4280d0ffbad.tar.xz dexon-consensus-9fbe8f9e561a64429eb15431782eb4280d0ffbad.tar.zst dexon-consensus-9fbe8f9e561a64429eb15431782eb4280d0ffbad.zip |
core: types: implement rlp.Encoder and rlp.Decoder (#232)
* core: types: implement rlp.Encoder and rlp.Decoder
* crypto: dkg: fix PublicKey.Bytes
-rw-r--r-- | core/crypto/dkg/dkg.go | 98 | ||||
-rw-r--r-- | core/crypto/dkg/dkg_test.go | 25 | ||||
-rw-r--r-- | core/types/block.go | 134 | ||||
-rw-r--r-- | core/types/block_test.go | 23 | ||||
-rw-r--r-- | core/types/dkg.go | 44 | ||||
-rw-r--r-- | core/types/dkg_test.go | 62 |
6 files changed, 379 insertions, 7 deletions
diff --git a/core/crypto/dkg/dkg.go b/core/crypto/dkg/dkg.go index 55199a9..8d0e088 100644 --- a/core/crypto/dkg/dkg.go +++ b/core/crypto/dkg/dkg.go @@ -20,8 +20,10 @@ package dkg import ( "encoding/json" "fmt" + "io" "github.com/Spiderpowa/bls/ffi/go/bls" + "github.com/dexon-foundation/dexon/rlp" "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/crypto" @@ -92,6 +94,72 @@ type PublicKeyShares struct { masterPublicKey []bls.PublicKey } +type rlpPublicKeyShares struct { + Shares [][]byte + ShareIndexK [][]byte + ShareIndexV []uint32 + MasterPublicKey [][]byte +} + +// 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 id, v := range pubs.shareIndex { + rps.ShareIndexK = append(rps.ShareIndexK, id.GetLittleEndian()) + rps.ShareIndexV = append(rps.ShareIndexV, uint32(v)) + } + + for _, m := range pubs.masterPublicKey { + rps.MasterPublicKey = append(rps.MasterPublicKey, m.Serialize()) + } + + return rlp.Encode(w, rps) +} + +// DecodeRLP implements rlp.Decoder +func (pubs *PublicKeyShares) DecodeRLP(s *rlp.Stream) error { + var dec rlpPublicKeyShares + if err := s.Decode(&dec); err != nil { + return err + } + + if len(dec.ShareIndexK) != len(dec.ShareIndexV) { + return fmt.Errorf("invalid shareIndex") + } + + ps := NewEmptyPublicKeyShares() + for _, share := range dec.Shares { + var publicKey PublicKey + if err := publicKey.Deserialize(share); err != nil { + return err + } + ps.shares = append(ps.shares, publicKey) + } + + for i, k := range dec.ShareIndexK { + id, err := BytesID(k) + if err != nil { + return err + } + ps.shareIndex[id] = int(dec.ShareIndexV[i]) + } + + for _, k := range dec.MasterPublicKey { + var key bls.PublicKey + if err := key.Deserialize(k); err != nil { + return err + } + ps.masterPublicKey = append(ps.masterPublicKey, key) + } + + *pubs = *ps + return nil +} + // MarshalJSON implements json.Marshaller. func (pubs *PublicKeyShares) MarshalJSON() ([]byte, error) { type Alias PublicKeyShares @@ -130,6 +198,14 @@ func NewID(id []byte) ID { return blsID } +// BytesID creates a new ID structure, +// It returns err if the byte slice is not valid. +func BytesID(id []byte) (ID, error) { + var blsID bls.ID + err := blsID.SetLittleEndian(id) + return blsID, err +} + // NewPrivateKey creates a new PrivateKey structure. func NewPrivateKey() *PrivateKey { var key bls.SecretKey @@ -327,6 +403,14 @@ func newPublicKey(prvKey *bls.SecretKey) *PublicKey { } } +// newPublicKeyFromBytes create a new PublicKey structure +// from bytes representation of bls.PublicKey +func newPublicKeyFromBytes(b []byte) (*PublicKey, error) { + var pub PublicKey + err := pub.publicKey.Deserialize(b) + return &pub, err +} + // PublicKey returns the public key associate this private key. func (prv *PrivateKey) PublicKey() crypto.PublicKey { return prv.publicKey @@ -380,7 +464,15 @@ func (pub PublicKey) VerifySignature( // Bytes returns []byte representation of public key. func (pub PublicKey) Bytes() []byte { - var bytes []byte - pub.publicKey.Deserialize(bytes) - return bytes + return pub.publicKey.Serialize() +} + +// Serialize return bytes representation of public key. +func (pub *PublicKey) Serialize() []byte { + return pub.publicKey.Serialize() +} + +// Deserialize parses bytes representation of public key. +func (pub *PublicKey) Deserialize(b []byte) error { + return pub.publicKey.Deserialize(b) } diff --git a/core/crypto/dkg/dkg_test.go b/core/crypto/dkg/dkg_test.go index 84a78f4..fe7ec73 100644 --- a/core/crypto/dkg/dkg_test.go +++ b/core/crypto/dkg/dkg_test.go @@ -20,10 +20,12 @@ package dkg import ( "encoding/binary" "math/rand" + "reflect" "sort" "sync" "testing" + "github.com/dexon-foundation/dexon/rlp" "github.com/stretchr/testify/suite" "github.com/dexon-foundation/dexon-consensus-core/common" @@ -290,6 +292,29 @@ func (s *DKGTestSuite) TestSignature() { s.False(pubKey.VerifySignature(hash, sig)) } +func (s *DKGTestSuite) TestPublicKeySharesRLPEncodeDecode() { + p := NewEmptyPublicKeyShares() + for i, id := range s.genID(1) { + privkey := NewPrivateKey() + pubkey := privkey.PublicKey().(PublicKey) + p.shares = append(p.shares, pubkey) + p.shareIndex[id] = i + p.masterPublicKey = append(p.masterPublicKey, pubkey.publicKey) + } + + b, err := rlp.EncodeToBytes(p) + s.Require().NoError(err) + + var pp PublicKeyShares + err = rlp.DecodeBytes(b, &pp) + s.Require().NoError(err) + + bb, err := rlp.EncodeToBytes(&pp) + s.Require().NoError(err) + + s.Require().True(reflect.DeepEqual(b, bb)) +} + func TestDKG(t *testing.T) { suite.Run(t, new(DKGTestSuite)) } diff --git a/core/types/block.go b/core/types/block.go index 63bcec4..c8ee3c6 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -22,10 +22,13 @@ package types import ( "bytes" "fmt" + "io" "sort" "sync" "time" + "github.com/dexon-foundation/dexon/rlp" + "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/crypto" ) @@ -39,6 +42,25 @@ var ( } ) +type rlpTimestamp struct { + time.Time +} + +func (t *rlpTimestamp) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, uint64(t.UTC().UnixNano())) +} + +func (t *rlpTimestamp) DecodeRLP(s *rlp.Stream) error { + var nano uint64 + err := s.Decode(&nano) + if err == nil { + sec := int64(nano) / 1000000000 + nsec := int64(nano) % 1000000000 + t.Time = time.Unix(sec, nsec).UTC() + } + return err +} + // FinalizationResult represents the result of DEXON consensus algorithm. type FinalizationResult struct { Randomness []byte `json:"randomness"` @@ -46,6 +68,35 @@ type FinalizationResult struct { Height uint64 `json:"height"` } +type rlpFinalizationResult struct { + Randomness []byte + Timestamp *rlpTimestamp + Height uint64 +} + +// EncodeRLP implements rlp.Encoder +func (f *FinalizationResult) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, &rlpFinalizationResult{ + Randomness: f.Randomness, + Timestamp: &rlpTimestamp{f.Timestamp}, + Height: f.Height, + }) +} + +// DecodeRLP implements rlp.Decoder +func (f *FinalizationResult) DecodeRLP(s *rlp.Stream) error { + var dec rlpFinalizationResult + err := s.Decode(&dec) + if err == nil { + *f = FinalizationResult{ + Randomness: dec.Randomness, + Timestamp: dec.Timestamp.Time, + Height: dec.Height, + } + } + return err +} + // Witness represents the consensus information on the compaction chain. type Witness struct { Timestamp time.Time `json:"timestamp"` @@ -53,6 +104,35 @@ type Witness struct { Data []byte `json:"data"` } +type rlpWitness struct { + Timestamp *rlpTimestamp + Height uint64 + Data []byte +} + +// EncodeRLP implements rlp.Encoder +func (w *Witness) EncodeRLP(writer io.Writer) error { + return rlp.Encode(writer, rlpWitness{ + Timestamp: &rlpTimestamp{w.Timestamp}, + Height: w.Height, + Data: w.Data, + }) +} + +// DecodeRLP implements rlp.Decoder +func (w *Witness) DecodeRLP(s *rlp.Stream) error { + var dec rlpWitness + err := s.Decode(&dec) + if err == nil { + *w = Witness{ + Timestamp: dec.Timestamp.Time, + Height: dec.Height, + Data: dec.Data, + } + } + return err +} + // RecycleBlock put unused block into cache, which might be reused if // not garbage collected. func RecycleBlock(b *Block) { @@ -82,6 +162,60 @@ type Block struct { CRSSignature crypto.Signature `json:"crs_signature"` } +type rlpBlock struct { + ProposerID NodeID + ParentHash common.Hash + Hash common.Hash + Position Position + Timestamp *rlpTimestamp + Acks common.SortedHashes + Payload []byte + Witness *Witness + Finalization *FinalizationResult + Signature crypto.Signature + + CRSSignature crypto.Signature +} + +// EncodeRLP implements rlp.Encoder +func (b *Block) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, rlpBlock{ + ProposerID: b.ProposerID, + ParentHash: b.ParentHash, + Hash: b.Hash, + Position: b.Position, + Timestamp: &rlpTimestamp{b.Timestamp}, + Acks: b.Acks, + Payload: b.Payload, + Witness: &b.Witness, + Finalization: &b.Finalization, + Signature: b.Signature, + CRSSignature: b.CRSSignature, + }) +} + +// DecodeRLP implements rlp.Decoder +func (b *Block) DecodeRLP(s *rlp.Stream) error { + var dec rlpBlock + err := s.Decode(&dec) + if err == nil { + *b = Block{ + ProposerID: dec.ProposerID, + ParentHash: dec.ParentHash, + Hash: dec.Hash, + Position: dec.Position, + Timestamp: dec.Timestamp.Time, + Acks: dec.Acks, + Payload: dec.Payload, + Witness: *dec.Witness, + Finalization: *dec.Finalization, + Signature: dec.Signature, + CRSSignature: dec.CRSSignature, + } + } + return err +} + func (b *Block) String() string { return fmt.Sprintf("Block(%v:%d:%d)", b.Hash.String()[:6], b.Position.ChainID, b.Position.Height) diff --git a/core/types/block_test.go b/core/types/block_test.go index 49eaa86..758619d 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -24,9 +24,11 @@ import ( "testing" "time" + "github.com/stretchr/testify/suite" + "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/crypto" - "github.com/stretchr/testify/suite" + "github.com/dexon-foundation/dexon/rlp" ) type BlockTestSuite struct { @@ -51,14 +53,14 @@ func (s *BlockTestSuite) createRandomBlock() *Block { common.NewRandomHash(), common.NewRandomHash(), }), - Timestamp: time.Now().Add(time.Duration(rand.Int())), + Timestamp: time.Now().UTC(), Witness: Witness{ Height: rand.Uint64(), - Timestamp: time.Now().Add(time.Duration(rand.Int())), + Timestamp: time.Now().UTC(), Data: s.randomBytes(), }, Finalization: FinalizationResult{ - Timestamp: time.Now().Add(time.Duration(rand.Int())), + Timestamp: time.Now().UTC(), Height: rand.Uint64(), Randomness: s.randomBytes(), }, @@ -66,6 +68,7 @@ func (s *BlockTestSuite) createRandomBlock() *Block { Signature: crypto.Signature{Signature: s.randomBytes()}, CRSSignature: crypto.Signature{Signature: s.randomBytes()}, } + return b } @@ -178,6 +181,18 @@ func (s *BlockTestSuite) TestClone() { } } +func (s *BlockTestSuite) TestRLPEncodeDecode() { + block := s.createRandomBlock() + b, err := rlp.EncodeToBytes(block) + s.Require().NoError(err) + + var dec Block + err = rlp.DecodeBytes(b, &dec) + s.Require().NoError(err) + + s.Require().True(reflect.DeepEqual(block, &dec)) +} + func TestBlock(t *testing.T) { suite.Run(t, new(BlockTestSuite)) } diff --git a/core/types/dkg.go b/core/types/dkg.go index 6018ebd..c0a42ee 100644 --- a/core/types/dkg.go +++ b/core/types/dkg.go @@ -20,6 +20,9 @@ package types import ( "encoding/json" "fmt" + "io" + + "github.com/dexon-foundation/dexon/rlp" "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core/crypto" @@ -59,6 +62,47 @@ func (d *DKGMasterPublicKey) String() string { d.Round) } +type rlpDKGMasterPublicKey struct { + ProposerID NodeID + Round uint64 + DKGID []byte + PublicKeyShares *dkg.PublicKeyShares + Signature crypto.Signature +} + +// EncodeRLP implements rlp.Encoder +func (d *DKGMasterPublicKey) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, rlpDKGMasterPublicKey{ + ProposerID: d.ProposerID, + Round: d.Round, + DKGID: d.DKGID.GetLittleEndian(), + PublicKeyShares: &d.PublicKeyShares, + Signature: d.Signature, + }) +} + +// DecodeRLP implements rlp.Decoder +func (d *DKGMasterPublicKey) DecodeRLP(s *rlp.Stream) error { + var dec rlpDKGMasterPublicKey + if err := s.Decode(&dec); err != nil { + return err + } + + id, err := dkg.BytesID(dec.DKGID) + if err != nil { + return err + } + + *d = DKGMasterPublicKey{ + ProposerID: dec.ProposerID, + Round: dec.Round, + DKGID: id, + PublicKeyShares: *dec.PublicKeyShares, + Signature: dec.Signature, + } + return err +} + // NewDKGMasterPublicKey returns a new DKGMasterPublicKey instance. func NewDKGMasterPublicKey() *DKGMasterPublicKey { return &DKGMasterPublicKey{ diff --git a/core/types/dkg_test.go b/core/types/dkg_test.go new file mode 100644 index 0000000..010d460 --- /dev/null +++ b/core/types/dkg_test.go @@ -0,0 +1,62 @@ +// 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 types + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon/rlp" +) + +type DKGTestSuite struct { + suite.Suite +} + +func (s *DKGTestSuite) TestRLPEncodeDecode() { + d := DKGMasterPublicKey{ + ProposerID: NodeID{common.Hash{1, 2, 3}}, + Round: 10, + Signature: crypto.Signature{ + Type: "123", + Signature: []byte{4, 5, 6}, + }, + } + + b, err := rlp.EncodeToBytes(&d) + s.Require().NoError(err) + + var dd DKGMasterPublicKey + 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)) +} + +func TestDKG(t *testing.T) { + suite.Run(t, new(DKGTestSuite)) +} |