diff options
Diffstat (limited to 'integration_test/node.go')
-rw-r--r-- | integration_test/node.go | 355 |
1 files changed, 0 insertions, 355 deletions
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 -} |