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,
113{
114 CLOG(clog) << "checkConsensusReached params: agreeing: " << agreeing
115 << ", total: " << total << ", count_self: " << count_self
116 << ", minConsensusPct: " << minConsensusPct
117 << ", reachedMax: " << reachedMax << ". ";
118
119 // If we are alone for too long, we have consensus.
120 // Delaying consensus like this avoids a circumstance where a peer
121 // gets ahead of proposers insofar as it has not received any proposals.
122 // This could happen if there's a slowdown in receiving proposals. Reaching
123 // consensus prematurely in this way means that the peer will likely desync.
124 // The check for reachedMax should allow plenty of time for proposals to
125 // arrive, and there should be no downside. If a peer is truly not
126 // receiving any proposals, then there should be no hurry. There's
127 // really nowhere to go.
128 if (total == 0)
129 {
130 if (reachedMax)
131 {
132 CLOG(clog)
133 << "Consensus reached because nobody shares our position and "
134 "maximum duration has passed.";
135 return true;
136 }
137 CLOG(clog) << "Consensus not reached and nobody shares our position. ";
138 return false;
139 }
140
141 if (count_self)
142 {
143 ++agreeing;
144 ++total;
145 CLOG(clog) << "agreeing and total adjusted: " << agreeing << ','
146 << total << ". ";
147 }
148
149 std::size_t currentPercentage = (agreeing * 100) / total;
150 CLOG(clog) << "currentPercentage: " << currentPercentage;
151 bool const ret = currentPercentage >= minConsensusPct;
152 if (ret)
153 {
154 CLOG(clog) << ", consensus reached. ";
155 }
156 else
157 {
158 CLOG(clog) << ", consensus not reached. ";
159 }
160 return ret;
161}
162
165 std::size_t prevProposers,
166 std::size_t currentProposers,
167 std::size_t currentAgree,
168 std::size_t currentFinished,
169 std::chrono::milliseconds previousAgreeTime,
170 std::chrono::milliseconds currentAgreeTime,
171 ConsensusParms const& parms,
172 bool proposing,
175{
176 CLOG(clog) << "checkConsensus: prop=" << currentProposers << "/"
177 << prevProposers << " agree=" << currentAgree
178 << " validated=" << currentFinished
179 << " time=" << currentAgreeTime.count() << "/"
180 << previousAgreeTime.count() << " proposing? " << proposing
181 << " minimum duration to reach consensus: "
182 << parms.ledgerMIN_CONSENSUS.count() << "ms"
183 << " max consensus time " << parms.ledgerMAX_CONSENSUS.count()
184 << "s"
185 << " minimum consensus percentage: " << parms.minCONSENSUS_PCT
186 << ". ";
187
188 if (currentAgreeTime <= parms.ledgerMIN_CONSENSUS)
189 {
190 CLOG(clog) << "Not reached. ";
191 return ConsensusState::No;
192 }
193
194 if (currentProposers < (prevProposers * 3 / 4))
195 {
196 // Less than 3/4 of the last ledger's proposers are present; don't
197 // rush: we may need more time.
198 if (currentAgreeTime < (previousAgreeTime + parms.ledgerMIN_CONSENSUS))
199 {
200 JLOG(j.trace()) << "too fast, not enough proposers";
201 CLOG(clog) << "Too fast, not enough proposers. Not reached. ";
202 return ConsensusState::No;
203 }
204 }
205
206 // Have we, together with the nodes on our UNL list, reached the threshold
207 // to declare consensus?
209 currentAgree,
210 currentProposers,
211 proposing,
212 parms.minCONSENSUS_PCT,
213 currentAgreeTime > parms.ledgerMAX_CONSENSUS,
214 clog))
215 {
216 JLOG(j.debug()) << "normal consensus";
217 CLOG(clog) << "reached. ";
218 return ConsensusState::Yes;
219 }
220
221 // Have sufficient nodes on our UNL list moved on and reached the threshold
222 // to declare consensus?
224 currentFinished,
225 currentProposers,
226 false,
227 parms.minCONSENSUS_PCT,
228 currentAgreeTime > parms.ledgerMAX_CONSENSUS,
229 clog))
230 {
231 JLOG(j.warn()) << "We see no consensus, but 80% of nodes have moved on";
232 CLOG(clog) << "We see no consensus, but 80% of nodes have moved on";
234 }
235
236 // no consensus yet
237 JLOG(j.trace()) << "no consensus";
238 CLOG(clog) << "No consensus. ";
239 return ConsensusState::No;
240}
241
242} // namespace ripple
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:26
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, 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.
Definition: Consensus.cpp:164
@ proposing
We are normal participant in consensus and propose our position.
ConsensusState
Whether we have or don't have a consensus.
@ MovedOn
The network has consensus without us.
@ Yes
We have consensus along with the network.
@ No
We do not have consensus.
bool checkConsensusReached(std::size_t agreeing, std::size_t total, bool count_self, std::size_t minConsensusPct, bool reachedMax, std::unique_ptr< std::stringstream > const &clog)
Definition: Consensus.cpp:106
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::size_t minCONSENSUS_PCT
The percentage threshold above which we can declare consensus.
std::chrono::milliseconds ledgerMIN_CONSENSUS
The number of seconds we wait minimum to ensure participation.
std::chrono::milliseconds ledgerMAX_CONSENSUS
The maximum amount of time to spend pausing for laggards.
std::chrono::milliseconds ledgerMIN_CLOSE
Minimum number of seconds to wait to ensure others have computed the LCL.