rippled
Loading...
Searching...
No Matches
Consensus.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/consensus/Consensus.h>
21
22#include <xrpl/basics/Log.h>
23
24namespace ripple {
25
26bool
28 bool anyTransactions,
29 std::size_t prevProposers,
30 std::size_t proposersClosed,
31 std::size_t proposersValidated,
32 std::chrono::milliseconds prevRoundTime,
34 timeSincePrevClose, // Time since last ledger's close time
35 std::chrono::milliseconds openTime, // Time waiting to close this ledger
36 std::chrono::milliseconds idleInterval,
37 ConsensusParms const& parms,
40{
41 CLOG(clog) << "shouldCloseLedger params anyTransactions: "
42 << anyTransactions << ", prevProposers: " << prevProposers
43 << ", proposersClosed: " << proposersClosed
44 << ", proposersValidated: " << proposersValidated
45 << ", prevRoundTime: " << prevRoundTime.count() << "ms"
46 << ", timeSincePrevClose: " << timeSincePrevClose.count() << "ms"
47 << ", openTime: " << openTime.count() << "ms"
48 << ", idleInterval: " << idleInterval.count() << "ms"
49 << ", ledgerMIN_CLOSE: " << parms.ledgerMIN_CLOSE.count() << "ms"
50 << ". ";
51 using namespace std::chrono_literals;
52 if ((prevRoundTime < -1s) || (prevRoundTime > 10min) ||
53 (timeSincePrevClose > 10min))
54 {
55 // These are unexpected cases, we just close the ledger
57 ss << "shouldCloseLedger Trans=" << (anyTransactions ? "yes" : "no")
58 << " Prop: " << prevProposers << "/" << proposersClosed
59 << " Secs: " << timeSincePrevClose.count()
60 << " (last: " << prevRoundTime.count() << ")";
61
62 JLOG(j.warn()) << ss.str();
63 CLOG(clog) << "closing ledger: " << ss.str() << ". ";
64 return true;
65 }
66
67 if ((proposersClosed + proposersValidated) > (prevProposers / 2))
68 {
69 // If more than half of the network has closed, we close
70 JLOG(j.trace()) << "Others have closed";
71 CLOG(clog) << "closing ledger because enough others have already. ";
72 return true;
73 }
74
75 if (!anyTransactions)
76 {
77 // Only close at the end of the idle interval
78 CLOG(clog) << "no transactions, returning. ";
79 return timeSincePrevClose >= idleInterval; // normal idle
80 }
81
82 // Preserve minimum ledger open time
83 if (openTime < parms.ledgerMIN_CLOSE)
84 {
85 JLOG(j.debug()) << "Must wait minimum time before closing";
86 CLOG(clog) << "not closing because under ledgerMIN_CLOSE. ";
87 return false;
88 }
89
90 // Don't let this ledger close more than twice as fast as the previous
91 // ledger reached consensus so that slower validators can slow down
92 // the network
93 if (openTime < (prevRoundTime / 2))
94 {
95 JLOG(j.debug()) << "Ledger has not been open long enough";
96 CLOG(clog) << "not closing because not open long enough. ";
97 return false;
98 }
99
100 // Close the ledger
101 CLOG(clog) << "no reason to not close. ";
102 return true;
103}
104
105bool
107 std::size_t agreeing,
108 std::size_t total,
109 bool count_self,
110 std::size_t minConsensusPct,
111 bool reachedMax,
112 bool stalled,
114{
115 CLOG(clog) << "checkConsensusReached params: agreeing: " << agreeing
116 << ", total: " << total << ", count_self: " << count_self
117 << ", minConsensusPct: " << minConsensusPct
118 << ", reachedMax: " << reachedMax << ". ";
119
120 // If we are alone for too long, we have consensus.
121 // Delaying consensus like this avoids a circumstance where a peer
122 // gets ahead of proposers insofar as it has not received any proposals.
123 // This could happen if there's a slowdown in receiving proposals. Reaching
124 // consensus prematurely in this way means that the peer will likely desync.
125 // The check for reachedMax should allow plenty of time for proposals to
126 // arrive, and there should be no downside. If a peer is truly not
127 // receiving any proposals, then there should be no hurry. There's
128 // really nowhere to go.
129 if (total == 0)
130 {
131 if (reachedMax)
132 {
133 CLOG(clog)
134 << "Consensus reached because nobody shares our position and "
135 "maximum duration has passed.";
136 return true;
137 }
138 CLOG(clog) << "Consensus not reached and nobody shares our position. ";
139 return false;
140 }
141
142 // We only get stalled when there are disputed transactions and all of them
143 // unequivocally have 80% (minConsensusPct) agreement, either for or
144 // against. That is: either under 20% or over 80% consensus (repectively
145 // "nay" or "yay"). This prevents manipulation by a minority of byzantine
146 // peers of which transactions make the cut to get into the ledger.
147 if (stalled)
148 {
149 CLOG(clog) << "consensus stalled. ";
150 return true;
151 }
152
153 if (count_self)
154 {
155 ++agreeing;
156 ++total;
157 CLOG(clog) << "agreeing and total adjusted: " << agreeing << ','
158 << total << ". ";
159 }
160
161 std::size_t currentPercentage = (agreeing * 100) / total;
162
163 CLOG(clog) << "currentPercentage: " << currentPercentage;
164 bool const ret = currentPercentage >= minConsensusPct;
165 if (ret)
166 {
167 CLOG(clog) << ", consensus reached. ";
168 }
169 else
170 {
171 CLOG(clog) << ", consensus not reached. ";
172 }
173 return ret;
174}
175
178 std::size_t prevProposers,
179 std::size_t currentProposers,
180 std::size_t currentAgree,
181 std::size_t currentFinished,
182 std::chrono::milliseconds previousAgreeTime,
183 std::chrono::milliseconds currentAgreeTime,
184 bool stalled,
185 ConsensusParms const& parms,
186 bool proposing,
189{
190 CLOG(clog) << "checkConsensus: prop=" << currentProposers << "/"
191 << prevProposers << " agree=" << currentAgree
192 << " validated=" << currentFinished
193 << " time=" << currentAgreeTime.count() << "/"
194 << previousAgreeTime.count() << " proposing? " << proposing
195 << " minimum duration to reach consensus: "
196 << parms.ledgerMIN_CONSENSUS.count() << "ms"
197 << " max consensus time " << parms.ledgerMAX_CONSENSUS.count()
198 << "ms"
199 << " minimum consensus percentage: " << parms.minCONSENSUS_PCT
200 << ". ";
201
202 if (currentAgreeTime <= parms.ledgerMIN_CONSENSUS)
203 {
204 CLOG(clog) << "Not reached. ";
205 return ConsensusState::No;
206 }
207
208 if (currentProposers < (prevProposers * 3 / 4))
209 {
210 // Less than 3/4 of the last ledger's proposers are present; don't
211 // rush: we may need more time.
212 if (currentAgreeTime < (previousAgreeTime + parms.ledgerMIN_CONSENSUS))
213 {
214 JLOG(j.trace()) << "too fast, not enough proposers";
215 CLOG(clog) << "Too fast, not enough proposers. Not reached. ";
216 return ConsensusState::No;
217 }
218 }
219
220 // Have we, together with the nodes on our UNL list, reached the threshold
221 // to declare consensus?
223 currentAgree,
224 currentProposers,
225 proposing,
226 parms.minCONSENSUS_PCT,
227 currentAgreeTime > parms.ledgerMAX_CONSENSUS,
228 stalled,
229 clog))
230 {
231 JLOG((stalled ? j.warn() : j.debug()))
232 << "normal consensus" << (stalled ? ", but stalled" : "");
233 CLOG(clog) << "reached" << (stalled ? ", but stalled." : ".");
234 return ConsensusState::Yes;
235 }
236
237 // Have sufficient nodes on our UNL list moved on and reached the threshold
238 // to declare consensus?
240 currentFinished,
241 currentProposers,
242 false,
243 parms.minCONSENSUS_PCT,
244 currentAgreeTime > parms.ledgerMAX_CONSENSUS,
245 false,
246 clog))
247 {
248 JLOG(j.warn()) << "We see no consensus, but 80% of nodes have moved on";
249 CLOG(clog) << "We see no consensus, but 80% of nodes have moved on";
251 }
252
253 std::chrono::milliseconds const maxAgreeTime =
254 previousAgreeTime * parms.ledgerABANDON_CONSENSUS_FACTOR;
255 if (currentAgreeTime > std::clamp(
256 maxAgreeTime,
259 {
260 JLOG(j.warn()) << "consensus taken too long";
261 CLOG(clog) << "Consensus taken too long. ";
262 // Note the Expired result may be overridden by the caller.
264 }
265
266 // no consensus yet
267 JLOG(j.trace()) << "no consensus";
268 CLOG(clog) << "No consensus. ";
269 return ConsensusState::No;
270}
271
272} // namespace ripple
T clamp(T... args)
A generic endpoint for log messages.
Definition Journal.h:60
Stream debug() const
Definition Journal.h:328
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
@ proposing
We are normal participant in consensus and propose our position.
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.
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)
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:27
T str(T... args)
Consensus algorithm parameters.
std::chrono::milliseconds const ledgerMIN_CONSENSUS
The number of seconds we wait minimum to ensure participation.
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 ledgerMIN_CLOSE
Minimum number of seconds to wait to ensure others have computed the LCL.
std::chrono::milliseconds const ledgerMAX_CONSENSUS
The maximum amount of time to spend pausing for laggards.
std::size_t const ledgerABANDON_CONSENSUS_FACTOR
How long to wait before completely abandoning consensus.