aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/consensus.go53
-rw-r--r--core/consensus_test.go102
-rw-r--r--core/test/governance.go18
-rw-r--r--core/types/block.go1
-rw-r--r--simulation/validator.go11
5 files changed, 125 insertions, 60 deletions
diff --git a/core/consensus.go b/core/consensus.go
index c8b10a0..9b95e56 100644
--- a/core/consensus.go
+++ b/core/consensus.go
@@ -42,10 +42,11 @@ func (e *ErrMissingBlockInfo) Error() string {
return "missing " + e.MissingField + " in block"
}
-// Errors for sanity check error.
+// Errors for consensus core.
var (
- ErrIncorrectHash = fmt.Errorf("hash of block is incorrect")
- ErrIncorrectSignature = fmt.Errorf("signature of block is incorrect")
+ ErrIncorrectHash = fmt.Errorf("hash of block is incorrect")
+ ErrIncorrectSignature = fmt.Errorf("signature of block is incorrect")
+ ErrGenesisBlockNotEmpty = fmt.Errorf("genesis block should be empty")
)
// Consensus implements DEXON Consensus algorithm.
@@ -103,8 +104,6 @@ func (con *Consensus) sanityCheck(blockConv types.BlockConverter) (err error) {
return ErrIncorrectHash
}
- /* Disable these check before the implmentation of the signature for
- * genesis block is finished.
// Check the signer.
pubKey, err := con.sigToPub(b.Hash, b.Signature)
if err != nil {
@@ -113,7 +112,6 @@ func (con *Consensus) sanityCheck(blockConv types.BlockConverter) (err error) {
if !b.ProposerID.Equal(crypto.Keccak256Hash(pubKey.Bytes())) {
return ErrIncorrectSignature
}
- */
return nil
}
@@ -176,12 +174,20 @@ func (con *Consensus) ProcessBlock(blockConv types.BlockConverter) (err error) {
return
}
+func (con *Consensus) checkPrepareBlock(
+ b *types.Block, proposeTime time.Time) (err error) {
+ if (b.ProposerID == types.ValidatorID{}) {
+ err = &ErrMissingBlockInfo{MissingField: "ProposerID"}
+ return
+ }
+ return
+}
+
// PrepareBlock would setup header fields of block based on its ProposerID.
func (con *Consensus) PrepareBlock(blockConv types.BlockConverter,
proposeTime time.Time) (err error) {
b := blockConv.Block()
- if (b.ProposerID == types.ValidatorID{}) {
- err = &ErrMissingBlockInfo{MissingField: "ProposerID"}
+ if err = con.checkPrepareBlock(b, proposeTime); err != nil {
return
}
con.lock.RLock()
@@ -200,3 +206,34 @@ func (con *Consensus) PrepareBlock(blockConv types.BlockConverter,
blockConv.SetBlock(b)
return
}
+
+// PrepareGenesisBlock would setup header fields for genesis block.
+func (con *Consensus) PrepareGenesisBlock(blockConv types.BlockConverter,
+ proposeTime time.Time) (err error) {
+ b := blockConv.Block()
+ if err = con.checkPrepareBlock(b, proposeTime); err != nil {
+ return
+ }
+ if len(b.Payloads()) != 0 {
+ err = ErrGenesisBlockNotEmpty
+ return
+ }
+ b.Height = 0
+ b.ParentHash = common.Hash{}
+ b.Acks = make(map[common.Hash]struct{})
+ b.Timestamps = make(map[types.ValidatorID]time.Time)
+ for vID := range con.gov.GetValidatorSet() {
+ b.Timestamps[vID] = time.Time{}
+ }
+ b.Timestamps[b.ProposerID] = proposeTime
+ b.Hash, err = hashBlock(b)
+ if err != nil {
+ return
+ }
+ b.Signature, err = con.prvKey.Sign(b.Hash)
+ if err != nil {
+ return
+ }
+ blockConv.SetBlock(b)
+ return
+}
diff --git a/core/consensus_test.go b/core/consensus_test.go
index 522c566..522fcbb 100644
--- a/core/consensus_test.go
+++ b/core/consensus_test.go
@@ -36,33 +36,24 @@ type ConsensusTestSuite struct {
func (s *ConsensusTestSuite) prepareGenesisBlock(
proposerID types.ValidatorID,
- gov Governance) *types.Block {
+ con *Consensus) *types.Block {
block := &types.Block{
ProposerID: proposerID,
- ParentHash: common.Hash{},
- Height: 0,
- Acks: make(map[common.Hash]struct{}),
- Timestamps: make(map[types.ValidatorID]time.Time),
}
- for vID := range gov.GetValidatorSet() {
- block.Timestamps[vID] = time.Time{}
- }
- block.Timestamps[proposerID] = time.Now().UTC()
- var err error
- block.Hash, err = hashBlock(block)
+ err := con.PrepareGenesisBlock(block, time.Now().UTC())
s.Require().Nil(err)
return block
}
-func (s *ConsensusTestSuite) prepareConsensus(gov *test.Governance) (
- *test.App, *Consensus) {
+func (s *ConsensusTestSuite) prepareConsensus(
+ gov *test.Governance, vID types.ValidatorID) (*test.App, *Consensus) {
app := test.NewApp()
db, err := blockdb.NewMemBackedBlockDB()
s.Require().Nil(err)
- prv, err := eth.NewPrivateKey()
- s.Require().Nil(err)
+ prv, exist := gov.PrivateKeys[vID]
+ s.Require().True(exist)
con := NewConsensus(app, gov, db, prv, eth.SigToPub)
return app, con
}
@@ -81,10 +72,11 @@ func (s *ConsensusTestSuite) TestSimpleDeliverBlock() {
// This test case only works for Total Ordering with K=0.
var (
minInterval = 50 * time.Millisecond
- gov = test.NewGovernance(4, 1000)
+ gov, err = test.NewGovernance(4, 1000)
req = s.Require()
validators []types.ValidatorID
)
+ s.Require().Nil(err)
for vID := range gov.GetValidatorSet() {
validators = append(validators, vID)
@@ -96,7 +88,7 @@ func (s *ConsensusTestSuite) TestSimpleDeliverBlock() {
con *Consensus
}{}
for _, vID := range validators {
- app, con := s.prepareConsensus(gov)
+ app, con := s.prepareConsensus(gov, vID)
objs[vID] = &struct {
app *test.App
con *Consensus
@@ -110,13 +102,13 @@ func (s *ConsensusTestSuite) TestSimpleDeliverBlock() {
}
}
// Genesis blocks
- b00 := s.prepareGenesisBlock(validators[0], gov)
+ b00 := s.prepareGenesisBlock(validators[0], objs[validators[0]].con)
time.Sleep(minInterval)
- b10 := s.prepareGenesisBlock(validators[1], gov)
+ b10 := s.prepareGenesisBlock(validators[1], objs[validators[1]].con)
time.Sleep(minInterval)
- b20 := s.prepareGenesisBlock(validators[2], gov)
+ b20 := s.prepareGenesisBlock(validators[2], objs[validators[2]].con)
time.Sleep(minInterval)
- b30 := s.prepareGenesisBlock(validators[3], gov)
+ b30 := s.prepareGenesisBlock(validators[3], objs[validators[3]].con)
broadcast(b00)
broadcast(b10)
broadcast(b20)
@@ -126,7 +118,6 @@ func (s *ConsensusTestSuite) TestSimpleDeliverBlock() {
b11 := &types.Block{
ProposerID: validators[1],
}
- var err error
b11.Hash, err = hashBlock(b11)
s.Require().Nil(err)
req.Nil(objs[validators[1]].con.PrepareBlock(b11, time.Now().UTC()))
@@ -268,47 +259,78 @@ func (s *ConsensusTestSuite) TestPrepareBlock() {
// - Make sure Consensus.PrepareBlock would only attempt to
// ack the prepared block.
var (
- gov = test.NewGovernance(4, 1000)
+ gov, err = test.NewGovernance(4, 1000)
req = s.Require()
validators []types.ValidatorID
)
+ s.Require().Nil(err)
for vID := range gov.GetValidatorSet() {
validators = append(validators, vID)
}
- _, con := s.prepareConsensus(gov)
- b00 := s.prepareGenesisBlock(validators[0], gov)
- b10 := s.prepareGenesisBlock(validators[1], gov)
- b20 := s.prepareGenesisBlock(validators[2], gov)
- b30 := s.prepareGenesisBlock(validators[3], gov)
- req.Nil(con.ProcessBlock(b00))
- req.Nil(con.ProcessBlock(b10))
- req.Nil(con.ProcessBlock(b20))
- req.Nil(con.ProcessBlock(b30))
+ // Setup core.Consensus and test.App.
+ objs := map[types.ValidatorID]*struct {
+ app *test.App
+ con *Consensus
+ }{}
+ for _, vID := range validators {
+ app, con := s.prepareConsensus(gov, vID)
+ objs[vID] = &struct {
+ app *test.App
+ con *Consensus
+ }{app, con}
+ }
+ b00 := s.prepareGenesisBlock(validators[0], objs[validators[0]].con)
+ b10 := s.prepareGenesisBlock(validators[1], objs[validators[1]].con)
+ b20 := s.prepareGenesisBlock(validators[2], objs[validators[2]].con)
+ b30 := s.prepareGenesisBlock(validators[3], objs[validators[3]].con)
+ for _, obj := range objs {
+ con := obj.con
+ req.Nil(con.ProcessBlock(b00))
+ req.Nil(con.ProcessBlock(b10))
+ req.Nil(con.ProcessBlock(b20))
+ req.Nil(con.ProcessBlock(b30))
+ }
b11 := &types.Block{
ProposerID: validators[1],
}
- var err error
- b11.Hash, err = hashBlock(b11)
- s.Require().Nil(err)
// Sleep to make sure 'now' is slower than b10's timestamp.
time.Sleep(100 * time.Millisecond)
- req.Nil(con.PrepareBlock(b11, time.Now().UTC()))
+ req.Nil(objs[validators[1]].con.PrepareBlock(b11, time.Now().UTC()))
// Make sure we would assign 'now' to the timestamp belongs to
// the proposer.
req.True(
b11.Timestamps[validators[1]].Sub(
b10.Timestamps[validators[1]]) > 100*time.Millisecond)
- req.Nil(con.ProcessBlock(b11))
+ for _, obj := range objs {
+ con := obj.con
+ req.Nil(con.ProcessBlock(b11))
+ }
b12 := &types.Block{
ProposerID: validators[1],
}
- b12.Hash, err = hashBlock(b12)
- s.Require().Nil(err)
- req.Nil(con.PrepareBlock(b12, time.Now().UTC()))
+ req.Nil(objs[validators[1]].con.PrepareBlock(b12, time.Now().UTC()))
req.Len(b12.Acks, 1)
req.Contains(b12.Acks, b11.Hash)
}
+func (s *ConsensusTestSuite) TestPrepareGenesisBlock() {
+ var (
+ gov, err = test.NewGovernance(4, 1000)
+ validators []types.ValidatorID
+ )
+ s.Require().Nil(err)
+ for vID := range gov.GetValidatorSet() {
+ validators = append(validators, vID)
+ }
+ _, con := s.prepareConsensus(gov, validators[0])
+ block := &types.Block{
+ ProposerID: validators[0],
+ }
+ con.PrepareGenesisBlock(block, time.Now().UTC())
+ s.True(block.IsGenesis())
+ s.Nil(con.sanityCheck(block))
+}
+
func TestConsensus(t *testing.T) {
suite.Run(t, new(ConsensusTestSuite))
}
diff --git a/core/test/governance.go b/core/test/governance.go
index a7ae1b0..a4e86d1 100644
--- a/core/test/governance.go
+++ b/core/test/governance.go
@@ -18,8 +18,9 @@
package test
import (
- "github.com/dexon-foundation/dexon-consensus-core/common"
"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/eth"
"github.com/shopspring/decimal"
)
@@ -27,18 +28,25 @@ import (
type Governance struct {
BlockProposingInterval int
Validators map[types.ValidatorID]decimal.Decimal
+ PrivateKeys map[types.ValidatorID]crypto.PrivateKey
}
// NewGovernance constructs a Governance instance.
-func NewGovernance(validatorCount, proposingInterval int) (g *Governance) {
-
+func NewGovernance(validatorCount, proposingInterval int) (
+ g *Governance, err error) {
g = &Governance{
BlockProposingInterval: proposingInterval,
Validators: make(map[types.ValidatorID]decimal.Decimal),
+ PrivateKeys: make(map[types.ValidatorID]crypto.PrivateKey),
}
for i := 0; i < validatorCount; i++ {
- g.Validators[types.ValidatorID{Hash: common.NewRandomHash()}] =
- decimal.NewFromFloat(0)
+ prv, err := eth.NewPrivateKey()
+ if err != nil {
+ return nil, err
+ }
+ vID := types.NewValidatorID(prv.PublicKey())
+ g.Validators[vID] = decimal.NewFromFloat(0)
+ g.PrivateKeys[vID] = prv
}
return
}
diff --git a/core/types/block.go b/core/types/block.go
index 4b8398c..15fae8b 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -113,6 +113,7 @@ func (b *Block) Clone() *Block {
CompactionChainAck: CompactionChainAck{
AckingBlockHash: b.CompactionChainAck.AckingBlockHash,
},
+ Signature: b.Signature,
ConsensusInfo: ConsensusInfo{
Timestamp: b.ConsensusInfo.Timestamp,
Height: b.ConsensusInfo.Height,
diff --git a/simulation/validator.go b/simulation/validator.go
index 7ebf06b..1912bf6 100644
--- a/simulation/validator.go
+++ b/simulation/validator.go
@@ -92,13 +92,10 @@ func (v *Validator) Run() {
genesisBlock := &types.Block{
ProposerID: v.ID,
- ParentHash: common.Hash{},
- Hash: common.NewRandomHash(),
- Height: 0,
- Acks: map[common.Hash]struct{}{},
- Timestamps: map[types.ValidatorID]time.Time{
- v.ID: time.Now().UTC(),
- },
+ }
+ err := v.consensus.PrepareGenesisBlock(genesisBlock, time.Now().UTC())
+ if err != nil {
+ panic(err)
}
isStopped := make(chan struct{}, 2)
isShutdown := make(chan struct{})