aboutsummaryrefslogblamecommitdiffstats
path: root/core/consensus.go
blob: bc6a2d79448c8da7ffc57f20bf4c10154623025e (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 core

import (
    "sync"
    "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/types"
)

// ErrMissingBlockInfo would be reported if some information is missing when
// calling PrepareBlock. It implements error interface.
type ErrMissingBlockInfo struct {
    MissingField string
}

func (e *ErrMissingBlockInfo) Error() string {
    return "missing " + e.MissingField + " in block"
}

// Consensus implements DEXON Consensus algorithm.
type Consensus struct {
    app      Application
    gov      Governance
    rbModule *reliableBroadcast
    toModule *totalOrdering
    ctModule *consensusTimestamp
    db       blockdb.BlockDatabase
    lock     sync.RWMutex
}

// NewConsensus construct an Consensus instance.
func NewConsensus(
    app Application,
    gov Governance,
    db blockdb.BlockDatabase) *Consensus {
    validatorSet := gov.GetValidatorSet()

    // Setup acking by information returned from Governace.
    rb := newReliableBroadcast()
    for vID := range validatorSet {
        rb.addValidator(vID)
    }

    // Setup sequencer by information returned from Governace.
    to := newTotalOrdering(
        uint64(gov.GetTotalOrderingK()),
        uint64(float32(len(validatorSet)-1)*gov.GetPhiRatio()+1),
        uint64(len(validatorSet)))

    return &Consensus{
        rbModule: rb,
        toModule: to,
        ctModule: newConsensusTimestamp(),
        app:      app,
        gov:      gov,
        db:       db,
    }
}

// ProcessBlock is the entry point to submit one block to a Consensus instance.
func (con *Consensus) ProcessBlock(blockConv types.BlockConverter) (err error) {
    b := blockConv.Block()
    var (
        deliveredBlocks []*types.Block
        earlyDelivered  bool
    )
    // To avoid application layer modify the content of block during
    // processing, we should always operate based on the cloned one.
    b = b.Clone()

    con.lock.Lock()
    defer con.lock.Unlock()
    // Perform reliable broadcast checking.
    if err = con.rbModule.processBlock(b); err != nil {
        return err
    }
    for _, b := range con.rbModule.extractBlocks() {
        // Notify application layer that some block is strongly acked.
        con.app.StronglyAcked(b.Hash)
        // Perform total ordering.
        deliveredBlocks, earlyDelivered, err = con.toModule.processBlock(b)
        if err != nil {
            return
        }
        if len(deliveredBlocks) == 0 {
            continue
        }
        for _, b := range deliveredBlocks {
            if err = con.db.Put(*b); err != nil {
                return
            }
        }
        // TODO(mission): handle membership events here.
        hashes := make(common.Hashes, len(deliveredBlocks))
        for idx := range deliveredBlocks {
            hashes[idx] = deliveredBlocks[idx].Hash
        }
        con.app.TotalOrderingDeliver(hashes, earlyDelivered)
        // Perform timestamp generation.
        deliveredBlocks, _, err = con.ctModule.processBlocks(
            deliveredBlocks)
        if err != nil {
            return
        }
        for _, b := range deliveredBlocks {
            if err = con.db.Update(*b); err != nil {
                return
            }
            con.app.DeliverBlock(b.Hash, b.ConsensusInfo.Timestamp)
        }
    }
    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"}
        return
    }
    con.lock.RLock()
    defer con.lock.RUnlock()

    con.rbModule.prepareBlock(b)
    b.Timestamps[b.ProposerID] = proposeTime
    blockConv.SetBlock(b)
    return
}