aboutsummaryrefslogblamecommitdiffstats
path: root/simulation/validator.go
blob: c5bbe15d11646e183b55209367df0e00873b3e26 (plain) (tree)



















                                                                               
             

              



                                                                     
                                                                 




                                                                            
                       

                              
                                     

                                   
                                   
                                

                                         
                                         
                                       
                                        



                                              
                                 
                                
                                     
 

                                                      

                                               


                          
                                                             
                            
                          


                                    
                                                   
                                
                               
                                                







                                               



                                        









                                                            
                                           

                                         
                                    

                                               

                                                 

                          
                   


                                            















                                                                     
                                                     




                                                  


                                                                     



                                                             
             





                                          


                                          
                                           






                                                                                     
                                                    















                                                                                           



                 
                                                               
                                                                      



                                                  
                                              
 
 

                                                                        

                                                      

                                       



                                                     
                   




                                         
                                                           
                 
                                                                                         

                                  
                                     



                                                                       
                                               


                                               
                                               



                                                

         
// 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
// <http://www.gnu.org/licenses/>.

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
        }
    }
}