rippled
DisputedTx.h
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 #ifndef RIPPLE_APP_CONSENSUS_IMPL_DISPUTEDTX_H_INCLUDED
21 #define RIPPLE_APP_CONSENSUS_IMPL_DISPUTEDTX_H_INCLUDED
22 
23 #include <ripple/basics/Log.h>
24 #include <ripple/basics/base_uint.h>
25 #include <ripple/beast/utility/Journal.h>
26 #include <ripple/consensus/ConsensusParms.h>
27 #include <ripple/json/json_writer.h>
28 #include <ripple/protocol/Serializer.h>
29 #include <ripple/protocol/UintTypes.h>
30 #include <boost/container/flat_map.hpp>
31 #include <memory>
32 
33 namespace ripple {
34 
49 template <class Tx_t, class NodeID_t>
51 {
52  using TxID_t = typename Tx_t::ID;
53  using Map_t = boost::container::flat_map<NodeID_t, bool>;
54 
55 public:
64  Tx_t const& tx,
65  bool ourVote,
66  std::size_t numPeers,
68  : yays_(0), nays_(0), ourVote_(ourVote), tx_(tx), j_(j)
69  {
70  votes_.reserve(numPeers);
71  }
72 
74  TxID_t const&
75  ID() const
76  {
77  return tx_.id();
78  }
79 
81  bool
82  getOurVote() const
83  {
84  return ourVote_;
85  }
86 
88  Tx_t const&
89  tx() const
90  {
91  return tx_;
92  }
93 
95  void
96  setOurVote(bool o)
97  {
98  ourVote_ = o;
99  }
100 
106  void
107  setVote(NodeID_t const& peer, bool votesYes);
108 
113  void
114  unVote(NodeID_t const& peer);
115 
127  bool
128  updateVote(int percentTime, bool proposing, ConsensusParms const& p);
129 
132  getJson() const;
133 
134 private:
135  int yays_; //< Number of yes votes
136  int nays_; //< Number of no votes
137  bool ourVote_; //< Our vote (true is yes)
138  Tx_t tx_; //< Transaction under dispute
139  Map_t votes_; //< Map from NodeID to vote
141 };
142 
143 // Track a peer's yes/no vote on a particular disputed tx_
144 template <class Tx_t, class NodeID_t>
145 void
146 DisputedTx<Tx_t, NodeID_t>::setVote(NodeID_t const& peer, bool votesYes)
147 {
148  auto const [it, inserted] = votes_.insert(std::make_pair(peer, votesYes));
149 
150  // new vote
151  if (inserted)
152  {
153  if (votesYes)
154  {
155  JLOG(j_.debug()) << "Peer " << peer << " votes YES on " << tx_.id();
156  ++yays_;
157  }
158  else
159  {
160  JLOG(j_.debug()) << "Peer " << peer << " votes NO on " << tx_.id();
161  ++nays_;
162  }
163  }
164  // changes vote to yes
165  else if (votesYes && !it->second)
166  {
167  JLOG(j_.debug()) << "Peer " << peer << " now votes YES on " << tx_.id();
168  --nays_;
169  ++yays_;
170  it->second = true;
171  }
172  // changes vote to no
173  else if (!votesYes && it->second)
174  {
175  JLOG(j_.debug()) << "Peer " << peer << " now votes NO on " << tx_.id();
176  ++nays_;
177  --yays_;
178  it->second = false;
179  }
180 }
181 
182 // Remove a peer's vote on this disputed transaction
183 template <class Tx_t, class NodeID_t>
184 void
186 {
187  auto it = votes_.find(peer);
188 
189  if (it != votes_.end())
190  {
191  if (it->second)
192  --yays_;
193  else
194  --nays_;
195 
196  votes_.erase(it);
197  }
198 }
199 
200 template <class Tx_t, class NodeID_t>
201 bool
203  int percentTime,
204  bool proposing,
205  ConsensusParms const& p)
206 {
207  if (ourVote_ && (nays_ == 0))
208  return false;
209 
210  if (!ourVote_ && (yays_ == 0))
211  return false;
212 
213  bool newPosition;
214  int weight;
215 
216  if (proposing) // give ourselves full weight
217  {
218  // This is basically the percentage of nodes voting 'yes' (including us)
219  weight = (yays_ * 100 + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1);
220 
221  // To prevent avalanche stalls, we increase the needed weight slightly
222  // over time.
223  if (percentTime < p.avMID_CONSENSUS_TIME)
224  newPosition = weight > p.avINIT_CONSENSUS_PCT;
225  else if (percentTime < p.avLATE_CONSENSUS_TIME)
226  newPosition = weight > p.avMID_CONSENSUS_PCT;
227  else if (percentTime < p.avSTUCK_CONSENSUS_TIME)
228  newPosition = weight > p.avLATE_CONSENSUS_PCT;
229  else
230  newPosition = weight > p.avSTUCK_CONSENSUS_PCT;
231  }
232  else
233  {
234  // don't let us outweigh a proposing node, just recognize consensus
235  weight = -1;
236  newPosition = yays_ > nays_;
237  }
238 
239  if (newPosition == ourVote_)
240  {
241  JLOG(j_.info()) << "No change (" << (ourVote_ ? "YES" : "NO")
242  << ") : weight " << weight << ", percent "
243  << percentTime;
244  JLOG(j_.debug()) << Json::Compact{getJson()};
245  return false;
246  }
247 
248  ourVote_ = newPosition;
249  JLOG(j_.debug()) << "We now vote " << (ourVote_ ? "YES" : "NO") << " on "
250  << tx_.id();
251  JLOG(j_.debug()) << Json::Compact{getJson()};
252  return true;
253 }
254 
255 template <class Tx_t, class NodeID_t>
258 {
259  using std::to_string;
260 
262 
263  ret["yays"] = yays_;
264  ret["nays"] = nays_;
265  ret["our_vote"] = ourVote_;
266 
267  if (!votes_.empty())
268  {
270  for (auto const& [nodeId, vote] : votes_)
271  votesj[to_string(nodeId)] = vote;
272  ret["votes"] = std::move(votesj);
273  }
274 
275  return ret;
276 }
277 
278 } // namespace ripple
279 
280 #endif
ripple::DisputedTx::TxID_t
typename Tx_t::ID TxID_t
Definition: DisputedTx.h:52
ripple::DisputedTx::setOurVote
void setOurVote(bool o)
Change our vote.
Definition: DisputedTx.h:96
ripple::DisputedTx::getJson
Json::Value getJson() const
JSON representation of dispute, used for debugging.
Definition: DisputedTx.h:257
ripple::DisputedTx::ourVote_
bool ourVote_
Definition: DisputedTx.h:137
ripple::ConsensusMode::proposing
@ proposing
We are normal participant in consensus and propose our position.
ripple::ConsensusParms::avMID_CONSENSUS_TIME
std::size_t avMID_CONSENSUS_TIME
Percentage of previous round duration before we advance.
Definition: ConsensusParms.h:119
ripple::DisputedTx::j_
const beast::Journal j_
Definition: DisputedTx.h:140
ripple::ConsensusParms::avLATE_CONSENSUS_PCT
std::size_t avLATE_CONSENSUS_PCT
Percentage of nodes that most vote yes after advancing.
Definition: ConsensusParms.h:128
Json::Compact
Decorator for streaming out compact json.
Definition: json_writer.h:316
ripple::ConsensusParms::avSTUCK_CONSENSUS_PCT
std::size_t avSTUCK_CONSENSUS_PCT
Percentage of nodes that must vote yes after we are stuck.
Definition: ConsensusParms.h:134
ripple::DisputedTx::setVote
void setVote(NodeID_t const &peer, bool votesYes)
Change a peer's vote.
Definition: DisputedTx.h:146
ripple::DisputedTx::ID
TxID_t const & ID() const
The unique id/hash of the disputed transaction.
Definition: DisputedTx.h:75
ripple::DisputedTx::tx
Tx_t const & tx() const
The disputed transaction.
Definition: DisputedTx.h:89
ripple::DisputedTx::unVote
void unVote(NodeID_t const &peer)
Remove a peer's vote.
Definition: DisputedTx.h:185
ripple::DisputedTx::DisputedTx
DisputedTx(Tx_t const &tx, bool ourVote, std::size_t numPeers, beast::Journal j)
Constructor.
Definition: DisputedTx.h:63
ripple::DisputedTx::Map_t
boost::container::flat_map< NodeID_t, bool > Map_t
Definition: DisputedTx.h:53
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::DisputedTx::yays_
int yays_
Definition: DisputedTx.h:135
ripple::DisputedTx::getOurVote
bool getOurVote() const
Our vote on whether the transaction should be included.
Definition: DisputedTx.h:82
ripple::DisputedTx::nays_
int nays_
Definition: DisputedTx.h:136
std::to_string
T to_string(T... args)
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::ConsensusParms::avINIT_CONSENSUS_PCT
std::size_t avINIT_CONSENSUS_PCT
Percentage of nodes on our UNL that must vote yes.
Definition: ConsensusParms.h:116
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::ConsensusParms::avMID_CONSENSUS_PCT
std::size_t avMID_CONSENSUS_PCT
Percentage of nodes that most vote yes after advancing.
Definition: ConsensusParms.h:122
memory
ripple::DisputedTx::updateVote
bool updateVote(int percentTime, bool proposing, ConsensusParms const &p)
Update our vote given progression of consensus.
Definition: DisputedTx.h:202
ripple::getJson
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
Definition: LedgerToJson.cpp:296
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::ConsensusParms
Consensus algorithm parameters.
Definition: ConsensusParms.h:33
ripple::DisputedTx::tx_
Tx_t tx_
Definition: DisputedTx.h:138
ripple::ConsensusParms::avSTUCK_CONSENSUS_TIME
std::size_t avSTUCK_CONSENSUS_TIME
Percentage of previous round duration before we are stuck.
Definition: ConsensusParms.h:131
ripple::DisputedTx
A transaction discovered to be in dispute during consensus.
Definition: DisputedTx.h:50
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::ConsensusParms::avLATE_CONSENSUS_TIME
std::size_t avLATE_CONSENSUS_TIME
Percentage of previous round duration before we advance.
Definition: ConsensusParms.h:125
std::make_pair
T make_pair(T... args)
ripple::DisputedTx::votes_
Map_t votes_
Definition: DisputedTx.h:139
Json::Value
Represents a JSON value.
Definition: json_value.h:145