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