forked from stellar/stellar-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBallotProtocol.h
272 lines (209 loc) · 9.63 KB
/
BallotProtocol.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#pragma once
// Copyright 2014 Stellar Development Foundation and contributors. Licensed
// under the Apache License, Version 2.0. See the COPYING file at the root
// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
#include "lib/json/json-forwards.h"
#include "scp/SCP.h"
#include <functional>
#include <memory>
#include <set>
#include <string>
#include <utility>
namespace stellar
{
class Node;
class Slot;
// used to filter statements
typedef std::function<bool(SCPStatement const& st)> StatementPredicate;
/**
* The Slot object is in charge of maintaining the state of the SCP protocol
* for a given slot index.
*/
class BallotProtocol
{
Slot& mSlot;
bool mHeardFromQuorum;
// state tracking members
enum SCPPhase
{
SCP_PHASE_PREPARE,
SCP_PHASE_CONFIRM,
SCP_PHASE_EXTERNALIZE,
SCP_PHASE_NUM
};
// human readable names matching SCPPhase
static const char* phaseNames[];
std::unique_ptr<SCPBallot> mCurrentBallot; // b
std::unique_ptr<SCPBallot> mPrepared; // p
std::unique_ptr<SCPBallot> mPreparedPrime; // p'
std::unique_ptr<SCPBallot> mHighBallot; // h
std::unique_ptr<SCPBallot> mCommit; // c
std::map<NodeID, SCPEnvelope> mLatestEnvelopes; // M
SCPPhase mPhase; // Phi
std::unique_ptr<Value> mValueOverride; // z
int mCurrentMessageLevel; // number of messages triggered in one run
std::shared_ptr<SCPEnvelope>
mLastEnvelope; // last envelope generated by this node
std::shared_ptr<SCPEnvelope>
mLastEnvelopeEmit; // last envelope emitted by this node
public:
BallotProtocol(Slot& slot);
// Process a newly received envelope for this slot and update the state of
// the slot accordingly.
// self: set to true when node feeds its own statements in order to
// trigger more potential state changes
SCP::EnvelopeState processEnvelope(SCPEnvelope const& envelope, bool self);
void ballotProtocolTimerExpired();
// abandon's current ballot, move to a new ballot
// at counter `n` (or, if n == 0, increment current counter)
bool abandonBallot(uint32 n);
// bumps the ballot based on the local state and the value passed in:
// in prepare phase, attempts to take value
// otherwise, no-ops
// force: when true, always bumps the value, otherwise only bumps
// the state if no value was prepared
bool bumpState(Value const& value, bool force);
// flavor that takes the actual desired counter value
bool bumpState(Value const& value, uint32 n);
// ** status methods
// returns information about the local state in JSON format
// including historical statements if available
Json::Value getJsonInfo();
// returns information about the quorum for a given node
Json::Value getJsonQuorumInfo(NodeID const& id, bool summary,
bool fullKeys = false);
// returns the hash of the QuorumSet that should be downloaded
// with the statement.
// note: the companion hash for an EXTERNALIZE statement does
// not match the hash of the QSet, but the hash of commitQuorumSetHash
static Hash getCompanionQuorumSetHashFromStatement(SCPStatement const& st);
// helper function to retrieve b for PREPARE, P for CONFIRM or
// c for EXTERNALIZE messages
static SCPBallot getWorkingBallot(SCPStatement const& st);
SCPEnvelope*
getLastMessageSend() const
{
return mLastEnvelopeEmit.get();
}
void setStateFromEnvelope(SCPEnvelope const& e);
std::vector<SCPEnvelope> getCurrentState() const;
// returns the latest message from a node
// or nullptr if not found
SCPEnvelope const* getLatestMessage(NodeID const& id) const;
std::vector<SCPEnvelope> getExternalizingState() const;
private:
// attempts to make progress using the latest statement as a hint
// calls into the various attempt* methods, emits message
// to make progress
void advanceSlot(SCPStatement const& hint);
// returns true if all values in statement are valid
SCPDriver::ValidationLevel validateValues(SCPStatement const& st);
// send latest envelope if needed
void sendLatestEnvelope();
// `attempt*` methods are called by `advanceSlot` internally call the
// the `set*` methods.
// * check if the specified state for the current slot has been
// reached or not.
// * idempotent
// input: latest statement received (used as a hint to reduce the
// space to explore)
// output: returns true if the state was updated
// `set*` methods progress the slot to the specified state
// input: state specific
// output: returns true if the state was updated.
// step 1 and 5 from the SCP paper
bool attemptPreparedAccept(SCPStatement const& hint);
// prepared: ballot that should be prepared
bool setPreparedAccept(SCPBallot const& prepared);
// step 2+3+8 from the SCP paper
// ballot is the candidate to record as 'confirmed prepared'
bool attemptPreparedConfirmed(SCPStatement const& hint);
// newC, newH : low/high bounds prepared confirmed
bool setPreparedConfirmed(SCPBallot const& newC, SCPBallot const& newH);
// step (4 and 6)+8 from the SCP paper
bool attemptAcceptCommit(SCPStatement const& hint);
// new values for c and h
bool setAcceptCommit(SCPBallot const& c, SCPBallot const& h);
// step 7+8 from the SCP paper
bool attemptConfirmCommit(SCPStatement const& hint);
bool setConfirmCommit(SCPBallot const& acceptCommitLow,
SCPBallot const& acceptCommitHigh);
// step 9 from the SCP paper
bool attemptBump();
// computes a list of candidate values that may have been prepared
std::set<SCPBallot> getPrepareCandidates(SCPStatement const& hint);
// helper to perform step (8) from the paper
bool updateCurrentIfNeeded(SCPBallot const& h);
// An interval is [low,high] represented as a pair
using Interval = std::pair<uint32, uint32>;
// helper function to find a contiguous range 'candidate' that satisfies the
// predicate.
// updates 'candidate' (or leave it unchanged)
static void findExtendedInterval(Interval& candidate,
std::set<uint32> const& boundaries,
std::function<bool(Interval const&)> pred);
// constructs the set of counters representing the
// commit ballots compatible with the ballot
std::set<uint32> getCommitBoundariesFromStatements(SCPBallot const& ballot);
// ** helper predicates that evaluate if a statement satisfies
// a certain property
// is ballot prepared by st
static bool hasPreparedBallot(SCPBallot const& ballot,
SCPStatement const& st);
// returns true if the statement commits the ballot in the range 'check'
static bool commitPredicate(SCPBallot const& ballot, Interval const& check,
SCPStatement const& st);
// attempts to update p to ballot (updating p' if needed)
bool setPrepared(SCPBallot const& ballot);
// ** Helper methods to compare two ballots
// ballot comparison (ordering)
static int compareBallots(std::unique_ptr<SCPBallot> const& b1,
std::unique_ptr<SCPBallot> const& b2);
static int compareBallots(SCPBallot const& b1, SCPBallot const& b2);
// b1 ~ b2
static bool areBallotsCompatible(SCPBallot const& b1, SCPBallot const& b2);
// b1 <= b2 && b1 !~ b2
static bool areBallotsLessAndIncompatible(SCPBallot const& b1,
SCPBallot const& b2);
// b1 <= b2 && b1 ~ b2
static bool areBallotsLessAndCompatible(SCPBallot const& b1,
SCPBallot const& b2);
// ** statement helper functions
// returns true if the statement is newer than the one we know about
// for a given node.
bool isNewerStatement(NodeID const& nodeID, SCPStatement const& st);
// returns true if st is newer than oldst
static bool isNewerStatement(SCPStatement const& oldst,
SCPStatement const& st);
// basic sanity check on statement
bool isStatementSane(SCPStatement const& st, bool self);
// records the statement in the state machine
void recordEnvelope(SCPEnvelope const& env);
// ** State related methods
// helper function that updates the current ballot
// this is the lowest level method to update the current ballot and as
// such doesn't do any validation
// check: verifies that ballot is greater than old one
void bumpToBallot(SCPBallot const& ballot, bool check);
// switch the local node to the given ballot's value
// with the assumption that the ballot is more recent than the one
// we have.
bool updateCurrentValue(SCPBallot const& ballot);
// emits a statement reflecting the nodes' current state
// and attempts to make progress
void emitCurrentStateStatement();
// verifies that the internal state is consistent
void checkInvariants();
// create a statement of the given type using the local state
SCPStatement createStatement(SCPStatementType const& type);
// returns a string representing the slot's state
// used for log lines
std::string getLocalState() const;
std::shared_ptr<LocalNode> getLocalNode();
bool federatedAccept(StatementPredicate voted, StatementPredicate accepted);
bool federatedRatify(StatementPredicate voted);
void startBallotProtocolTimer();
void stopBallotProtocolTimer();
void checkHeardFromQuorum();
};
}