rippled
PeerReservationTable.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2019 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 <ripple/overlay/PeerReservationTable.h>
21 
22 #include <ripple/basics/Log.h>
23 #include <ripple/core/DatabaseCon.h>
24 #include <ripple/json/json_value.h>
25 #include <ripple/protocol/PublicKey.h>
26 #include <ripple/protocol/jss.h>
27 
28 #include <boost/optional.hpp>
29 
30 #include <algorithm>
31 #include <iterator>
32 #include <mutex>
33 #include <string>
34 #include <vector>
35 
36 namespace ripple {
37 
38 auto
39 PeerReservation::toJson() const -> Json::Value
40 {
42  result[jss::node] = toBase58(TokenType::NodePublic, nodeId);
43  if (!description.empty())
44  {
45  result[jss::description] = description;
46  }
47  return result;
48 }
49 
50 auto
52 {
54  {
55  std::lock_guard lock(mutex_);
56  list.reserve(table_.size());
57  std::copy(table_.begin(), table_.end(), std::back_inserter(list));
58  }
59  std::sort(list.begin(), list.end());
60  return list;
61 }
62 
63 // See `ripple/app/main/DBInit.cpp` for the `CREATE TABLE` statement.
64 // It is unfortunate that we do not get to define a function for it.
65 
66 // We choose a `bool` return type to fit in with the error handling scheme
67 // of other functions called from `ApplicationImp::setup`, but we always
68 // return "no error" (`true`) because we can always return an empty table.
69 bool
71 {
72  std::lock_guard lock(mutex_);
73 
74  connection_ = &connection;
75  auto db = connection_->checkoutDb();
76 
77  boost::optional<std::string> valPubKey, valDesc;
78  // We should really abstract the table and column names into constants,
79  // but no one else does. Because it is too tedious? It would be easy if we
80  // had a jOOQ for C++.
81  soci::statement st =
82  (db->prepare << "SELECT PublicKey, Description FROM PeerReservations;",
83  soci::into(valPubKey),
84  soci::into(valDesc));
85  st.execute();
86  while (st.fetch())
87  {
88  if (!valPubKey || !valDesc)
89  {
90  // This represents a `NULL` in a `NOT NULL` column. It should be
91  // unreachable.
92  continue;
93  }
94  auto const optNodeId =
95  parseBase58<PublicKey>(TokenType::NodePublic, *valPubKey);
96  if (!optNodeId)
97  {
98  JLOG(journal_.warn()) << "load: not a public key: " << valPubKey;
99  continue;
100  }
101  table_.insert(PeerReservation{*optNodeId, *valDesc});
102  }
103 
104  return true;
105 }
106 
107 auto
109  PeerReservation const& reservation)
110  -> boost::optional<PeerReservation>
111 {
112  boost::optional<PeerReservation> previous;
113 
114  std::lock_guard lock(mutex_);
115 
116  auto hint = table_.find(reservation);
117  if (hint != table_.end()) {
118  // The node already has a reservation. Remove it.
119  // `std::unordered_set` does not have an `insert_or_assign` method,
120  // and sadly makes it impossible for us to implement one efficiently:
121  // https://stackoverflow.com/q/49651835/618906
122  // Regardless, we don't expect this function to be called often, or
123  // for the table to be very large, so this less-than-ideal
124  // remove-then-insert is acceptable in order to present a better API.
125  previous = *hint;
126  // We should pick an adjacent location for the insertion hint.
127  // Decrementing may be illegal if the found reservation is at the
128  // beginning. Incrementing is always legal; at worst we'll point to
129  // the end.
130  auto const deleteme = hint;
131  ++hint;
132  table_.erase(deleteme);
133  }
134  table_.insert(hint, reservation);
135 
136  auto db = connection_->checkoutDb();
137  *db << "INSERT INTO PeerReservations (PublicKey, Description) "
138  "VALUES (:nodeId, :desc) "
139  "ON CONFLICT (PublicKey) DO UPDATE SET "
140  "Description=excluded.Description",
141  soci::use(toBase58(TokenType::NodePublic, reservation.nodeId)),
142  soci::use(reservation.description);
143 
144  return previous;
145 }
146 
147 auto
149  -> boost::optional<PeerReservation>
150 {
151  boost::optional<PeerReservation> previous;
152 
153  std::lock_guard lock(mutex_);
154 
155  auto const it = table_.find({nodeId});
156  if (it != table_.end())
157  {
158  previous = *it;
159  table_.erase(it);
160  auto db = connection_->checkoutDb();
161  *db << "DELETE FROM PeerReservations WHERE PublicKey = :nodeId",
162  soci::use(toBase58(TokenType::NodePublic, nodeId));
163  }
164 
165  return previous;
166 }
167 
168 } // namespace ripple
ripple::PeerReservationTable::table_
std::unordered_set< PeerReservation, beast::uhash<>, KeyEqual > table_
Definition: PeerReservationTable.h:118
vector
std::back_inserter
T back_inserter(T... args)
iterator
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
beast::Journal::warn
Stream warn() const
Definition: Journal.h:302
std::lock_guard
STL class.
ripple::PeerReservationTable::connection_
DatabaseCon * connection_
Definition: PeerReservationTable.h:117
ripple::PeerReservation::toJson
auto toJson() const -> Json::Value
Definition: PeerReservationTable.cpp:39
std::sort
T sort(T... args)
algorithm
ripple::PeerReservationTable::erase
auto erase(PublicKey const &nodeId) -> boost::optional< PeerReservation >
Definition: PeerReservationTable.cpp:148
Json
JSON (JavaScript Object Notation).
Definition: json_reader.cpp:26
ripple::DatabaseCon::checkoutDb
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:123
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:45
ripple::PeerReservation::nodeId
PublicKey nodeId
Definition: PeerReservationTable.h:46
ripple::PeerReservationTable::journal_
beast::Journal journal_
Definition: PeerReservationTable.h:115
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
std::copy
T copy(T... args)
ripple::PeerReservation
Definition: PeerReservationTable.h:43
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::PeerReservationTable::mutex_
std::mutex mutex_
Definition: PeerReservationTable.h:116
ripple::PeerReservationTable::list
std::vector< PeerReservation > list() const
Definition: PeerReservationTable.cpp:51
std
STL namespace.
ripple::DatabaseCon
Definition: DatabaseCon.h:77
std::string::empty
T empty(T... args)
ripple::TokenType::NodePublic
@ NodePublic
mutex
ripple::PeerReservation::description
std::string description
Definition: PeerReservationTable.h:47
ripple::PeerReservationTable::insert_or_assign
auto insert_or_assign(PeerReservation const &reservation) -> boost::optional< PeerReservation >
Definition: PeerReservationTable.cpp:108
ripple::PeerReservationTable::load
bool load(DatabaseCon &connection)
Definition: PeerReservationTable.cpp:70
Json::Value
Represents a JSON value.
Definition: json_value.h:141
string