diff options
-rw-r--r-- | core/consensus.go | 53 | ||||
-rw-r--r-- | core/consensus_test.go | 102 | ||||
-rw-r--r-- | core/test/governance.go | 18 | ||||
-rw-r--r-- | core/types/block.go | 1 | ||||
-rw-r--r-- | simulation/validator.go | 11 |
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{}) |