rippled
Loading...
Searching...
No Matches
Consensus.cpp
1#include <xrpld/consensus/Consensus.h>
2
3#include <xrpl/basics/Log.h>
4
5namespace xrpl {
6
7bool
9 bool anyTransactions,
10 std::size_t prevProposers,
11 std::size_t proposersClosed,
12 std::size_t proposersValidated,
13 std::chrono::milliseconds prevRoundTime,
14 std::chrono::milliseconds timeSincePrevClose, // Time since last ledger's close time
15 std::chrono::milliseconds openTime, // Time waiting to close this ledger
16 std::chrono::milliseconds idleInterval,
17 ConsensusParms const& parms,
20{
21 CLOG(clog) << "shouldCloseLedger params anyTransactions: " << anyTransactions
22 << ", prevProposers: " << prevProposers << ", proposersClosed: " << proposersClosed
23 << ", proposersValidated: " << proposersValidated
24 << ", prevRoundTime: " << prevRoundTime.count() << "ms"
25 << ", timeSincePrevClose: " << timeSincePrevClose.count() << "ms"
26 << ", openTime: " << openTime.count() << "ms"
27 << ", idleInterval: " << idleInterval.count() << "ms"
28 << ", ledgerMIN_CLOSE: " << parms.ledgerMIN_CLOSE.count() << "ms"
29 << ". ";
30 using namespace std::chrono_literals;
31 if ((prevRoundTime < -1s) || (prevRoundTime > 10min) || (timeSincePrevClose > 10min))
32 {
33 // These are unexpected cases, we just close the ledger
35 ss << "shouldCloseLedger Trans=" << (anyTransactions ? "yes" : "no")
36 << " Prop: " << prevProposers << "/" << proposersClosed
37 << " Secs: " << timeSincePrevClose.count() << " (last: " << prevRoundTime.count() << ")";
38
39 JLOG(j.warn()) << ss.str();
40 CLOG(clog) << "closing ledger: " << ss.str() << ". ";
41 return true;
42 }
43
44 if ((proposersClosed + proposersValidated) > (prevProposers / 2))
45 {
46 // If more than half of the network has closed, we close
47 JLOG(j.trace()) << "Others have closed";
48 CLOG(clog) << "closing ledger because enough others have already. ";
49 return true;
50 }
51
52 if (!anyTransactions)
53 {
54 // Only close at the end of the idle interval
55 CLOG(clog) << "no transactions, returning. ";
56 return timeSincePrevClose >= idleInterval; // normal idle
57 }
58
59 // Preserve minimum ledger open time
60 if (openTime < parms.ledgerMIN_CLOSE)
61 {
62 JLOG(j.debug()) << "Must wait minimum time before closing";
63 CLOG(clog) << "not closing because under ledgerMIN_CLOSE. ";
64 return false;
65 }
66
67 // Don't let this ledger close more than twice as fast as the previous
68 // ledger reached consensus so that slower validators can slow down
69 // the network
70 if (openTime < (prevRoundTime / 2))
71 {
72 JLOG(j.debug()) << "Ledger has not been open long enough";
73 CLOG(clog) << "not closing because not open long enough. ";
74 return false;
75 }
76
77 // Close the ledger
78 CLOG(clog) << "no reason to not close. ";
79 return true;
80}
81
82bool
84 std::size_t agreeing,
85 std::size_t total,
86 bool count_self,
87 std::size_t minConsensusPct,
88 bool reachedMax,
89 bool stalled,
91{
92 CLOG(clog) << "checkConsensusReached params: agreeing: " << agreeing << ", total: " << total
93 << ", count_self: " << count_self << ", minConsensusPct: " << minConsensusPct
94 << ", reachedMax: " << reachedMax << ". ";
95
96 // If we are alone for too long, we have consensus.
97 // Delaying consensus like this avoids a circumstance where a peer
98 // gets ahead of proposers insofar as it has not received any proposals.
99 // This could happen if there's a slowdown in receiving proposals. Reaching
100 // consensus prematurely in this way means that the peer will likely desync.
101 // The check for reachedMax should allow plenty of time for proposals to
102 // arrive, and there should be no downside. If a peer is truly not
103 // receiving any proposals, then there should be no hurry. There's
104 // really nowhere to go.
105 if (total == 0)
106 {
107 if (reachedMax)
108 {
109 CLOG(clog) << "Consensus reached because nobody shares our position and "
110 "maximum duration has passed.";
111 return true;
112 }
113 CLOG(clog) << "Consensus not reached and nobody shares our position. ";
114 return false;
115 }
116
117 // We only get stalled when there are disputed transactions and all of them
118 // unequivocally have 80% (minConsensusPct) agreement, either for or
119 // against. That is: either under 20% or over 80% consensus (respectively
120 // "nay" or "yay"). This prevents manipulation by a minority of byzantine
121 // peers of which transactions make the cut to get into the ledger.
122 if (stalled)
123 {
124 CLOG(clog) << "consensus stalled. ";
125 return true;
126 }
127
128 if (count_self)
129 {
130 ++agreeing;
131 ++total;
132 CLOG(clog) << "agreeing and total adjusted: " << agreeing << ',' << total << ". ";
133 }
134
135 std::size_t const currentPercentage = (agreeing * 100) / total;
136
137 CLOG(clog) << "currentPercentage: " << currentPercentage;
138 bool const ret = currentPercentage >= minConsensusPct;
139 if (ret)
140 {
141 CLOG(clog) << ", consensus reached. ";
142 }
143 else
144 {
145 CLOG(clog) << ", consensus not reached. ";
146 }
147 return ret;
148}
149
152 std::size_t prevProposers,
153 std::size_t currentProposers,
154 std::size_t currentAgree,
155 std::size_t currentFinished,
156 std::chrono::milliseconds previousAgreeTime,
157 std::chrono::milliseconds currentAgreeTime,
158 bool stalled,
159 ConsensusParms const& parms,
160 bool proposing,
163{
164 CLOG(clog) << "checkConsensus: prop=" << currentProposers << "/" << prevProposers
165 << " agree=" << currentAgree << " validated=" << currentFinished
166 << " time=" << currentAgreeTime.count() << "/" << previousAgreeTime.count()
167 << " proposing? " << proposing
168 << " minimum duration to reach consensus: " << parms.ledgerMIN_CONSENSUS.count()
169 << "ms"
170 << " max consensus time " << parms.ledgerMAX_CONSENSUS.count() << "ms"
171 << " minimum consensus percentage: " << parms.minCONSENSUS_PCT << ". ";
172
173 if (currentAgreeTime <= parms.ledgerMIN_CONSENSUS)
174 {
175 CLOG(clog) << "Not reached. ";
176 return ConsensusState::No;
177 }
178
179 if (currentProposers < (prevProposers * 3 / 4))
180 {
181 // Less than 3/4 of the last ledger's proposers are present; don't
182 // rush: we may need more time.
183 if (currentAgreeTime < (previousAgreeTime + parms.ledgerMIN_CONSENSUS))
184 {
185 JLOG(j.trace()) << "too fast, not enough proposers";
186 CLOG(clog) << "Too fast, not enough proposers. Not reached. ";
187 return ConsensusState::No;
188 }
189 }
190
191 // Have we, together with the nodes on our UNL list, reached the threshold
192 // to declare consensus?
194 currentAgree,
195 currentProposers,
196 proposing,
197 parms.minCONSENSUS_PCT,
198 currentAgreeTime > parms.ledgerMAX_CONSENSUS,
199 stalled,
200 clog))
201 {
202 JLOG((stalled ? j.warn() : j.debug()))
203 << "normal consensus" << (stalled ? ", but stalled" : "");
204 CLOG(clog) << "reached" << (stalled ? ", but stalled." : ".");
205 return ConsensusState::Yes;
206 }
207
208 // Have sufficient nodes on our UNL list moved on and reached the threshold
209 // to declare consensus?
211 currentFinished,
212 currentProposers,
213 false,
214 parms.minCONSENSUS_PCT,
215 currentAgreeTime > parms.ledgerMAX_CONSENSUS,
216 false,
217 clog))
218 {
219 JLOG(j.warn()) << "We see no consensus, but 80% of nodes have moved on";
220 CLOG(clog) << "We see no consensus, but 80% of nodes have moved on";
222 }
223
224 std::chrono::milliseconds const maxAgreeTime =
225 previousAgreeTime * parms.ledgerABANDON_CONSENSUS_FACTOR;
226 if (currentAgreeTime >
227 std::clamp(maxAgreeTime, parms.ledgerMAX_CONSENSUS, parms.ledgerABANDON_CONSENSUS))
228 {
229 JLOG(j.warn()) << "consensus taken too long";
230 CLOG(clog) << "Consensus taken too long. ";
231 // Note the Expired result may be overridden by the caller.
233 }
234
235 // no consensus yet
236 JLOG(j.trace()) << "no consensus";
237 CLOG(clog) << "No consensus. ";
238 return ConsensusState::No;
239}
240
241} // namespace xrpl
T clamp(T... args)
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Stream warn() const
Definition Journal.h:313
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ proposing
We are normal participant in consensus and propose our position.
bool checkConsensusReached(std::size_t agreeing, std::size_t total, bool count_self, std::size_t minConsensusPct, bool reachedMax, bool stalled, std::unique_ptr< std::stringstream > const &clog)
Definition Consensus.cpp:83
ConsensusState checkConsensus(std::size_t prevProposers, std::size_t currentProposers, std::size_t currentAgree, std::size_t currentFinished, std::chrono::milliseconds previousAgreeTime, std::chrono::milliseconds currentAgreeTime, bool stalled, ConsensusParms const &parms, bool proposing, beast::Journal j, std::unique_ptr< std::stringstream > const &clog)
Determine whether the network reached consensus and whether we joined.
ConsensusState
Whether we have or don't have a consensus.
@ Expired
Consensus time limit has hard-expired.
@ MovedOn
The network has consensus without us.
@ Yes
We have consensus along with the network.
@ No
We do not have consensus.
bool shouldCloseLedger(bool anyTransactions, std::size_t prevProposers, std::size_t proposersClosed, std::size_t proposersValidated, std::chrono::milliseconds prevRoundTime, std::chrono::milliseconds timeSincePrevClose, std::chrono::milliseconds openTime, std::chrono::milliseconds idleInterval, ConsensusParms const &parms, beast::Journal j, std::unique_ptr< std::stringstream > const &clog)
Determines whether the current ledger should close at this time.
Definition Consensus.cpp:8
T str(T... args)
Consensus algorithm parameters.
std::chrono::milliseconds const ledgerABANDON_CONSENSUS
Maximum amount of time to give a consensus round.
std::size_t const minCONSENSUS_PCT
The percentage threshold above which we can declare consensus.
std::chrono::milliseconds const ledgerMAX_CONSENSUS
The maximum amount of time to spend pausing for laggards.
std::chrono::milliseconds const ledgerMIN_CONSENSUS
The number of seconds we wait minimum to ensure participation.
std::chrono::milliseconds const ledgerMIN_CLOSE
Minimum number of seconds to wait to ensure others have computed the LCL.
std::size_t const ledgerABANDON_CONSENSUS_FACTOR
How long to wait before completely abandoning consensus.