rippled
app/misc/impl/Manifest.cpp
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 #include <ripple/app/misc/Manifest.h>
21 #include <ripple/basics/Log.h>
22 #include <ripple/basics/StringUtilities.h>
23 #include <ripple/basics/base64.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/beast/rfc2616.h>
26 #include <ripple/core/DatabaseCon.h>
27 #include <ripple/json/json_reader.h>
28 #include <ripple/protocol/PublicKey.h>
29 #include <ripple/protocol/Sign.h>
30 #include <boost/algorithm/clamp.hpp>
31 #include <boost/regex.hpp>
32 #include <numeric>
33 #include <stdexcept>
34 
35 namespace ripple {
36 
37 boost::optional<Manifest>
39 {
40  if (s.empty())
41  return boost::none;
42 
43  static SOTemplate const manifestFormat{
44  // A manifest must include:
45  // - the master public key
47 
48  // - a signature with that public key
50 
51  // - a sequence number
53 
54  // It may, optionally, contain:
55  // - a version number which defaults to 0
57 
58  // - a domain name
60 
61  // - an ephemeral signing key that can be changed as necessary
63 
64  // - a signature using the ephemeral signing key, if it is present
66  };
67 
68  try
69  {
70  SerialIter sit{s};
71  STObject st{sit, sfGeneric};
72 
73  st.applyTemplate(manifestFormat);
74 
75  // We only understand "version 0" manifests at this time:
76  if (st.isFieldPresent(sfVersion) && st.getFieldU16(sfVersion) != 0)
77  return boost::none;
78 
79  auto const pk = st.getFieldVL(sfPublicKey);
80 
81  if (!publicKeyType(makeSlice(pk)))
82  return boost::none;
83 
84  Manifest m;
85  m.serialized.assign(reinterpret_cast<char const*>(s.data()), s.size());
86  m.masterKey = PublicKey(makeSlice(pk));
87  m.sequence = st.getFieldU32(sfSequence);
88 
89  if (st.isFieldPresent(sfDomain))
90  {
91  auto const d = st.getFieldVL(sfDomain);
92 
93  // The domain must be between 4 and 128 characters long
94  if (boost::algorithm::clamp(d.size(), 4, 128) != d.size())
95  return boost::none;
96 
97  m.domain.assign(reinterpret_cast<char const*>(d.data()), d.size());
98 
99  // This regular expression should do a decent job of weeding out
100  // obviously wrong domain names but it isn't perfect. It does not
101  // really support IDNs. If this turns out to be an issue, a more
102  // thorough regex can be used or this check can just be removed.
103  static boost::regex const re(
104  "^" // Beginning of line
105  "(" // Beginning of a segment
106  "(?!-)" // - must not begin with '-'
107  "[a-zA-Z0-9-]{1,63}" // - only alphanumeric and '-'
108  "(?<!-)" // - must not end with '-'
109  "\\." // segment separator
110  ")+" // 1 or more segments
111  "[A-Za-z]{2,63}" // TLD
112  "$" // End of line
113  ,
114  boost::regex_constants::optimize);
115 
116  if (!boost::regex_match(m.domain, re))
117  return boost::none;
118  }
119 
120  bool const hasEphemeralKey = st.isFieldPresent(sfSigningPubKey);
121  bool const hasEphemeralSig = st.isFieldPresent(sfSignature);
122 
123  if (m.revoked())
124  {
125  // Revocation manifests should not specify a new signing key
126  // or a signing key signature.
127  if (hasEphemeralKey)
128  return boost::none;
129 
130  if (hasEphemeralSig)
131  return boost::none;
132  }
133  else
134  {
135  // Regular manifests should contain a signing key and an
136  // associated signature.
137  if (!hasEphemeralKey)
138  return boost::none;
139 
140  if (!hasEphemeralSig)
141  return boost::none;
142 
143  auto const spk = st.getFieldVL(sfSigningPubKey);
144 
145  if (!publicKeyType(makeSlice(spk)))
146  return boost::none;
147 
148  m.signingKey = PublicKey(makeSlice(spk));
149  }
150 
151  return m;
152  }
153  catch (std::exception const&)
154  {
155  return boost::none;
156  }
157 }
158 
159 template <class Stream>
160 Stream&
162  Stream& s,
163  std::string const& action,
164  PublicKey const& pk,
165  std::uint32_t seq)
166 {
167  s << "Manifest: " << action
168  << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq
169  << ";";
170  return s;
171 }
172 
173 template <class Stream>
174 Stream&
176  Stream& s,
177  std::string const& action,
178  PublicKey const& pk,
179  std::uint32_t seq,
180  std::uint32_t oldSeq)
181 {
182  s << "Manifest: " << action
183  << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq
184  << ";OldSeq: " << oldSeq << ";";
185  return s;
186 }
187 
188 bool
190 {
191  STObject st(sfGeneric);
193  st.set(sit);
194 
195  // Signing key and signature are not required for
196  // master key revocations
198  return false;
199 
200  return ripple::verify(
202 }
203 
204 uint256
206 {
207  STObject st(sfGeneric);
209  st.set(sit);
210  return st.getHash(HashPrefix::manifest);
211 }
212 
213 bool
215 {
216  /*
217  The maximum possible sequence number means that the master key
218  has been revoked.
219  */
221 }
222 
223 boost::optional<Blob>
225 {
226  STObject st(sfGeneric);
228  st.set(sit);
229  if (!get(st, sfSignature))
230  return boost::none;
231  return st.getFieldVL(sfSignature);
232 }
233 
234 Blob
236 {
237  STObject st(sfGeneric);
239  st.set(sit);
240  return st.getFieldVL(sfMasterSignature);
241 }
242 
243 boost::optional<ValidatorToken>
245 {
246  try
247  {
248  std::string tokenStr;
249 
250  tokenStr.reserve(std::accumulate(
251  blob.cbegin(),
252  blob.cend(),
253  std::size_t(0),
254  [](std::size_t init, std::string const& s) {
255  return init + s.size();
256  }));
257 
258  for (auto const& line : blob)
259  tokenStr += beast::rfc2616::trim(line);
260 
261  tokenStr = base64_decode(tokenStr);
262 
263  Json::Reader r;
264  Json::Value token;
265 
266  if (r.parse(tokenStr, token))
267  {
268  auto const m = token.get("manifest", Json::Value{});
269  auto const k = token.get("validation_secret_key", Json::Value{});
270 
271  if (m.isString() && k.isString())
272  {
273  auto const key = strUnHex(k.asString());
274 
275  if (key && key->size() == 32)
276  return ValidatorToken{m.asString(), makeSlice(*key)};
277  }
278  }
279 
280  return boost::none;
281  }
282  catch (std::exception const&)
283  {
284  return boost::none;
285  }
286 }
287 
288 PublicKey
290 {
292  auto const iter = map_.find(pk);
293 
294  if (iter != map_.end() && !iter->second.revoked())
295  return iter->second.signingKey;
296 
297  return pk;
298 }
299 
300 PublicKey
302 {
304  auto const iter = signingToMasterKeys_.find(pk);
305 
306  if (iter != signingToMasterKeys_.end())
307  return iter->second;
308 
309  return pk;
310 }
311 
312 boost::optional<std::uint32_t>
314 {
316  auto const iter = map_.find(pk);
317 
318  if (iter != map_.end() && !iter->second.revoked())
319  return iter->second.sequence;
320 
321  return boost::none;
322 }
323 
324 boost::optional<std::string>
326 {
328  auto const iter = map_.find(pk);
329 
330  if (iter != map_.end() && !iter->second.revoked())
331  return iter->second.domain;
332 
333  return boost::none;
334 }
335 
336 boost::optional<std::string>
338 {
340  auto const iter = map_.find(pk);
341 
342  if (iter != map_.end() && !iter->second.revoked())
343  return iter->second.serialized;
344 
345  return boost::none;
346 }
347 
348 bool
350 {
352  auto const iter = map_.find(pk);
353 
354  if (iter != map_.end())
355  return iter->second.revoked();
356 
357  return false;
358 }
359 
362 {
363  std::lock_guard applyLock{apply_mutex_};
364 
365  /*
366  before we spend time checking the signature, make sure the
367  sequence number is newer than any we have.
368  */
369  auto const iter = map_.find(m.masterKey);
370 
371  if (iter != map_.end() && m.sequence <= iter->second.sequence)
372  {
373  /*
374  A manifest was received for a validator we're tracking, but
375  its sequence number is not higher than the one already stored.
376  This will happen normally when a peer without the latest gossip
377  connects.
378  */
379  if (auto stream = j_.debug())
380  logMftAct(
381  stream,
382  "Stale",
383  m.masterKey,
384  m.sequence,
385  iter->second.sequence);
386  return ManifestDisposition::stale; // not a newer manifest, ignore
387  }
388 
389  if (!m.verify())
390  {
391  /*
392  A manifest's signature is invalid.
393  This shouldn't happen normally.
394  */
395  if (auto stream = j_.warn())
396  logMftAct(stream, "Invalid", m.masterKey, m.sequence);
398  }
399 
400  std::lock_guard readLock{read_mutex_};
401 
402  bool const revoked = m.revoked();
403 
404  if (revoked)
405  {
406  /*
407  A validator master key has been compromised, so its manifests
408  are now untrustworthy. In order to prevent us from accepting
409  a forged manifest signed by the compromised master key, store
410  this manifest, which has the highest possible sequence number
411  and therefore can't be superseded by a forged one.
412  */
413  if (auto stream = j_.warn())
414  logMftAct(stream, "Revoked", m.masterKey, m.sequence);
415  }
416 
417  if (iter == map_.end())
418  {
419  /*
420  This is the first received manifest for a trusted master key
421  (possibly our own). This only happens once per validator per
422  run.
423  */
424  if (auto stream = j_.info())
425  logMftAct(stream, "AcceptedNew", m.masterKey, m.sequence);
426 
427  if (!revoked)
429 
430  auto masterKey = m.masterKey;
431  map_.emplace(std::move(masterKey), std::move(m));
432  }
433  else
434  {
435  /*
436  An ephemeral key was revoked and superseded by a new key.
437  This is expected, but should happen infrequently.
438  */
439  if (auto stream = j_.info())
440  logMftAct(
441  stream,
442  "AcceptedUpdate",
443  m.masterKey,
444  m.sequence,
445  iter->second.sequence);
446 
447  signingToMasterKeys_.erase(iter->second.signingKey);
448 
449  if (!revoked)
451 
452  iter->second = std::move(m);
453  }
454 
456 }
457 
458 void
460 {
461  // Load manifests stored in database
462  std::string const sql = "SELECT RawData FROM " + dbTable + ";";
463  auto db = dbCon.checkoutDb();
464  soci::blob sociRawData(*db);
465  soci::statement st = (db->prepare << sql, soci::into(sociRawData));
466  st.execute();
467  while (st.fetch())
468  {
469  std::string serialized;
470  convert(sociRawData, serialized);
471  if (auto mo = deserializeManifest(serialized))
472  {
473  if (!mo->verify())
474  {
475  JLOG(j_.warn()) << "Unverifiable manifest in db";
476  continue;
477  }
478 
479  applyManifest(std::move(*mo));
480  }
481  else
482  {
483  JLOG(j_.warn()) << "Malformed manifest in database";
484  }
485  }
486 }
487 
488 bool
490  DatabaseCon& dbCon,
491  std::string const& dbTable,
492  std::string const& configManifest,
493  std::vector<std::string> const& configRevocation)
494 {
495  load(dbCon, dbTable);
496 
497  if (!configManifest.empty())
498  {
499  auto mo = deserializeManifest(base64_decode(configManifest));
500  if (!mo)
501  {
502  JLOG(j_.error()) << "Malformed validator_token in config";
503  return false;
504  }
505 
506  if (mo->revoked())
507  {
508  JLOG(j_.warn()) << "Configured manifest revokes public key";
509  }
510 
511  if (applyManifest(std::move(*mo)) == ManifestDisposition::invalid)
512  {
513  JLOG(j_.error()) << "Manifest in config was rejected";
514  return false;
515  }
516  }
517 
518  if (!configRevocation.empty())
519  {
520  std::string revocationStr;
521  revocationStr.reserve(std::accumulate(
522  configRevocation.cbegin(),
523  configRevocation.cend(),
524  std::size_t(0),
525  [](std::size_t init, std::string const& s) {
526  return init + s.size();
527  }));
528 
529  for (auto const& line : configRevocation)
530  revocationStr += beast::rfc2616::trim(line);
531 
532  auto mo = deserializeManifest(base64_decode(revocationStr));
533 
534  if (!mo || !mo->revoked() ||
535  applyManifest(std::move(*mo)) == ManifestDisposition::invalid)
536  {
537  JLOG(j_.error()) << "Invalid validator key revocation in config";
538  return false;
539  }
540  }
541 
542  return true;
543 }
544 
545 void
547  DatabaseCon& dbCon,
548  std::string const& dbTable,
549  std::function<bool(PublicKey const&)> isTrusted)
550 {
552 
553  auto db = dbCon.checkoutDb();
554 
555  soci::transaction tr(*db);
556  *db << "DELETE FROM " << dbTable;
557  std::string const sql =
558  "INSERT INTO " + dbTable + " (RawData) VALUES (:rawData);";
559  for (auto const& v : map_)
560  {
561  // Save all revocation manifests,
562  // but only save trusted non-revocation manifests.
563  if (!v.second.revoked() && !isTrusted(v.second.masterKey))
564  {
565  JLOG(j_.info()) << "Untrusted manifest in cache not saved to db";
566  continue;
567  }
568 
569  // soci does not support bulk insertion of blob data
570  // Do not reuse blob because manifest ecdsa signatures vary in length
571  // but blob write length is expected to be >= the last write
572  soci::blob rawData(*db);
573  convert(v.second.serialized, rawData);
574  *db << sql, soci::use(rawData);
575  }
576  tr.commit();
577 }
578 } // namespace ripple
ripple::Slice::size
std::size_t size() const noexcept
Returns the number of bytes in the storage.
Definition: Slice.h:79
ripple::ManifestCache::save
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> isTrusted)
Save cached manifests to database.
Definition: app/misc/impl/Manifest.cpp:546
ripple::Manifest::domain
std::string domain
The domain, if one was specified in the manifest; empty otherwise.
Definition: Manifest.h:93
ripple::makeSlice
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:240
std::string
STL class.
ripple::ManifestDisposition
ManifestDisposition
Definition: Manifest.h:179
ripple::ManifestCache::getMasterKey
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
Definition: app/misc/impl/Manifest.cpp:301
std::exception
STL class.
ripple::sfGeneric
const SField sfGeneric(access, 0)
Definition: SField.h:332
ripple::publicKeyType
boost::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:203
ripple::Manifest
Definition: Manifest.h:78
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
std::string::reserve
T reserve(T... args)
ripple::HashPrefix::manifest
@ manifest
Manifest.
ripple::convert
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:156
ripple::sfSigningPubKey
const SF_Blob sfSigningPubKey(access, STI_VL, 3, "SigningPubKey")
Definition: SField.h:459
Json::Value::get
Value get(UInt index, const Value &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
Definition: json_value.cpp:834
ripple::Manifest::signingKey
PublicKey signingKey
The ephemeral key associated with this manifest.
Definition: Manifest.h:87
std::vector< unsigned char >
ripple::Manifest::serialized
std::string serialized
The manifest in serialized form.
Definition: Manifest.h:81
std::string::size
T size(T... args)
ripple::sfSequence
const SF_U32 sfSequence(access, STI_UINT32, 4, "Sequence")
Definition: SField.h:356
ripple::Manifest::masterKey
PublicKey masterKey
The master key associated with this manifest.
Definition: Manifest.h:84
ripple::Manifest::verify
bool verify() const
Returns true if manifest signature is valid.
Definition: app/misc/impl/Manifest.cpp:189
ripple::sfMasterSignature
const SF_Blob sfMasterSignature(access, STI_VL, 18, "MasterSignature", SField::sMD_Default, SField::notSigning)
Definition: SField.h:474
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::Slice::data
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition: Slice.h:96
ripple::ValidatorToken
Definition: Manifest.h:170
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::lock_guard
STL class.
ripple::ManifestCache::map_
hash_map< PublicKey, Manifest > map_
Active manifests stored by master public key.
Definition: Manifest.h:217
ripple::soeREQUIRED
@ soeREQUIRED
Definition: SOTemplate.h:34
ripple::strUnHex
boost::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
Definition: StringUtilities.h:72
std::function
ripple::STObject::getFieldVL
Blob getFieldVL(SField const &field) const
Definition: STObject.cpp:568
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
ripple::Slice::empty
bool empty() const noexcept
Return true if the byte range is empty.
Definition: Slice.h:68
ripple::ManifestCache::apply_mutex_
std::mutex apply_mutex_
Definition: Manifest.h:213
ripple::sfSignature
const SF_Blob sfSignature(access, STI_VL, 6, "Signature", SField::sMD_Default, SField::notSigning)
Definition: SField.h:461
ripple::Manifest::revoked
bool revoked() const
Returns true if manifest revokes master key.
Definition: app/misc/impl/Manifest.cpp:214
ripple::ManifestCache::read_mutex_
std::mutex read_mutex_
Definition: Manifest.h:214
beast::rfc2616::trim
std::pair< FwdIter, FwdIter > trim(FwdIter first, FwdIter last)
Definition: rfc2616.h:164
ripple::verify
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical)
Verify a signature on a message.
Definition: PublicKey.cpp:268
stdexcept
ripple::base_uint< 256 >
ripple::Manifest::getSignature
boost::optional< Blob > getSignature() const
Returns manifest signature.
Definition: app/misc/impl/Manifest.cpp:224
ripple::SOTemplate
Defines the fields and their attributes within a STObject.
Definition: SOTemplate.h:81
ripple::DatabaseCon::checkoutDb
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:176
ripple::ManifestCache::signingToMasterKeys_
hash_map< PublicKey, PublicKey > signingToMasterKeys_
Master public keys stored by current ephemeral public key.
Definition: Manifest.h:220
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::soeOPTIONAL
@ soeOPTIONAL
Definition: SOTemplate.h:35
ripple::ManifestCache::revoked
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
Definition: app/misc/impl/Manifest.cpp:349
ripple::ManifestCache::getManifest
boost::optional< std::string > getManifest(PublicKey const &pk) const
Returns mainfest corresponding to a given public key.
Definition: app/misc/impl/Manifest.cpp:337
ripple::base64_decode
std::string base64_decode(std::string const &data)
Definition: base64.cpp:245
beast::Journal::error
Stream error() const
Definition: Journal.h:333
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::ManifestCache::j_
beast::Journal j_
Definition: Manifest.h:212
std::accumulate
T accumulate(T... args)
ripple::SerialIter
Definition: Serializer.h:308
ripple::ManifestCache::getSigningKey
PublicKey getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
Definition: app/misc/impl/Manifest.cpp:289
std::uint32_t
ripple::Manifest::getMasterSignature
Blob getMasterSignature() const
Returns manifest master key signature.
Definition: app/misc/impl/Manifest.cpp:235
ripple::ManifestDisposition::invalid
@ invalid
Timely, but invalid signature.
ripple::sfVersion
const SF_U16 sfVersion(access, STI_UINT16, 16, "Version")
Definition: SField.h:351
ripple::ManifestDisposition::accepted
@ accepted
Manifest is valid.
ripple::Manifest::sequence
std::uint32_t sequence
The sequence number of this manifest.
Definition: Manifest.h:90
ripple::STObject
Definition: STObject.h:51
ripple::sfPublicKey
const SF_Blob sfPublicKey(access, STI_VL, 1, "PublicKey")
Definition: SField.h:457
ripple::ManifestDisposition::stale
@ stale
Sequence is too old.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::deserializeManifest
boost::optional< Manifest > deserializeManifest(Slice s)
Constructs Manifest from serialized string.
Definition: app/misc/impl/Manifest.cpp:38
ripple::loadValidatorToken
boost::optional< ValidatorToken > loadValidatorToken(std::vector< std::string > const &blob)
Definition: app/misc/impl/Manifest.cpp:244
std::vector::cbegin
T cbegin(T... args)
ripple::logMftAct
Stream & logMftAct(Stream &s, std::string const &action, PublicKey const &pk, std::uint32_t seq)
Definition: app/misc/impl/Manifest.cpp:161
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:73
ripple::DatabaseCon
Definition: DatabaseCon.h:81
ripple::Manifest::hash
uint256 hash() const
Returns hash of serialized manifest data.
Definition: app/misc/impl/Manifest.cpp:205
std::string::empty
T empty(T... args)
std::string::assign
T assign(T... args)
ripple::TokenType::NodePublic
@ NodePublic
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
std::vector::cend
T cend(T... args)
ripple::ManifestCache::getSequence
boost::optional< std::uint32_t > getSequence(PublicKey const &pk) const
Returns master key's current manifest sequence.
Definition: app/misc/impl/Manifest.cpp:313
numeric
std::numeric_limits::max
T max(T... args)
ripple::ManifestCache::load
bool load(DatabaseCon &dbCon, std::string const &dbTable, std::string const &configManifest, std::vector< std::string > const &configRevocation)
Populate manifest cache with manifests in database and config.
Definition: app/misc/impl/Manifest.cpp:489
ripple::ManifestCache::applyManifest
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
Definition: app/misc/impl/Manifest.cpp:361
ripple::STObject::set
void set(const SOTemplate &)
Definition: STObject.cpp:73
ripple::ManifestCache::getDomain
boost::optional< std::string > getDomain(PublicKey const &pk) const
Returns domain claimed by a given public key.
Definition: app/misc/impl/Manifest.cpp:325
std::string::data
T data(T... args)
ripple::soeDEFAULT
@ soeDEFAULT
Definition: SOTemplate.h:36
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:116
ripple::STObject::getHash
uint256 getHash(HashPrefix prefix) const
Definition: STObject.cpp:312
ripple::sfDomain
const SF_Blob sfDomain(access, STI_VL, 7, "Domain")
Definition: SField.h:462