rippled
Loading...
Searching...
No Matches
Wallet.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2021 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/app/rdb/Wallet.h>
21
22#include <boost/format.hpp>
23
24namespace ripple {
25
28{
29 // wallet database
32}
33
36 DatabaseCon::Setup const& setup,
37 std::string const& dbname,
39{
40 // wallet database
42 setup, dbname.data(), std::array<std::string, 0>(), WalletDBInit, j);
43}
44
45void
47 soci::session& session,
48 std::string const& dbTable,
49 ManifestCache& mCache,
51{
52 // Load manifests stored in database
53 std::string const sql = "SELECT RawData FROM " + dbTable + ";";
54 soci::blob sociRawData(session);
55 soci::statement st = (session.prepare << sql, soci::into(sociRawData));
56 st.execute();
57 while (st.fetch())
58 {
59 std::string serialized;
60 convert(sociRawData, serialized);
61 if (auto mo = deserializeManifest(serialized))
62 {
63 if (!mo->verify())
64 {
65 JLOG(j.warn()) << "Unverifiable manifest in db";
66 continue;
67 }
68
69 mCache.applyManifest(std::move(*mo));
70 }
71 else
72 {
73 JLOG(j.warn()) << "Malformed manifest in database";
74 }
75 }
76}
77
78static void
80 soci::session& session,
81 std::string const& dbTable,
82 std::string const& serialized)
83{
84 // soci does not support bulk insertion of blob data
85 // Do not reuse blob because manifest ecdsa signatures vary in length
86 // but blob write length is expected to be >= the last write
87 soci::blob rawData(session);
88 convert(serialized, rawData);
89 session << "INSERT INTO " << dbTable << " (RawData) VALUES (:rawData);",
90 soci::use(rawData);
91}
92
93void
95 soci::session& session,
96 std::string const& dbTable,
97 std::function<bool(PublicKey const&)> const& isTrusted,
100{
101 soci::transaction tr(session);
102 session << "DELETE FROM " << dbTable;
103 for (auto const& v : map)
104 {
105 // Save all revocation manifests,
106 // but only save trusted non-revocation manifests.
107 if (!v.second.revoked() && !isTrusted(v.second.masterKey))
108 {
109 JLOG(j.info()) << "Untrusted manifest in cache not saved to db";
110 continue;
111 }
112
113 saveManifest(session, dbTable, v.second.serialized);
114 }
115 tr.commit();
116}
117
118void
119addValidatorManifest(soci::session& session, std::string const& serialized)
120{
121 soci::transaction tr(session);
122 saveManifest(session, "ValidatorManifests", serialized);
123 tr.commit();
124}
125
126void
127clearNodeIdentity(soci::session& session)
128{
129 session << "DELETE FROM NodeIdentity;";
130}
131
133getNodeIdentity(soci::session& session)
134{
135 {
136 // SOCI requires boost::optional (not std::optional) as the parameter.
137 boost::optional<std::string> pubKO, priKO;
138 soci::statement st =
139 (session.prepare
140 << "SELECT PublicKey, PrivateKey FROM NodeIdentity;",
141 soci::into(pubKO),
142 soci::into(priKO));
143 st.execute();
144 while (st.fetch())
145 {
146 auto const sk = parseBase58<SecretKey>(
147 TokenType::NodePrivate, priKO.value_or(""));
148 auto const pk = parseBase58<PublicKey>(
149 TokenType::NodePublic, pubKO.value_or(""));
150
151 // Only use if the public and secret keys are a pair
152 if (sk && pk && (*pk == derivePublicKey(KeyType::secp256k1, *sk)))
153 return {*pk, *sk};
154 }
155 }
156
157 // If a valid identity wasn't found, we randomly generate a new one:
158 auto [newpublicKey, newsecretKey] = randomKeyPair(KeyType::secp256k1);
159
160 session << str(
161 boost::format("INSERT INTO NodeIdentity (PublicKey,PrivateKey) "
162 "VALUES ('%s','%s');") %
163 toBase58(TokenType::NodePublic, newpublicKey) %
164 toBase58(TokenType::NodePrivate, newsecretKey));
165
166 return {newpublicKey, newsecretKey};
167}
168
170getPeerReservationTable(soci::session& session, beast::Journal j)
171{
173 // These values must be boost::optionals (not std) because SOCI expects
174 // boost::optionals.
175 boost::optional<std::string> valPubKey, valDesc;
176 // We should really abstract the table and column names into constants,
177 // but no one else does. Because it is too tedious? It would be easy if we
178 // had a jOOQ for C++.
179 soci::statement st =
180 (session.prepare
181 << "SELECT PublicKey, Description FROM PeerReservations;",
182 soci::into(valPubKey),
183 soci::into(valDesc));
184 st.execute();
185 while (st.fetch())
186 {
187 if (!valPubKey || !valDesc)
188 {
189 // This represents a `NULL` in a `NOT NULL` column. It should be
190 // unreachable.
191 continue;
192 }
193 auto const optNodeId =
194 parseBase58<PublicKey>(TokenType::NodePublic, *valPubKey);
195 if (!optNodeId)
196 {
197 JLOG(j.warn()) << "load: not a public key: " << valPubKey;
198 continue;
199 }
200 table.insert(PeerReservation{*optNodeId, *valDesc});
201 }
202
203 return table;
204}
205
206void
208 soci::session& session,
209 PublicKey const& nodeId,
210 std::string const& description)
211{
212 auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
213 session << "INSERT INTO PeerReservations (PublicKey, Description) "
214 "VALUES (:nodeId, :desc) "
215 "ON CONFLICT (PublicKey) DO UPDATE SET "
216 "Description=excluded.Description",
217 soci::use(sNodeId), soci::use(description);
218}
219
220void
221deletePeerReservation(soci::session& session, PublicKey const& nodeId)
222{
223 auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
224 session << "DELETE FROM PeerReservations WHERE PublicKey = :nodeId",
225 soci::use(sNodeId);
226}
227
228bool
229createFeatureVotes(soci::session& session)
230{
231 soci::transaction tr(session);
232 std::string sql =
233 "SELECT count(*) FROM sqlite_master "
234 "WHERE type='table' AND name='FeatureVotes'";
235 // SOCI requires boost::optional (not std::optional) as the parameter.
236 boost::optional<int> featureVotesCount;
237 session << sql, soci::into(featureVotesCount);
238 bool exists = static_cast<bool>(*featureVotesCount);
239
240 // Create FeatureVotes table in WalletDB if it doesn't exist
241 if (!exists)
242 {
243 session << "CREATE TABLE FeatureVotes ( "
244 "AmendmentHash CHARACTER(64) NOT NULL, "
245 "AmendmentName TEXT, "
246 "Veto INTEGER NOT NULL );";
247 tr.commit();
248 }
249 return exists;
250}
251
252void
254 soci::session& session,
255 std::function<void(
256 boost::optional<std::string> amendment_hash,
257 boost::optional<std::string> amendment_name,
258 boost::optional<AmendmentVote> vote)> const& callback)
259{
260 // lambda that converts the internally stored int to an AmendmentVote.
261 auto intToVote = [](boost::optional<int> const& dbVote)
262 -> boost::optional<AmendmentVote> {
263 return safe_cast<AmendmentVote>(dbVote.value_or(1));
264 };
265
266 soci::transaction tr(session);
267 std::string sql =
268 "SELECT AmendmentHash, AmendmentName, Veto FROM "
269 "( SELECT AmendmentHash, AmendmentName, Veto, RANK() OVER "
270 "( PARTITION BY AmendmentHash ORDER BY ROWID DESC ) "
271 "as rnk FROM FeatureVotes ) WHERE rnk = 1";
272 // SOCI requires boost::optional (not std::optional) as parameters.
273 boost::optional<std::string> amendment_hash;
274 boost::optional<std::string> amendment_name;
275 boost::optional<int> vote_to_veto;
276 soci::statement st =
277 (session.prepare << sql,
278 soci::into(amendment_hash),
279 soci::into(amendment_name),
280 soci::into(vote_to_veto));
281 st.execute();
282 while (st.fetch())
283 {
284 callback(amendment_hash, amendment_name, intToVote(vote_to_veto));
285 }
286}
287
288void
290 soci::session& session,
291 uint256 const& amendment,
292 std::string const& name,
293 AmendmentVote vote)
294{
295 soci::transaction tr(session);
296 std::string sql =
297 "INSERT INTO FeatureVotes (AmendmentHash, AmendmentName, Veto) VALUES "
298 "('";
299 sql += to_string(amendment);
300 sql += "', '" + name;
301 sql += "', '" + std::to_string(safe_cast<int>(vote)) + "');";
302 session << sql;
303 tr.commit();
304}
305
306} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:60
Stream info() const
Definition Journal.h:334
Stream warn() const
Definition Journal.h:340
Remembers manifests with the highest sequence number.
Definition Manifest.h:256
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
Definition Manifest.cpp:383
A public key.
Definition PublicKey.h:62
T data(T... args)
T insert(T... args)
T is_same_v
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
std::optional< Manifest > deserializeManifest(Slice s, beast::Journal journal)
Constructs Manifest from serialized string.
Definition Manifest.cpp:54
AmendmentVote
Definition Wallet.h:150
void saveManifests(soci::session &session, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted, hash_map< PublicKey, Manifest > const &map, beast::Journal j)
saveManifests Saves all given manifests to the database.
Definition Wallet.cpp:94
std::unordered_set< PeerReservation, beast::uhash<>, KeyEqual > getPeerReservationTable(soci::session &session, beast::Journal j)
getPeerReservationTable Returns the peer reservation table.
Definition Wallet.cpp:170
void insertPeerReservation(soci::session &session, PublicKey const &nodeId, std::string const &description)
insertPeerReservation Adds an entry to the peer reservation table.
Definition Wallet.cpp:207
void addValidatorManifest(soci::session &session, std::string const &serialized)
addValidatorManifest Saves the manifest of a validator to the database.
Definition Wallet.cpp:119
std::pair< PublicKey, SecretKey > getNodeIdentity(Application &app, boost::program_options::variables_map const &cmdline)
The cryptographic credentials identifying this server instance.
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:229
std::unique_ptr< DatabaseCon > makeTestWalletDB(DatabaseCon::Setup const &setup, std::string const &dbname, beast::Journal j)
makeTestWalletDB Opens a test wallet database with an arbitrary name.
Definition Wallet.cpp:35
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition SociDB.cpp:157
constexpr std::array< char const *, 6 > WalletDBInit
Definition DBInit.h:107
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendment_hash, boost::optional< std::string > amendment_name, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
Definition Wallet.cpp:253
void clearNodeIdentity(soci::session &session)
Delete any saved public/private key associated with this node.
Definition Wallet.cpp:127
std::unique_ptr< DatabaseCon > makeWalletDB(DatabaseCon::Setup const &setup, beast::Journal j)
makeWalletDB Opens the wallet database and returns it.
Definition Wallet.cpp:27
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
void getManifests(soci::session &session, std::string const &dbTable, ManifestCache &mCache, beast::Journal j)
getManifests Loads a manifest from the wallet database and stores it in the cache.
Definition Wallet.cpp:46
void deletePeerReservation(soci::session &session, PublicKey const &nodeId)
deletePeerReservation Deletes an entry from the peer reservation table.
Definition Wallet.cpp:221
static void saveManifest(soci::session &session, std::string const &dbTable, std::string const &serialized)
Definition Wallet.cpp:79
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
Definition Wallet.cpp:289
constexpr auto WalletDBName
Definition DBInit.h:105
T to_string(T... args)