// 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 // 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. // // You should have received a copy of the GNU Lesser General Public License // along with the dexon-consensus-core library. If not, see // . package simulation import ( "fmt" "time" "github.com/dexon-foundation/dexon-consensus-core/blockdb" "github.com/dexon-foundation/dexon-consensus-core/common" "github.com/dexon-foundation/dexon-consensus-core/core" "github.com/dexon-foundation/dexon-consensus-core/core/types" "github.com/dexon-foundation/dexon-consensus-core/crypto" "github.com/dexon-foundation/dexon-consensus-core/simulation/config" ) // Validator represents a validator in DexCon. type Validator struct { network Network app *simApp gov *simGovernance db blockdb.BlockDatabase config config.Validator msgChannel chan interface{} isFinished chan struct{} ID types.ValidatorID privateKey crypto.PrivateKey consensus *core.Consensus compactionChain *core.BlockChain } // NewValidator returns a new empty validator. func NewValidator( prvKey crypto.PrivateKey, config config.Validator, network Network) *Validator { id := types.NewValidatorID(prvKey.PublicKey()) db, err := blockdb.NewMemBackedBlockDB( id.String() + ".blockdb") if err != nil { panic(err) } gov := newSimGovernance(config.Num, config.Consensus) gov.addValidator(id) return &Validator{ ID: id, config: config, network: network, app: newSimApp(id, network), gov: gov, db: db, isFinished: make(chan struct{}), } } // GetID returns the ID of validator. func (v *Validator) GetID() types.ValidatorID { return v.ID } // Run starts the validator. func (v *Validator) Run() { v.msgChannel = v.network.Join(v) 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(), }, } isStopped := make(chan struct{}, 2) isShutdown := make(chan struct{}) v.app.addBlock(genesisBlock) v.BroadcastGenesisBlock(genesisBlock) go v.MsgServer(isStopped, genesisBlock) go v.CheckServerInfo(isShutdown) go v.BlockProposer(isStopped, isShutdown) // Blocks forever. <-isStopped if err := v.db.Close(); err != nil { fmt.Println(err) } v.network.NotifyServer(Message{ Type: shutdownAck, }) v.isFinished <- struct{}{} } // Wait for the validator to stop (if peerServer told it to). func (v *Validator) Wait() { <-v.isFinished } // CheckServerInfo will check the info from the peerServer and update // validator's status if needed. func (v *Validator) CheckServerInfo(isShutdown chan struct{}) { for { infoMsg := v.network.GetServerInfo() if infoMsg.Status == statusShutdown { isShutdown <- struct{}{} break } time.Sleep(250 * time.Millisecond) } } // MsgServer listen to the network channel for message and handle it. func (v *Validator) MsgServer( isStopped chan struct{}, genesisBlock *types.Block) { pendingBlocks := []*types.Block{genesisBlock} for { var msg interface{} select { case msg = <-v.msgChannel: case <-isStopped: return } switch val := msg.(type) { case *types.Block: v.app.addBlock(val) if v.consensus != nil { if err := v.consensus.ProcessBlock(val); err != nil { fmt.Println(err) //panic(err) } } else { pendingBlocks = append(pendingBlocks, val) if val.IsGenesis() { v.gov.addValidator(val.ProposerID) } validatorSet := v.gov.GetValidatorSet() if len(validatorSet) != v.config.Num { // We don't collect all validators yet. break } v.consensus = core.NewConsensus(v.app, v.gov, v.db) for _, b := range pendingBlocks { if err := v.consensus.ProcessBlock(b); err != nil { fmt.Println(err) //panic(err) } } pendingBlocks = pendingBlocks[:0] } } } } // BroadcastGenesisBlock broadcasts genesis block to all peers. func (v *Validator) BroadcastGenesisBlock(genesisBlock *types.Block) { // Wait until all peer joined the network. for v.network.NumPeers() != v.config.Num { time.Sleep(time.Second) } v.network.BroadcastBlock(genesisBlock) } // BlockProposer propose blocks to be send to the DEXON network. func (v *Validator) BlockProposer(isStopped, isShutdown chan struct{}) { // Wait until all genesis blocks are received. for v.consensus == nil { time.Sleep(time.Second) } model := &NormalNetwork{ Sigma: v.config.ProposeIntervalSigma, Mean: v.config.ProposeIntervalMean, } ProposingBlockLoop: for { time.Sleep(model.Delay()) block := &types.Block{ ProposerID: v.ID, Hash: common.NewRandomHash(), } if err := v.consensus.PrepareBlock(block, time.Now().UTC()); err != nil { panic(err) } v.app.addBlock(block) if err := v.consensus.ProcessBlock(block); err != nil { fmt.Println(err) //panic(err) } v.network.BroadcastBlock(block) select { case <-isShutdown: isStopped <- struct{}{} isStopped <- struct{}{} break ProposingBlockLoop default: break } } }