diff options
Diffstat (limited to 'core/consensus.go')
-rw-r--r-- | core/consensus.go | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/core/consensus.go b/core/consensus.go new file mode 100644 index 0000000..6a97e9e --- /dev/null +++ b/core/consensus.go @@ -0,0 +1,130 @@ +// 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/core/types" +) + +// 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. + // TODO(mission): the value of 'K' should be in governace. + // TODO(mission): the ratio of 'phi' should be in governance. + to := newTotalOrdering( + 0, + uint64(2*(len(validatorSet)-1)/3+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(b *types.Block) (err error) { + 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. + // TODO(mission): return block hash instead of whole block here. + con.app.TotalOrderingDeliver(deliveredBlocks, 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(b *types.Block) (err error) { + con.lock.RLock() + defer con.lock.RUnlock() + + con.rbModule.prepareBlock(b) + b.Timestamps[b.ProposerID] = time.Now().UTC() + return +} |