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