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,
38{
39 using namespace std::chrono_literals;
40 if ((prevRoundTime < -1s) || (prevRoundTime > 10min) ||
41 (timeSincePrevClose > 10min))
42 {
43 // These are unexpected cases, we just close the ledger
44 JLOG(j.warn()) << "shouldCloseLedger Trans="
45 << (anyTransactions ? "yes" : "no")
46 << " Prop: " << prevProposers << "/" << proposersClosed
47 << " Secs: " << timeSincePrevClose.count()
48 << " (last: " << prevRoundTime.count() << ")";
49 return true;
50 }
51
52 if ((proposersClosed + proposersValidated) > (prevProposers / 2))
53 {
54 // If more than half of the network has closed, we close
55 JLOG(j.trace()) << "Others have closed";
56 return true;
57 }
58
59 if (!anyTransactions)
60 {
61 // Only close at the end of the idle interval
62 return timeSincePrevClose >= idleInterval; // normal idle
63 }
64
65 // Preserve minimum ledger open time
66 if (openTime < parms.ledgerMIN_CLOSE)
67 {
68 JLOG(j.debug()) << "Must wait minimum time before closing";
69 return false;
70 }
71
72 // Don't let this ledger close more than twice as fast as the previous
73 // ledger reached consensus so that slower validators can slow down
74 // the network
75 if (openTime < (prevRoundTime / 2))
76 {
77 JLOG(j.debug()) << "Ledger has not been open long enough";
78 return false;
79 }
80
81 // Close the ledger
82 return true;
83}
84
85bool
87 std::size_t agreeing,
88 std::size_t total,
89 bool count_self,
90 std::size_t minConsensusPct,
91 bool reachedMax)
92{
93 // If we are alone for too long, we have consensus.
94 // Delaying consensus like this avoids a circumstance where a peer
95 // gets ahead of proposers insofar as it has not received any proposals.
96 // This could happen if there's a slowdown in receiving proposals. Reaching
97 // consensus prematurely in this way means that the peer will likely desync.
98 // The check for reachedMax should allow plenty of time for proposals to
99 // arrive, and there should be no downside. If a peer is truly not
100 // receiving any proposals, then there should be no hurry. There's
101 // really nowhere to go.
102 if (total == 0)
103 {
104 if (reachedMax)
105 return true;
106 return false;
107 }
108
109 if (count_self)
110 {
111 ++agreeing;
112 ++total;
113 }
114
115 std::size_t currentPercentage = (agreeing * 100) / total;
116
117 return currentPercentage >= minConsensusPct;
118}
119
122 std::size_t prevProposers,
123 std::size_t currentProposers,
124 std::size_t currentAgree,
125 std::size_t currentFinished,
126 std::chrono::milliseconds previousAgreeTime,
127 std::chrono::milliseconds currentAgreeTime,
128 ConsensusParms const& parms,
129 bool proposing,
131{
132 JLOG(j.trace()) << "checkConsensus: prop=" << currentProposers << "/"
133 << prevProposers << " agree=" << currentAgree
134 << " validated=" << currentFinished
135 << " time=" << currentAgreeTime.count() << "/"
136 << previousAgreeTime.count() << " proposing? " << proposing
137 << " minimum duration to reach consensus: "
138 << parms.ledgerMIN_CONSENSUS.count() << "ms"
139 << " max consensus time "
140 << parms.ledgerMAX_CONSENSUS.count() << "s"
141 << " minimum consensus percentage: "
142 << parms.minCONSENSUS_PCT;
143
144 if (currentAgreeTime <= parms.ledgerMIN_CONSENSUS)
145 return ConsensusState::No;
146
147 if (currentProposers < (prevProposers * 3 / 4))
148 {
149 // Less than 3/4 of the last ledger's proposers are present; don't
150 // rush: we may need more time.
151 if (currentAgreeTime < (previousAgreeTime + parms.ledgerMIN_CONSENSUS))
152 {
153 JLOG(j.trace()) << "too fast, not enough proposers";
154 return ConsensusState::No;
155 }
156 }
157
158 // Have we, together with the nodes on our UNL list, reached the threshold
159 // to declare consensus?
161 currentAgree,
162 currentProposers,
163 proposing,
164 parms.minCONSENSUS_PCT,
165 currentAgreeTime > parms.ledgerMAX_CONSENSUS))
166 {
167 JLOG(j.debug()) << "normal consensus";
168 return ConsensusState::Yes;
169 }
170
171 // Have sufficient nodes on our UNL list moved on and reached the threshold
172 // to declare consensus?
174 currentFinished,
175 currentProposers,
176 false,
177 parms.minCONSENSUS_PCT,
178 currentAgreeTime > parms.ledgerMAX_CONSENSUS))
179 {
180 JLOG(j.warn()) << "We see no consensus, but 80% of nodes have moved on";
182 }
183
184 // no consensus yet
185 JLOG(j.trace()) << "no consensus";
186 return ConsensusState::No;
187}
188
189} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:59
Stream debug() const
Definition: Journal.h:317
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
Stream warn() const
Definition: Journal.h:329
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)
Determine whether the network reached consensus and whether we joined.
Definition: Consensus.cpp:121
@ proposing
We are normal participant in consensus and propose our position.
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)
Determines whether the current ledger should close at this time.
Definition: Consensus.cpp:26
bool checkConsensusReached(std::size_t agreeing, std::size_t total, bool count_self, std::size_t minConsensusPct, bool reachedMax)
Definition: Consensus.cpp:86
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.
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.