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#include <xrpl/basics/Log.h>
22
23namespace ripple {
24
25bool
27 bool anyTransactions,
28 std::size_t prevProposers,
29 std::size_t proposersClosed,
30 std::size_t proposersValidated,
31 std::chrono::milliseconds prevRoundTime,
33 timeSincePrevClose, // Time since last ledger's close time
34 std::chrono::milliseconds openTime, // Time waiting to close this ledger
35 std::chrono::milliseconds idleInterval,
36 ConsensusParms const& parms,
39{
40 CLOG(clog) << "shouldCloseLedger params anyTransactions: "
41 << anyTransactions << ", prevProposers: " << prevProposers
42 << ", proposersClosed: " << proposersClosed
43 << ", proposersValidated: " << proposersValidated
44 << ", prevRoundTime: " << prevRoundTime.count() << "ms"
45 << ", timeSincePrevClose: " << timeSincePrevClose.count() << "ms"
46 << ", openTime: " << openTime.count() << "ms"
47 << ", idleInterval: " << idleInterval.count() << "ms"
48 << ", ledgerMIN_CLOSE: " << parms.ledgerMIN_CLOSE.count() << "ms"
49 << ". ";
50 using namespace std::chrono_literals;
51 if ((prevRoundTime < -1s) || (prevRoundTime > 10min) ||
52 (timeSincePrevClose > 10min))
53 {
54 // These are unexpected cases, we just close the ledger
56 ss << "shouldCloseLedger Trans=" << (anyTransactions ? "yes" : "no")
57 << " Prop: " << prevProposers << "/" << proposersClosed
58 << " Secs: " << timeSincePrevClose.count()
59 << " (last: " << prevRoundTime.count() << ")";
60
61 JLOG(j.warn()) << ss.str();
62 CLOG(clog) << "closing ledger: " << ss.str() << ". ";
63 return true;
64 }
65
66 if ((proposersClosed + proposersValidated) > (prevProposers / 2))
67 {
68 // If more than half of the network has closed, we close
69 JLOG(j.trace()) << "Others have closed";
70 CLOG(clog) << "closing ledger because enough others have already. ";
71 return true;
72 }
73
74 if (!anyTransactions)
75 {
76 // Only close at the end of the idle interval
77 CLOG(clog) << "no transactions, returning. ";
78 return timeSincePrevClose >= idleInterval; // normal idle
79 }
80
81 // Preserve minimum ledger open time
82 if (openTime < parms.ledgerMIN_CLOSE)
83 {
84 JLOG(j.debug()) << "Must wait minimum time before closing";
85 CLOG(clog) << "not closing because under ledgerMIN_CLOSE. ";
86 return false;
87 }
88
89 // Don't let this ledger close more than twice as fast as the previous
90 // ledger reached consensus so that slower validators can slow down
91 // the network
92 if (openTime < (prevRoundTime / 2))
93 {
94 JLOG(j.debug()) << "Ledger has not been open long enough";
95 CLOG(clog) << "not closing because not open long enough. ";
96 return false;
97 }
98
99 // Close the ledger
100 CLOG(clog) << "no reason to not close. ";
101 return true;
102}
103
104bool
106 std::size_t agreeing,
107 std::size_t total,
108 bool count_self,
109 std::size_t minConsensusPct,
110 bool reachedMax,
112{
113 CLOG(clog) << "checkConsensusReached params: agreeing: " << agreeing
114 << ", total: " << total << ", count_self: " << count_self
115 << ", minConsensusPct: " << minConsensusPct
116 << ", reachedMax: " << reachedMax << ". ";
117
118 // If we are alone for too long, we have consensus.
119 // Delaying consensus like this avoids a circumstance where a peer
120 // gets ahead of proposers insofar as it has not received any proposals.
121 // This could happen if there's a slowdown in receiving proposals. Reaching
122 // consensus prematurely in this way means that the peer will likely desync.
123 // The check for reachedMax should allow plenty of time for proposals to
124 // arrive, and there should be no downside. If a peer is truly not
125 // receiving any proposals, then there should be no hurry. There's
126 // really nowhere to go.
127 if (total == 0)
128 {
129 if (reachedMax)
130 {
131 CLOG(clog)
132 << "Consensus reached because nobody shares our position and "
133 "maximum duration has passed.";
134 return true;
135 }
136 CLOG(clog) << "Consensus not reached and nobody shares our position. ";
137 return false;
138 }
139
140 if (count_self)
141 {
142 ++agreeing;
143 ++total;
144 CLOG(clog) << "agreeing and total adjusted: " << agreeing << ','
145 << total << ". ";
146 }
147
148 std::size_t currentPercentage = (agreeing * 100) / total;
149 CLOG(clog) << "currentPercentage: " << currentPercentage;
150 bool const ret = currentPercentage >= minConsensusPct;
151 if (ret)
152 {
153 CLOG(clog) << ", consensus reached. ";
154 }
155 else
156 {
157 CLOG(clog) << ", consensus not reached. ";
158 }
159 return ret;
160}
161
164 std::size_t prevProposers,
165 std::size_t currentProposers,
166 std::size_t currentAgree,
167 std::size_t currentFinished,
168 std::chrono::milliseconds previousAgreeTime,
169 std::chrono::milliseconds currentAgreeTime,
170 ConsensusParms const& parms,
171 bool proposing,
174{
175 CLOG(clog) << "checkConsensus: prop=" << currentProposers << "/"
176 << prevProposers << " agree=" << currentAgree
177 << " validated=" << currentFinished
178 << " time=" << currentAgreeTime.count() << "/"
179 << previousAgreeTime.count() << " proposing? " << proposing
180 << " minimum duration to reach consensus: "
181 << parms.ledgerMIN_CONSENSUS.count() << "ms"
182 << " max consensus time " << parms.ledgerMAX_CONSENSUS.count()
183 << "s"
184 << " minimum consensus percentage: " << parms.minCONSENSUS_PCT
185 << ". ";
186
187 if (currentAgreeTime <= parms.ledgerMIN_CONSENSUS)
188 {
189 CLOG(clog) << "Not reached. ";
190 return ConsensusState::No;
191 }
192
193 if (currentProposers < (prevProposers * 3 / 4))
194 {
195 // Less than 3/4 of the last ledger's proposers are present; don't
196 // rush: we may need more time.
197 if (currentAgreeTime < (previousAgreeTime + parms.ledgerMIN_CONSENSUS))
198 {
199 JLOG(j.trace()) << "too fast, not enough proposers";
200 CLOG(clog) << "Too fast, not enough proposers. Not reached. ";
201 return ConsensusState::No;
202 }
203 }
204
205 // Have we, together with the nodes on our UNL list, reached the threshold
206 // to declare consensus?
208 currentAgree,
209 currentProposers,
210 proposing,
211 parms.minCONSENSUS_PCT,
212 currentAgreeTime > parms.ledgerMAX_CONSENSUS,
213 clog))
214 {
215 JLOG(j.debug()) << "normal consensus";
216 CLOG(clog) << "reached. ";
217 return ConsensusState::Yes;
218 }
219
220 // Have sufficient nodes on our UNL list moved on and reached the threshold
221 // to declare consensus?
223 currentFinished,
224 currentProposers,
225 false,
226 parms.minCONSENSUS_PCT,
227 currentAgreeTime > parms.ledgerMAX_CONSENSUS,
228 clog))
229 {
230 JLOG(j.warn()) << "We see no consensus, but 80% of nodes have moved on";
231 CLOG(clog) << "We see no consensus, but 80% of nodes have moved on";
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 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:163
@ 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:105
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:26
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.