aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/crypto.go62
-rw-r--r--core/crypto_test.go81
-rw-r--r--core/types/block.go12
-rw-r--r--core/types/validator.go29
4 files changed, 174 insertions, 10 deletions
diff --git a/core/crypto.go b/core/crypto.go
index 679b045..3bf8aa8 100644
--- a/core/crypto.go
+++ b/core/crypto.go
@@ -19,6 +19,7 @@ package core
import (
"encoding/binary"
+ "sort"
"github.com/dexon-foundation/dexon-consensus-core/common"
"github.com/dexon-foundation/dexon-consensus-core/core/types"
@@ -56,3 +57,64 @@ func verifyCompactionChainAckSignature(pubkey crypto.PublicKey,
}
return pubkey.VerifySignature(hash, sig), nil
}
+
+func hashBlock(blockConv types.BlockConverter) (common.Hash, error) {
+ block := blockConv.Block()
+ binaryHeight := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryHeight, block.Height)
+ // Handling Block.Acks.
+ acks := make(common.Hashes, 0, len(block.Acks))
+ for ack := range block.Acks {
+ acks = append(acks, ack)
+ }
+ sort.Sort(acks)
+ binaryAcks := make([][]byte, len(block.Acks))
+ for idx := range acks {
+ binaryAcks[idx] = acks[idx][:]
+ }
+ hashAcks := crypto.Keccak256Hash(binaryAcks...)
+ // Handle Block.Timestamps.
+ // TODO(jimmy-dexon): Store and get the sorted validatorIDs.
+ validators := make(types.ValidatorIDs, 0, len(block.Timestamps))
+ for vID := range block.Timestamps {
+ validators = append(validators, vID)
+ }
+ sort.Sort(validators)
+ binaryTimestamps := make([][]byte, len(block.Timestamps))
+ for idx, vID := range validators {
+ var err error
+ binaryTimestamps[idx], err = block.Timestamps[vID].MarshalBinary()
+ if err != nil {
+ return common.Hash{}, err
+ }
+ }
+ hashTimestamps := crypto.Keccak256Hash(binaryTimestamps...)
+ payloadHash := crypto.Keccak256Hash(blockConv.GetPayloads()...)
+
+ hash := crypto.Keccak256Hash(
+ block.ProposerID.Hash[:],
+ block.ParentHash[:],
+ binaryHeight,
+ hashAcks[:],
+ hashTimestamps[:],
+ payloadHash[:])
+ return hash, nil
+}
+
+func signBlock(blockConv types.BlockConverter,
+ prv crypto.PrivateKey) (crypto.Signature, error) {
+ hash, err := hashBlock(blockConv)
+ if err != nil {
+ return crypto.Signature{}, err
+ }
+ return prv.Sign(hash)
+}
+
+func verifyBlockSignature(pubkey crypto.PublicKey,
+ blockConv types.BlockConverter, sig crypto.Signature) (bool, error) {
+ hash, err := hashBlock(blockConv)
+ if err != nil {
+ return false, err
+ }
+ return pubkey.VerifySignature(hash, sig), nil
+}
diff --git a/core/crypto_test.go b/core/crypto_test.go
index 0ee28e4..be130e2 100644
--- a/core/crypto_test.go
+++ b/core/crypto_test.go
@@ -32,10 +32,28 @@ type CryptoTestSuite struct {
suite.Suite
}
-func (s *CryptoTestSuite) newBlock(prevBlock *types.Block) *types.Block {
+var myVID = types.ValidatorID{Hash: common.NewRandomHash()}
+
+type simpleBlock struct {
+ block *types.Block
+}
+
+func (sb *simpleBlock) Block() *types.Block {
+ return sb.block
+}
+
+func (sb *simpleBlock) GetPayloads() [][]byte {
+ return [][]byte{}
+}
+
+func (s *CryptoTestSuite) prepareBlock(prevBlock *types.Block) *types.Block {
+ acks := make(map[common.Hash]struct{})
+ timestamps := make(map[types.ValidatorID]time.Time)
+ timestamps[myVID] = time.Now().UTC()
if prevBlock == nil {
return &types.Block{
- Hash: common.NewRandomHash(),
+ Acks: acks,
+ Timestamps: timestamps,
ConsensusInfo: types.ConsensusInfo{
Timestamp: time.Now(),
Height: 0,
@@ -44,9 +62,14 @@ func (s *CryptoTestSuite) newBlock(prevBlock *types.Block) *types.Block {
}
parentHash, err := hashCompactionChainAck(prevBlock)
s.Require().Nil(err)
+ s.Require().NotEqual(prevBlock.Hash, common.Hash{})
+ acks[parentHash] = struct{}{}
return &types.Block{
- Hash: common.NewRandomHash(),
+ ParentHash: prevBlock.Hash,
+ Acks: acks,
+ Timestamps: timestamps,
ConsensusInfoParentHash: parentHash,
+ Height: prevBlock.Height + 1,
CompactionChainAck: types.CompactionChainAck{
AckingBlockHash: prevBlock.Hash,
},
@@ -57,6 +80,14 @@ func (s *CryptoTestSuite) newBlock(prevBlock *types.Block) *types.Block {
}
}
+func (s *CryptoTestSuite) newBlock(prevBlock *types.Block) *types.Block {
+ block := s.prepareBlock(prevBlock)
+ var err error
+ block.Hash, err = hashBlock(&simpleBlock{block: block})
+ s.Require().Nil(err)
+ return block
+}
+
func (s *CryptoTestSuite) generateCompactionChain(
length int, prv crypto.PrivateKey) []*types.Block {
blocks := make([]*types.Block, length)
@@ -76,7 +107,7 @@ func (s *CryptoTestSuite) generateCompactionChain(
return blocks
}
-func (s *CryptoTestSuite) TestSignature() {
+func (s *CryptoTestSuite) TestCompactionChainAckSignature() {
prv, err := eth.NewPrivateKey()
pub := prv.PublicKey()
s.Require().Nil(err)
@@ -111,6 +142,48 @@ func (s *CryptoTestSuite) TestSignature() {
}
}
+func (s *CryptoTestSuite) generateBlockChain(
+ length int, prv crypto.PrivateKey) []*types.Block {
+ blocks := make([]*types.Block, length)
+ var prevBlock *types.Block
+ for idx := range blocks {
+ block := s.newBlock(prevBlock)
+ blocks[idx] = block
+ var err error
+ block.Signature, err = signBlock(&simpleBlock{block: block}, prv)
+ s.Require().Nil(err)
+ }
+ return blocks
+}
+
+func (s *CryptoTestSuite) TestBlockSignature() {
+ prv, err := eth.NewPrivateKey()
+ pub := prv.PublicKey()
+ s.Require().Nil(err)
+ blocks := s.generateBlockChain(10, prv)
+ blockMap := make(map[common.Hash]*types.Block)
+ for _, block := range blocks {
+ blockMap[block.Hash] = block
+ }
+ for _, block := range blocks {
+ if !block.IsGenesis() {
+ parentBlock, exist := blockMap[block.ParentHash]
+ s.Require().True(exist)
+ s.True(parentBlock.Height == block.Height-1)
+ hash, err := hashBlock(&simpleBlock{block: parentBlock})
+ s.Require().Nil(err)
+ s.Equal(hash, block.ParentHash)
+ }
+ s.True(verifyBlockSignature(pub, &simpleBlock{block: block}, block.Signature))
+ }
+ // Modify Block.Acks and verify signature again.
+ for _, block := range blocks {
+ block.Acks[common.NewRandomHash()] = struct{}{}
+ s.False(verifyBlockSignature(
+ pub, &simpleBlock{block: block}, block.Signature))
+ }
+}
+
func TestCrypto(t *testing.T) {
suite.Run(t, new(CryptoTestSuite))
}
diff --git a/core/types/block.go b/core/types/block.go
index e35ab8d..363de3a 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -60,6 +60,7 @@ type Block struct {
Timestamps map[ValidatorID]time.Time `json:"timestamps"`
Acks map[common.Hash]struct{} `json:"acks"`
CompactionChainAck CompactionChainAck `json:"compaction_chain_ack"`
+ Signature crypto.Signature `json:"signature"`
ConsensusInfo ConsensusInfo `json:"consensus_info"`
// ConsensusInfoParentHash is the hash value of Block.ConsensusInfoParentHash
@@ -72,10 +73,21 @@ type Block struct {
Status Status `json:"-"`
}
+// Block implements BlockConverter interface.
+func (b *Block) Block() *Block {
+ return b
+}
+
+// GetPayloads impelmemnts BlockConverter interface.
+func (b *Block) GetPayloads() [][]byte {
+ return [][]byte{}
+}
+
// BlockConverter interface define the interface for extracting block
// information from an existing object.
type BlockConverter interface {
Block() *Block
+ GetPayloads() [][]byte
}
func (b *Block) String() string {
diff --git a/core/types/validator.go b/core/types/validator.go
index 48ce586..86c3acc 100644
--- a/core/types/validator.go
+++ b/core/types/validator.go
@@ -1,15 +1,15 @@
// 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
+// 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.
+// 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
@@ -18,6 +18,8 @@
package types
import (
+ "bytes"
+
"github.com/dexon-foundation/dexon-consensus-core/common"
)
@@ -25,3 +27,18 @@ import (
type ValidatorID struct {
common.Hash
}
+
+// ValidatorIDs implements sort.Interface for ValidatorID.
+type ValidatorIDs []ValidatorID
+
+func (v ValidatorIDs) Len() int {
+ return len(v)
+}
+
+func (v ValidatorIDs) Less(i int, j int) bool {
+ return bytes.Compare([]byte(v[i].Hash[:]), []byte(v[j].Hash[:])) == -1
+}
+
+func (v ValidatorIDs) Swap(i int, j int) {
+ v[i], v[j] = v[j], v[i]
+}