diff options
author | Mission Liao <mission.liao@dexon.org> | 2019-02-15 14:18:59 +0800 |
---|---|---|
committer | Jimmy Hu <jimmy.hu@dexon.org> | 2019-02-19 10:48:50 +0800 |
commit | 4dbdc22e355cf1f6f0c39af1b2f3737b7527bc0c (patch) | |
tree | 625b7d34aa700d072ffb8e68dc89ed3936b76d29 /integration_test | |
parent | e4825619fb2499f5f534537c1a4d52d3e0bcacfe (diff) | |
download | tangerine-consensus-4dbdc22e355cf1f6f0c39af1b2f3737b7527bc0c.tar tangerine-consensus-4dbdc22e355cf1f6f0c39af1b2f3737b7527bc0c.tar.gz tangerine-consensus-4dbdc22e355cf1f6f0c39af1b2f3737b7527bc0c.tar.bz2 tangerine-consensus-4dbdc22e355cf1f6f0c39af1b2f3737b7527bc0c.tar.lz tangerine-consensus-4dbdc22e355cf1f6f0c39af1b2f3737b7527bc0c.tar.xz tangerine-consensus-4dbdc22e355cf1f6f0c39af1b2f3737b7527bc0c.tar.zst tangerine-consensus-4dbdc22e355cf1f6f0c39af1b2f3737b7527bc0c.zip |
big-bang: single chain (#446)
Diffstat (limited to 'integration_test')
-rw-r--r-- | integration_test/consensus_test.go | 38 | ||||
-rw-r--r-- | integration_test/node.go | 355 | ||||
-rw-r--r-- | integration_test/stats.go | 192 | ||||
-rw-r--r-- | integration_test/stats_test.go | 84 | ||||
-rw-r--r-- | integration_test/utils.go | 140 | ||||
-rw-r--r-- | integration_test/utils_test.go | 61 | ||||
-rw-r--r-- | integration_test/with_scheduler_test.go | 150 |
7 files changed, 26 insertions, 994 deletions
diff --git a/integration_test/consensus_test.go b/integration_test/consensus_test.go index 8af118b..454ed1e 100644 --- a/integration_test/consensus_test.go +++ b/integration_test/consensus_test.go @@ -239,10 +239,10 @@ Loop: s.verifyNodes(nodes) } -func (s *ConsensusTestSuite) TestNumChainsChange() { +func (s *ConsensusTestSuite) TestSetSizeChange() { var ( req = s.Require() - peerCount = 4 + peerCount = 7 dMoment = time.Now().UTC() untilRound = uint64(6) ) @@ -261,24 +261,34 @@ func (s *ConsensusTestSuite) TestNumChainsChange() { req.NoError(err) req.NoError(seedGov.State().RequestChange( test.StateChangeRoundInterval, 45*time.Second)) + req.NoError(seedGov.State().RequestChange( + test.StateChangeNotarySetSize, uint32(4))) + req.NoError(seedGov.State().RequestChange( + test.StateChangeDKGSetSize, uint32(4))) seedGov.CatchUpWithRound(0) // Setup configuration for round 0 and round 1. req.NoError(seedGov.State().RequestChange( - test.StateChangeNumChains, uint32(5))) - req.NoError(seedGov.State().RequestChange( test.StateChangeRoundInterval, 55*time.Second)) + req.NoError(seedGov.State().RequestChange( + test.StateChangeNotarySetSize, uint32(5))) + req.NoError(seedGov.State().RequestChange( + test.StateChangeDKGSetSize, uint32(6))) seedGov.CatchUpWithRound(1) // Setup configuration for round 2. req.NoError(seedGov.State().RequestChange( - test.StateChangeNumChains, uint32(6))) - req.NoError(seedGov.State().RequestChange( test.StateChangeRoundInterval, 55*time.Second)) + req.NoError(seedGov.State().RequestChange( + test.StateChangeNotarySetSize, uint32(6))) + req.NoError(seedGov.State().RequestChange( + test.StateChangeDKGSetSize, uint32(5))) seedGov.CatchUpWithRound(2) // Setup configuration for round 3. req.NoError(seedGov.State().RequestChange( - test.StateChangeNumChains, uint32(5))) - req.NoError(seedGov.State().RequestChange( test.StateChangeRoundInterval, 75*time.Second)) + req.NoError(seedGov.State().RequestChange( + test.StateChangeNotarySetSize, uint32(4))) + req.NoError(seedGov.State().RequestChange( + test.StateChangeDKGSetSize, uint32(4))) seedGov.CatchUpWithRound(3) // Setup nodes. nodes := s.setupNodes(dMoment, prvKeys, seedGov) @@ -289,14 +299,18 @@ func (s *ConsensusTestSuite) TestNumChainsChange() { } // Register configuration changes for round 4. req.NoError(pickedNode.gov.RegisterConfigChange( - 4, test.StateChangeNumChains, uint32(4))) - req.NoError(pickedNode.gov.RegisterConfigChange( 4, test.StateChangeRoundInterval, 45*time.Second)) + req.NoError(seedGov.State().RequestChange( + test.StateChangeNotarySetSize, uint32(5))) + req.NoError(seedGov.State().RequestChange( + test.StateChangeDKGSetSize, uint32(5))) // Register configuration changes for round 5. req.NoError(pickedNode.gov.RegisterConfigChange( - 5, test.StateChangeNumChains, uint32(5))) - req.NoError(pickedNode.gov.RegisterConfigChange( 5, test.StateChangeRoundInterval, 55*time.Second)) + req.NoError(seedGov.State().RequestChange( + test.StateChangeNotarySetSize, uint32(4))) + req.NoError(seedGov.State().RequestChange( + test.StateChangeDKGSetSize, uint32(4))) // Run test. for _, n := range nodes { go n.con.Run() diff --git a/integration_test/node.go b/integration_test/node.go deleted file mode 100644 index 732e54a..0000000 --- a/integration_test/node.go +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus 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 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 library. If not, see -// <http://www.gnu.org/licenses/>. - -package integration - -import ( - "fmt" - "time" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/crypto" - "github.com/dexon-foundation/dexon-consensus/core/db" - "github.com/dexon-foundation/dexon-consensus/core/test" - "github.com/dexon-foundation/dexon-consensus/core/types" - "github.com/dexon-foundation/dexon-consensus/core/utils" -) - -type consensusEventType int - -const ( - evtProposeBlock consensusEventType = iota - evtReceiveBlock -) - -type consensusEventPayload struct { - Type consensusEventType - PiggyBack interface{} -} - -// newProposeBlockEvent constructs an test.Event that would trigger -// block proposing. -func newProposeBlockEvent(nID types.NodeID, - roundID uint64, chainID uint32, when time.Time) *test.Event { - return test.NewEvent(nID, when, &consensusEventPayload{ - Type: evtProposeBlock, - PiggyBack: &struct { - round uint64 - chain uint32 - }{roundID, chainID}, - }) -} - -// newReceiveBlockEvent constructs an test.Event that would trigger -// block received. -func newReceiveBlockEvent( - nID types.NodeID, when time.Time, block *types.Block) *test.Event { - - return test.NewEvent(nID, when, &consensusEventPayload{ - Type: evtReceiveBlock, - PiggyBack: block, - }) -} - -// Node is designed to work with test.Scheduler. -type Node struct { - ID types.NodeID - ownChains []uint32 - roundEndTimes []time.Time - roundToNotify uint64 - lattice *core.Lattice - appModule *test.App - stateModule *test.State - govModule *test.Governance - dbModule db.Database - broadcastTargets map[types.NodeID]struct{} - networkLatency test.LatencyModel - proposingLatency test.LatencyModel - prevFinalHeight uint64 - pendings []*types.Block - prevHash common.Hash - // This variable caches the maximum NumChains seen by this node when - // it's notified for round switching. - latticeMaxNumChains uint32 -} - -// newNode constructs an instance of Node. -func newNode( - gov *test.Governance, - privateKey crypto.PrivateKey, - dMoment time.Time, - ownChains []uint32, - networkLatency test.LatencyModel, - proposingLatency test.LatencyModel) (*Node, error) { - // Load all configs prepared in core.Governance into core.Lattice. - copiedGov := gov.Clone() - configs := loadAllConfigs(copiedGov) - // Setup db. - dbInst, err := db.NewMemBackedDB() - if err != nil { - return nil, err - } - // Setup test.App - app := test.NewApp(0, copiedGov) - // Setup lattice instance. - lattice := core.NewLattice( - dMoment, - 0, - configs[0], - utils.NewSigner(privateKey), - app, - app, - dbInst, - &common.NullLogger{}) - n := &Node{ - ID: types.NewNodeID(privateKey.PublicKey()), - ownChains: ownChains, - roundEndTimes: genRoundEndTimes(configs, dMoment), - roundToNotify: 2, - networkLatency: networkLatency, - proposingLatency: proposingLatency, - appModule: app, - stateModule: copiedGov.State(), - dbModule: dbInst, - govModule: copiedGov, - lattice: lattice, - latticeMaxNumChains: configs[0].NumChains, - } - for idx, config := range configs[1:] { - if err := lattice.AppendConfig(uint64(idx+1), config); err != nil { - return nil, err - } - if config.NumChains > n.latticeMaxNumChains { - n.latticeMaxNumChains = config.NumChains - } - } - return n, nil -} - -// Handle implements test.EventHandler interface. -func (n *Node) Handle(e *test.Event) (events []*test.Event) { - payload := e.Payload.(*consensusEventPayload) - switch payload.Type { - case evtProposeBlock: - events, e.ExecError = n.handleProposeBlock(e.Time, payload.PiggyBack) - case evtReceiveBlock: - events, e.ExecError = n.handleReceiveBlock(payload.PiggyBack) - default: - panic(fmt.Errorf("unknown consensus event type: %v", payload.Type)) - } - return -} - -func (n *Node) handleProposeBlock(when time.Time, payload interface{}) ( - events []*test.Event, err error) { - pos := payload.(*struct { - round uint64 - chain uint32 - }) - b, err := n.prepareBlock(pos.round, pos.chain, when) - if err != nil { - if err == utils.ErrInvalidChainID { - // This chain is not included in this round, retry in next round. - events = append(events, newProposeBlockEvent( - n.ID, b.Position.Round+1, b.Position.ChainID, - n.roundEndTimes[b.Position.Round])) - } - return - } - if events, err = n.processBlock(b); err != nil { - // It's shouldn't be error when prepared. - panic(err) - } - // Create 'block received' event for each other nodes. - for nID := range n.broadcastTargets { - events = append(events, newReceiveBlockEvent( - nID, when.Add(n.networkLatency.Delay()), b.Clone())) - } - // Create next 'block proposing' event for this nodes. - events = append(events, newProposeBlockEvent(n.ID, - b.Position.Round, - b.Position.ChainID, - when.Add(n.proposingLatency.Delay()))) - return -} - -func (n *Node) handleReceiveBlock(piggyback interface{}) ( - events []*test.Event, err error) { - events, err = n.processBlock(piggyback.(*types.Block)) - if err != nil { - panic(err) - } - return -} - -func (n *Node) prepareBlock( - round uint64, chainID uint32, when time.Time) (b *types.Block, err error) { - b = &types.Block{ - Position: types.Position{ - Round: round, - ChainID: chainID, - }} - if err = n.lattice.PrepareBlock(b, when); err != nil { - if err == core.ErrRoundNotSwitch { - b.Position.Round++ - err = n.lattice.PrepareBlock(b, when) - } - } - return -} - -func (n *Node) processBlock(b *types.Block) (events []*test.Event, err error) { - // TODO(mission): this segment of code is identical to testLatticeMgr in - // core/lattice_test.go, except the compaction-chain part. - var ( - delivered []*types.Block - ) - n.pendings = append([]*types.Block{b}, n.pendings...) - for { - var ( - newPendings []*types.Block - tmpDelivered []*types.Block - tmpErr error - ) - updated := false - for _, p := range n.pendings { - if tmpErr = n.lattice.SanityCheck(p, false); tmpErr != nil { - if tmpErr == core.ErrRetrySanityCheckLater { - newPendings = append(newPendings, p) - } else { - // Those blocks are prepared by lattice module, they should - // not be wrong. - panic(tmpErr) - } - continue - } - if tmpDelivered, tmpErr = - n.lattice.ProcessBlock(p); tmpErr != nil { - // It's not allowed that sanity checked block failed to - // be added to lattice. - panic(tmpErr) - } - delivered = append(delivered, tmpDelivered...) - updated = true - } - n.pendings = newPendings - if !updated { - break - } - } - // Deliver blocks. - for _, b = range delivered { - b.Finalization.Height = n.prevFinalHeight + 1 - b.Finalization.ParentHash = n.prevHash - n.appModule.BlockDelivered(b.Hash, b.Position, b.Finalization) - n.prevFinalHeight++ - n.prevHash = b.Hash - events = append(events, n.checkRoundSwitch(b)...) - } - if err = n.lattice.PurgeBlocks(delivered); err != nil { - panic(err) - } - return -} - -func (n *Node) checkRoundSwitch(b *types.Block) (evts []*test.Event) { - if !b.Timestamp.After(n.roundEndTimes[b.Position.Round]) { - return - } - if b.Position.Round+2 != n.roundToNotify { - return - } - // Handle round switching logic. - n.govModule.NotifyRound(n.roundToNotify) - if n.roundToNotify == uint64(len(n.roundEndTimes)) { - config := n.govModule.Configuration(n.roundToNotify) - if config == nil { - panic(fmt.Errorf( - "config is not ready for round: %v", n.roundToNotify-1)) - } - // Cache round ended time for each round. - n.roundEndTimes = append(n.roundEndTimes, - n.roundEndTimes[len(n.roundEndTimes)-1].Add( - config.RoundInterval)) - // Add new config to lattice module. - if err := n.lattice.AppendConfig(n.roundToNotify, config); err != nil { - panic(err) - } - if config.NumChains > n.latticeMaxNumChains { - // We can be sure that lattice module can support this number of - // chains. - for _, chainID := range n.ownChains { - if chainID < n.latticeMaxNumChains { - continue - } - if chainID >= config.NumChains { - continue - } - // For newly added chains, add block proposing seed event. - evts = append(evts, newProposeBlockEvent(n.ID, n.roundToNotify, - chainID, n.roundEndTimes[n.roundToNotify-1])) - } - n.latticeMaxNumChains = config.NumChains - } - } else if n.roundToNotify > uint64(len(n.roundEndTimes)) { - panic(fmt.Errorf( - "config notification not incremental: %v, cached configs: %v", - n.roundToNotify, len(n.roundEndTimes))) - } - n.roundToNotify++ - return -} - -// Bootstrap this node with block proposing event. -func (n *Node) Bootstrap(sch *test.Scheduler, now time.Time) (err error) { - sch.RegisterEventHandler(n.ID, n) - for _, chainID := range n.ownChains { - if chainID >= n.latticeMaxNumChains { - continue - } - err = sch.Seed(newProposeBlockEvent(n.ID, 0, chainID, now)) - if err != nil { - return - } - } - return -} - -func (n *Node) setBroadcastTargets(targets map[types.NodeID]struct{}) { - // Clone targets, except self. - targetsCopy := make(map[types.NodeID]struct{}) - for nID := range targets { - if nID == n.ID { - continue - } - targetsCopy[nID] = struct{}{} - } - n.broadcastTargets = targetsCopy -} - -func (n *Node) app() *test.App { - return n.appModule -} - -func (n *Node) db() db.Database { - return n.dbModule -} - -func (n *Node) gov() *test.Governance { - return n.govModule -} diff --git a/integration_test/stats.go b/integration_test/stats.go deleted file mode 100644 index 5d66e29..0000000 --- a/integration_test/stats.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus 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 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 library. If not, see -// <http://www.gnu.org/licenses/>. - -package integration - -import ( - "fmt" - "time" - - "github.com/dexon-foundation/dexon-consensus/core/test" - "github.com/dexon-foundation/dexon-consensus/core/types" -) - -// Errors when calculating statistics for events. -var ( - ErrUnknownEvent = fmt.Errorf("unknown event") - ErrUnknownConsensusEventType = fmt.Errorf("unknown consensus event type") -) - -// StatsSet represents accumulatee result of a group of related events -// (ex. All events from one node). -type StatsSet struct { - ProposedBlockCount int - ReceivedBlockCount int - ConfirmedBlockCount int - TotalOrderedBlockCount int - DeliveredBlockCount int - ProposingLatency time.Duration - ReceivingLatency time.Duration - PrepareExecLatency time.Duration - ProcessExecLatency time.Duration -} - -// newBlockProposeEvent accumulates a block proposing event. -func (s *StatsSet) newBlockProposeEvent( - e *test.Event, payload *consensusEventPayload, history []*test.Event) { - - // Find previous block proposing event. - if e.ParentHistoryIndex != -1 { - parentEvent := history[e.ParentHistoryIndex] - s.ProposingLatency += - e.Time.Sub(parentEvent.Time) - parentEvent.ExecInterval - } - s.PrepareExecLatency += e.ExecInterval - s.ProposedBlockCount++ -} - -// newBlockReceiveEvent accumulates a block received event. -func (s *StatsSet) newBlockReceiveEvent( - e *test.Event, - payload *consensusEventPayload, - history []*test.Event, - app *test.App) { - - // Find previous block proposing event. - parentEvent := history[e.ParentHistoryIndex] - s.ReceivingLatency += - e.Time.Sub(parentEvent.Time) - parentEvent.ExecInterval - s.ProcessExecLatency += e.ExecInterval - s.ReceivedBlockCount++ - - // Find statistics from test.App - block := payload.PiggyBack.(*types.Block) - app.WithLock(func(app *test.App) { - // Is this block confirmed? - if _, exists := app.Confirmed[block.Hash]; !exists { - return - } - s.ConfirmedBlockCount++ - // Is this block total ordered? - if _, exists := app.TotalOrderedByHash[block.Hash]; !exists { - return - } - s.TotalOrderedBlockCount++ - - // Is this block delivered? - if _, exists := app.Delivered[block.Hash]; !exists { - return - } - s.DeliveredBlockCount++ - }) -} - -// done would divide the latencies we cached with related event count. This way -// to calculate average latency is more accurate. -func (s *StatsSet) done(nodeCount int) { - s.ProposingLatency /= time.Duration(s.ProposedBlockCount - nodeCount) - s.ReceivingLatency /= time.Duration(s.ReceivedBlockCount) - s.PrepareExecLatency /= time.Duration(s.ProposedBlockCount) - s.ProcessExecLatency /= time.Duration(s.ReceivedBlockCount) -} - -// Stats is statistics of a slice of test.Event generated by nodes. -type Stats struct { - ByNode map[types.NodeID]*StatsSet - All *StatsSet - BPS float64 - ExecutionTime time.Duration -} - -// NewStats constructs an Stats instance by providing a slice of -// test.Event. -func NewStats( - history []*test.Event, apps map[types.NodeID]*test.App) ( - stats *Stats, err error) { - - stats = &Stats{ - ByNode: make(map[types.NodeID]*StatsSet), - All: &StatsSet{}, - } - if err = stats.calculate(history, apps); err != nil { - stats = nil - } - stats.summary(history) - return -} - -func (stats *Stats) calculate( - history []*test.Event, apps map[types.NodeID]*test.App) error { - - defer func() { - stats.All.done(len(stats.ByNode)) - for _, set := range stats.ByNode { - set.done(1) - } - }() - - for _, e := range history { - payload, ok := e.Payload.(*consensusEventPayload) - if !ok { - return ErrUnknownEvent - } - switch payload.Type { - case evtProposeBlock: - stats.All.newBlockProposeEvent( - e, payload, history) - stats.getStatsSetByNode(e.NodeID).newBlockProposeEvent( - e, payload, history) - case evtReceiveBlock: - stats.All.newBlockReceiveEvent( - e, payload, history, apps[e.NodeID]) - stats.getStatsSetByNode(e.NodeID).newBlockReceiveEvent( - e, payload, history, apps[e.NodeID]) - default: - return ErrUnknownConsensusEventType - } - } - return nil -} - -func (stats *Stats) getStatsSetByNode( - vID types.NodeID) (s *StatsSet) { - - s = stats.ByNode[vID] - if s == nil { - s = &StatsSet{} - stats.ByNode[vID] = s - } - return -} - -func (stats *Stats) summary(history []*test.Event) { - // Find average delivered block count among all blocks. - totalConfirmedBlocks := 0 - for _, s := range stats.ByNode { - totalConfirmedBlocks += s.DeliveredBlockCount - } - averageConfirmedBlocks := totalConfirmedBlocks / len(stats.ByNode) - - // Find execution time. - // Note: it's a simplified way to calculate the execution time: - // the latest event might not be at the end of history when - // the number of worker routine is larger than 1. - stats.ExecutionTime = history[len(history)-1].Time.Sub(history[0].Time) - // Calculate BPS. - latencyAsSecond := stats.ExecutionTime.Nanoseconds() / (1000 * 1000 * 1000) - stats.BPS = float64(averageConfirmedBlocks) / float64(latencyAsSecond) -} diff --git a/integration_test/stats_test.go b/integration_test/stats_test.go deleted file mode 100644 index 95bd5ac..0000000 --- a/integration_test/stats_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus 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 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 library. If not, see -// <http://www.gnu.org/licenses/>. - -package integration - -import ( - "testing" - "time" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/test" - "github.com/stretchr/testify/suite" -) - -type EventStatsTestSuite struct { - suite.Suite -} - -func (s *EventStatsTestSuite) TestCalculate() { - // Setup a test with fixed latency in proposing and network, - // and make sure the calculated statistics is expected. - var ( - networkLatency = &test.FixedLatencyModel{Latency: 100} - proposingLatency = &test.FixedLatencyModel{Latency: 300} - req = s.Require() - ) - prvKeys, pubKeys, err := test.NewKeys(7) - req.NoError(err) - gov, err := test.NewGovernance( - test.NewState( - pubKeys, 100*time.Millisecond, &common.NullLogger{}, true), - core.ConfigRoundShift) - req.NoError(err) - nodes, err := PrepareNodes( - gov, prvKeys, 7, networkLatency, proposingLatency) - req.NoError(err) - apps, dbs := CollectAppAndDBFromNodes(nodes) - sch := test.NewScheduler(test.NewStopByConfirmedBlocks(50, apps, dbs)) - now := time.Now().UTC() - for _, n := range nodes { - req.NoError(n.Bootstrap(sch, now)) - } - sch.Run(10) - req.Nil(VerifyApps(apps)) - // Check total statistics result. - stats, err := NewStats(sch.CloneExecutionHistory(), apps) - req.Nil(err) - req.True(stats.All.ProposedBlockCount > 350) - req.True(stats.All.ReceivedBlockCount > 350) - req.True(stats.All.ConfirmedBlockCount > 350) - req.True(stats.All.TotalOrderedBlockCount >= 350) - req.True(stats.All.DeliveredBlockCount >= 350) - req.Equal(stats.All.ProposingLatency, 300*time.Millisecond) - req.Equal(stats.All.ReceivingLatency, 100*time.Millisecond) - // Check statistics for each node. - for _, vStats := range stats.ByNode { - req.True(vStats.ProposedBlockCount > 50) - req.True(vStats.ReceivedBlockCount > 50) - req.True(vStats.ConfirmedBlockCount > 50) - req.True(vStats.TotalOrderedBlockCount >= 50) - req.True(vStats.DeliveredBlockCount >= 50) - req.Equal(vStats.ProposingLatency, 300*time.Millisecond) - req.Equal(vStats.ReceivingLatency, 100*time.Millisecond) - } -} - -func TestEventStats(t *testing.T) { - suite.Run(t, new(EventStatsTestSuite)) -} diff --git a/integration_test/utils.go b/integration_test/utils.go deleted file mode 100644 index 2fbfa1a..0000000 --- a/integration_test/utils.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus 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 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 library. If not, see -// <http://www.gnu.org/licenses/>. - -package integration - -import ( - "errors" - "time" - - "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/crypto" - "github.com/dexon-foundation/dexon-consensus/core/db" - "github.com/dexon-foundation/dexon-consensus/core/test" - "github.com/dexon-foundation/dexon-consensus/core/types" -) - -func genRoundEndTimes( - configs []*types.Config, dMoment time.Time) (ends []time.Time) { - now := dMoment - for _, config := range configs { - now = now.Add(config.RoundInterval) - ends = append(ends, now) - } - return -} - -// loadAllConfigs loads all prepared configuration from governance, -// starts from round 0. -func loadAllConfigs(gov core.Governance) (configs []*types.Config) { - var round uint64 - for { - config := gov.Configuration(round) - if config == nil { - break - } - configs = append(configs, config) - round++ - } - return -} - -// decideOwnChains compute which chainIDs belongs to this node. -func decideOwnChains(numChains uint32, numNodes, id int) (own []uint32) { - var cur = uint32(id) - if numNodes == 0 { - panic(errors.New("attempt to arrange chains on 0 nodes")) - } - for { - if cur >= numChains { - break - } - own = append(own, cur) - cur += uint32(numNodes) - } - return -} - -// PrepareNodes setups nodes for testing. -func PrepareNodes( - gov *test.Governance, - prvKeys []crypto.PrivateKey, - maxNumChains uint32, - networkLatency, proposingLatency test.LatencyModel) ( - nodes map[types.NodeID]*Node, err error) { - if maxNumChains == 0 { - err = errors.New("zero NumChains is unexpected") - return - } - // Setup nodes, count of nodes is derived from the count of private keys - // hold in Governance. - nodes = make(map[types.NodeID]*Node) - dMoment := time.Now().UTC() - broadcastTargets := make(map[types.NodeID]struct{}) - for idx, prvKey := range prvKeys { - nID := types.NewNodeID(prvKey.PublicKey()) - broadcastTargets[nID] = struct{}{} - // Decides which chains are owned by this node. - if nodes[nID], err = newNode( - gov, - prvKey, - dMoment, - decideOwnChains(maxNumChains, len(prvKeys), idx), - networkLatency, - proposingLatency); err != nil { - return - } - } - // Assign broadcast targets. - for _, n := range nodes { - n.setBroadcastTargets(broadcastTargets) - n.gov().State().SwitchToRemoteMode() - } - return -} - -// VerifyApps is a helper to check delivery between test.Apps -func VerifyApps(apps map[types.NodeID]*test.App) (err error) { - for vFrom, fromApp := range apps { - if err = fromApp.Verify(); err != nil { - return - } - for vTo, toApp := range apps { - if vFrom == vTo { - continue - } - if err = fromApp.Compare(toApp); err != nil { - return - } - } - } - return -} - -// CollectAppAndDBFromNodes collects test.App and db.Database -// from nodes. -func CollectAppAndDBFromNodes(nodes map[types.NodeID]*Node) ( - apps map[types.NodeID]*test.App, - dbs map[types.NodeID]db.Database) { - apps = make(map[types.NodeID]*test.App) - dbs = make(map[types.NodeID]db.Database) - for nID, node := range nodes { - apps[nID] = node.app() - dbs[nID] = node.db() - } - return -} diff --git a/integration_test/utils_test.go b/integration_test/utils_test.go deleted file mode 100644 index 99b62c1..0000000 --- a/integration_test/utils_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus 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 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 library. If not, see -// <http://www.gnu.org/licenses/>. - -package integration - -import ( - "testing" - - "github.com/stretchr/testify/suite" -) - -type UtilsTestSuite struct { - suite.Suite -} - -func (s *UtilsTestSuite) TestDecideOwnChains() { - // Basic test for each node index. - s.Empty(decideOwnChains(1, 1, 1)) - s.Equal(decideOwnChains(1, 1, 0), []uint32{0}) - s.Equal(decideOwnChains(30, 7, 4), []uint32{4, 11, 18, 25}) - // Make sure every chain is covered. - isAllCovered := func(numChains uint32, numNodes int) bool { - if numNodes == 0 { - decideOwnChains(numChains, numNodes, 0) - return false - } - covered := make(map[uint32]struct{}) - for i := 0; i < numNodes; i++ { - for _, chainID := range decideOwnChains(numChains, numNodes, i) { - s.Require().True(chainID < numChains) - covered[chainID] = struct{}{} - } - } - return uint32(len(covered)) == numChains - } - s.True(isAllCovered(100, 33)) - s.True(isAllCovered(100, 200)) - s.True(isAllCovered(100, 50)) - s.True(isAllCovered(100, 1)) - s.Panics(func() { - isAllCovered(100, 0) - }) -} - -func TestUtils(t *testing.T) { - suite.Run(t, new(UtilsTestSuite)) -} diff --git a/integration_test/with_scheduler_test.go b/integration_test/with_scheduler_test.go deleted file mode 100644 index 66fc9b8..0000000 --- a/integration_test/with_scheduler_test.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2018 The dexon-consensus Authors -// This file is part of the dexon-consensus library. -// -// The dexon-consensus 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 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 library. If not, see -// <http://www.gnu.org/licenses/>. - -package integration - -import ( - "testing" - "time" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core" - "github.com/dexon-foundation/dexon-consensus/core/test" - "github.com/stretchr/testify/suite" -) - -type WithSchedulerTestSuite struct { - suite.Suite -} - -func (s *WithSchedulerTestSuite) TestNonByzantine() { - var ( - networkLatency = &test.NormalLatencyModel{ - Sigma: 20, - Mean: 250, - } - proposingLatency = &test.NormalLatencyModel{ - Sigma: 30, - Mean: 500, - } - numNodes = 25 - req = s.Require() - ) - if testing.Short() { - numNodes = 7 - } - // Setup key pairs. - prvKeys, pubKeys, err := test.NewKeys(numNodes) - req.NoError(err) - // Setup governance. - gov, err := test.NewGovernance( - test.NewState( - pubKeys, 250*time.Millisecond, &common.NullLogger{}, true), - core.ConfigRoundShift) - req.NoError(err) - // Setup nodes. - nodes, err := PrepareNodes( - gov, prvKeys, 25, networkLatency, proposingLatency) - req.NoError(err) - // Setup scheduler. - apps, dbs := CollectAppAndDBFromNodes(nodes) - now := time.Now().UTC() - sch := test.NewScheduler(test.NewStopByConfirmedBlocks(50, apps, dbs)) - for _, n := range nodes { - req.NoError(n.Bootstrap(sch, now)) - } - sch.Run(4) - // Check results by comparing test.App instances. - req.NoError(VerifyApps(apps)) -} - -func (s *WithSchedulerTestSuite) TestConfigurationChange() { - // This test case verify the correctness of core.Lattice when configuration - // changes. - // - Configuration changes are registered at 'pickedNode', and would carried - // in blocks' payload and broadcast to other nodes. - var ( - networkLatency = &test.NormalLatencyModel{ - Sigma: 20, - Mean: 250, - } - proposingLatency = &test.NormalLatencyModel{ - Sigma: 30, - Mean: 500, - } - numNodes = 4 - req = s.Require() - maxNumChains = uint32(9) - ) - // Setup key pairs. - prvKeys, pubKeys, err := test.NewKeys(numNodes) - req.NoError(err) - // Setup governance. - gov, err := test.NewGovernance( - test.NewState( - pubKeys, 250*time.Millisecond, &common.NullLogger{}, true), - core.ConfigRoundShift) - req.NoError(err) - // Change default round interval, expect 1 round produce 30 blocks. - gov.State().RequestChange(test.StateChangeRoundInterval, 15*time.Second) - // Setup nodes. - nodes, err := PrepareNodes( - gov, prvKeys, maxNumChains, networkLatency, proposingLatency) - req.NoError(err) - // Set scheduler. - apps, dbs := CollectAppAndDBFromNodes(nodes) - now := time.Now().UTC() - sch := test.NewScheduler(test.NewStopByRound(9, apps, dbs)) - for _, n := range nodes { - req.NoError(n.Bootstrap(sch, now)) - } - // Register some configuration changes at some node. - var pickedNode *Node - for _, pickedNode = range nodes { - break - } - // Config changes for round 5, numChains from 4 to 7. - req.NoError(pickedNode.gov().RegisterConfigChange( - 5, test.StateChangeNumChains, uint32(7))) - req.NoError(pickedNode.gov().RegisterConfigChange( - 5, test.StateChangeK, 3)) - req.NoError(pickedNode.gov().RegisterConfigChange( - 5, test.StateChangePhiRatio, float32(0.5))) - // Config changes for round 6, numChains from 7 to 9. - req.NoError(pickedNode.gov().RegisterConfigChange( - 6, test.StateChangeNumChains, maxNumChains)) - req.NoError(pickedNode.gov().RegisterConfigChange( - 6, test.StateChangeK, 0)) - // Config changes for round 7, numChains from 9 to 7. - req.NoError(pickedNode.gov().RegisterConfigChange( - 7, test.StateChangeNumChains, uint32(7))) - req.NoError(pickedNode.gov().RegisterConfigChange( - 7, test.StateChangeK, 1)) - // Config changes for round 8, numChains from 7 to 5. - req.NoError(pickedNode.gov().RegisterConfigChange( - 8, test.StateChangeNumChains, uint32(5))) - req.NoError(pickedNode.gov().RegisterConfigChange( - 8, test.StateChangeK, 1)) - // Perform test. - sch.Run(4) - // Check results by comparing test.App instances. - req.NoError(VerifyApps(apps)) -} - -func TestWithScheduler(t *testing.T) { - suite.Run(t, new(WithSchedulerTestSuite)) -} |