I. Overview of the principles of PBFT
1. Algorithm formula:
The replicacount int variable is defined in the Pbftcore struct body
N (n corresponds to the replicacount integer variable in the code) is a collection of all replicas, each replica represented by an integer, such as {0, 1, 2, 3,... N-1}
N-1 = 3f-----> F = N-1/3
F is the maximum tolerable error node, which means that the permission is 1/3 wrong
2. Diagram PBFT Execution process
The request sent by the client is actually sent to the main replica, where it is assumed that the main replica has not been selected.
Here you can also see that figure 3 and Figure 4 are the same, in fact, this is a mutual confirmation of the process
Client
Client---->request--->replicas
1. Request carries operation, timestamp, with a timestamp for each request, so that later request will have a higher timestamp than the previous one
2.replicas receives the request and, if they verify the request, writes it to its own log. After the consensus algorithm guarantees that each replica completes the execution of the request, it returns the reply to the client directly:
3.REPLY carries the current view ordinal and timestamp, and the number of the replica node, returning the execution result
4. There is a weak certificate in the consensus algorithm, where the client also waits for weak certificate: There are f+1 replicas replies, and their replies have the same T and R, because at most f faulty replicas , so make sure the reply is legal. We call this weak certificate for reply certificate.
5. In active state each replica will share a secret key with each client.
Pre-prepare Stage
1. The master node receives a request from the client and assigns a number to the request,
2. The master node broadcasts a pre-prepare message to the backup node,
3. This pre-prepare information contains the number of the request, the view in which it is located, and a digest of its own.
4. Until the information is sent to each backup node, the next step is to see the backup node that received the information and disagree
5. The primary node assigns this number n to the request, that is, whether to accept this pre-prepare information,
6. If a backup node is admitted to this pre-prepare, it will enter the PREPARE phase below.
Prepare stage
1. After the backup node enters the prepared phase, broadcast a prepare message to the master node and other backup nodes until the prepare information arrives at the three nodes. At the same time, the backup node receives prepare information from other backup nodes, respectively.
2. The backup node will synthesize these prepare information to make its own final decision on number N. When the backup node starts to compare the prepare information from the other two backup nodes and its own prepare information, if the backup node discovers that the other two nodes agree to the number assigned by the primary node, and then looks at itself, he also agrees to the allocation of the master node, a quorum Certificate with the Pre-prepare and 2 F matching PREPARE messages for sequence number n, view V, and request M, if a replic A has reached the English speaking condition, for example is above the italic character description one kind of situation, then we say that the request in this replica state is prepared, the replica has a certificate called prepared certificate. So we can say that this sort of work has been done, the whole network node has reached a consistent request sequence, each of the replica began to follow this sequence. This is a loophole, imagine, at the T1 moment only replica 1 request m (number N) to the prepared state, the other two backup nodes replica 2, replica 3 have not been in time to collect the prepare information from other nodes to judge, Then there's a view change coming into a new view, replica 1 also thinks that the number n assigned to M has been given a quorum consent, can continue to be in the sequence, or can be executed, but for replica 2, it came to the new view, It loses the judgment of the request m, even in the last view it has collected all other nodes issued prepare information, so for replica 2, to the request m assigned number n will not count, the same replica 3 is also. So replica 11 people think that count is not enough to allow the whole network to agree, so in the new view, the request m number n will be void, need to re-launch the proposal. So there is the following commit phase.
It is important to note that the backup node logs its own pre-prepare and sent prepare information to its own log.
The backup node sends out a prepare message indicating that the node agrees that the master node assigns the number n to the request M in view V, and that it does not agree.
If a replica sends Pre-prepare and prepare information to request M, then we say that the request M is in pre-prepared state on this replica node.
Commit phase
Immediately following the prepare phase, when a replica node discovers that there is a quorum consent number assignment, it broadcasts a commit message to all other nodes telling them that it has a prepared certificate. At the same time it will receive commit information from other nodes in succession, if it receives a 2f+1 commit (including one of its own, these from different node commits carry the same number N and view v), we say that the node has a call committed Certificate certificate, the request reached the committed state on this node. At this point only through this one node, we can conclude that the request has reached the prepared state in a quorum, send a quorum node agreed to the allocation of number N. When the request m reaches the commited state, the request is executed by that node.
The process of executing the core code of this view is as follows
Second, consensus algorithm code parsing
1. Code directory structure
Getengine returns initialized peer. Engine
//============================================================================
It initializes a consenter and a helper, and assigns a handle to each other.
The purpose of this is to allow external calls to be made internally, internally to invoke external
//============================================================================
Func Getengine (coord peer. Messagehandlercoordinator) (peer. Engine, error) {
var err error
Engineonce.do (func () {
engine = new (Engineimpl)
Engine.helper = Newhelper (coord)
Engine.consenter = controller. Newconsenter (Engine.helper)
Engine.helper.setConsenter (Engine.consenter)
Engine.peerendpoint, err = coord. Getpeerendpoint ()
Engine.consensusfan = util. Newmessagefan ()
Go func () {
Logger. Debug ("Starting up message thread for Consenter")
The channel never closes, so this should never break
For msg: = Range Engine.consensusFan.GetOutChannel () {
ENGINE.CONSENTER.RECVMSG (Msg. MSG, MSG. Sender)
}
}()
})
return engine, Err
}
//==============================================================================
Newconsenter constructs a Consenter object if not already present
//==============================================================================
//==============================================================================
Call the controller to get a plugin, when the selection is the PBFT algorithm, it will call Pbft.go in the
Getplugin (c consensus. Stack) method that reads all external parameters into the algorithm inside the Pbft.go
//==============================================================================
Func newconsenter (Stack consensus. Stack) consensus. Consenter {
Plugin: = Strings. ToLower (Viper. GetString ("Peer.validator.consensus.plugin"))
If plugin = = "PBFT" {
Logger.infof ("Creating consensus plugin%s", plugin)
Return PBFT. Getplugin (Stack)
}
Logger. Info ("Creating default consensus plugin (noops)")
Return Noops. Getnoops (Stack)
}
The controller directory is a function of the consensus plug-in selection module
The---->hyperledger provides two algorithms pbft and Noops
----> Default single node Usage noops is equivalent to no consensus algorithm
Func newconsenter (Stack consensus. Stack) consensus. Consenter {
Plugin: = Strings. ToLower (Viper. GetString ("Peer.validator.consensus.plugin"))
If plugin = = "PBFT" {
Logger.infof ("Creating consensus plugin%s", plugin)
Return PBFT. Getplugin (Stack)
}
Logger. Info ("Creating default consensus plugin (noops)")
Return Noops. Getnoops (Stack)
}
function, it can be seen that Hyperledger fabric supports only pbft and Noops
Executor and helper are two interdependent modules
----> mainly provides a piece of code for consensus algorithms and external cohesion. Mainly responsible for the transfer of event handling
Helper
---> This includes a call to the external interface, such as executing processing transaction,stateupdate, persisting some objects, etc.
Noops
--->noops equivalent to no consensus algorithm
Pbft
Default consensus algorithm for---> Hyperledger
Util
---> Interaction requires a toolkit, one of the most important implementations is its messaging mechanism.
Func Getplugin (c consensus. Stack) consensus. Consenter {
if plugininstance = = Nil {
Plugininstance = New (c)
}
Return plugininstance
}
Func New (Stack consensus. Stack) consensus. Consenter {
Handle, _, _: = Stack. Getnetworkhandles ()
ID, _: = Getvalidatorid (handle)
Switch strings. ToLower (config. GetString ("General.mode")) {
Case "Batch":
return Newobcbatch (ID, config, stack)
Default
Panic (FMT. Errorf ("Invalid pbft mode:%s", config. GetString ("General.mode")))
}
}
==============================================================================//in Newobcbatch, Initializes an instance of the Pbftcore, which is the core module of the algorithm. And a Batchtimer is started (this batchtimer is a timer,//When Batchtimer timeout triggers a sendbatch operation, which only the primary node will do). Of course, an event-handling mechanism is created, which is a bridge for each module's communication. //==============================================================================
Func Newobcbatch (id uint64, config *viper. Viper, stack consensus. Stack) *obcbatch {
var err error
OP: = &obcbatch{
Obcgeneric:obcgeneric{stack:stack},
}
Op.persistForward.persistor = Stack
Logger. DEBUGF ("Replica%d obtaining startup information", ID)
Op.manager = events. Newmanagerimpl ()//TODO, this was hacky, eventually rip it out
Op.manager.SetReceiver (OP)
ETF: = events. Newtimerfactoryimpl (Op.manager)
OP.PBFT = Newpbftcore (id, config, op, etf)
Op.manager.Start ()
Blockchaininfoblob: = stack. Getblockchaininfoblob ()
Op.externalEventReceiver.manager = Op.manager
Op.broadcaster = Newbroadcaster (ID, OP.PBFT.N, OP.PBFT.F, op.pbft.broadcastTimeout, Stack)
Op.manager.Queue () <-workevent (func () {
Op.pbft.stateTransfer (&stateupdatetarget{
checkpointmessage:checkpointmessage{
SeqNo:op.pbft.lastExec,
ID: Blockchaininfoblob,
},
})
})
op.batchsize = config. GetInt ("General.batchsize")
Op.batchstore = Nil
Op.batchtimeout, err = time. Parseduration (config. GetString ("General.timeout.batch"))