mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-19 10:05:48 +00:00
Allow manifests to include an optional 'domain' field:
The new 'Domain' field allows validator operators to associate a domain name with their manifest in a transparent and independently verifiable fashion. It is important to point out that while this system can cryptographically prove that a particular validator claims to be associated with a domain it does *NOT* prove that the validator is, actually, associated with that domain. Domain owners will have to cryptographically attest to operating particular validators that claim to be associated with that domain. One option for doing so would be by making available a file over HTTPS under the domain being claimed, which is verified separately (e.g. by ensuring that the certificate used to serve the file matches the domain being claimed) and which contains the long-term master public keys of validator(s) associated with that domain. Credit for an early prototype of this idea goes to GitHub user @cryptobrad who introduced a PR that would allow a validator list publisher to attest that a particular validator was associated with a domain. The idea may be worth revisiting as a way of verifying the domain name claimed by the validator's operator.
This commit is contained in:
@@ -77,40 +77,27 @@ namespace ripple {
|
||||
|
||||
struct Manifest
|
||||
{
|
||||
/// The manifest in serialized form.
|
||||
std::string serialized;
|
||||
|
||||
/// The master key associated with this manifest.
|
||||
PublicKey masterKey;
|
||||
|
||||
/// The ephemeral key associated with this manifest.
|
||||
PublicKey signingKey;
|
||||
std::uint32_t sequence;
|
||||
|
||||
Manifest(std::string s, PublicKey pk, PublicKey spk, std::uint32_t seq);
|
||||
/// The sequence number of this manifest.
|
||||
std::uint32_t sequence = 0;
|
||||
|
||||
/// The domain, if one was specified in the manifest; empty otherwise.
|
||||
std::string domain;
|
||||
|
||||
Manifest() = default;
|
||||
Manifest(Manifest const& other) = delete;
|
||||
Manifest& operator=(Manifest const& other) = delete;
|
||||
Manifest(Manifest&& other) = default;
|
||||
Manifest& operator=(Manifest&& other) = default;
|
||||
|
||||
inline bool
|
||||
operator==(Manifest const& rhs) const
|
||||
{
|
||||
return sequence == rhs.sequence && masterKey == rhs.masterKey &&
|
||||
signingKey == rhs.signingKey && serialized == rhs.serialized;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(Manifest const& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
/** Constructs Manifest from serialized string
|
||||
|
||||
@param s Serialized manifest string
|
||||
|
||||
@return `boost::none` if string is invalid
|
||||
|
||||
@note This does not verify manifest signatures.
|
||||
`Manifest::verify` should be called after constructing manifest.
|
||||
*/
|
||||
static boost::optional<Manifest> make_Manifest(std::string s);
|
||||
|
||||
/// Returns `true` if manifest signature is valid
|
||||
bool verify () const;
|
||||
|
||||
@@ -127,6 +114,55 @@ struct Manifest
|
||||
Blob getMasterSignature () const;
|
||||
};
|
||||
|
||||
/** Constructs Manifest from serialized string
|
||||
|
||||
@param s Serialized manifest string
|
||||
|
||||
@return `boost::none` if string is invalid
|
||||
|
||||
@note This does not verify manifest signatures.
|
||||
`Manifest::verify` should be called after constructing manifest.
|
||||
*/
|
||||
/** @{ */
|
||||
boost::optional<Manifest>
|
||||
deserializeManifest(Slice s);
|
||||
|
||||
inline
|
||||
boost::optional<Manifest>
|
||||
deserializeManifest(std::string const& s)
|
||||
{
|
||||
return deserializeManifest(makeSlice(s));
|
||||
}
|
||||
|
||||
template <class T, class = std::enable_if_t<
|
||||
std::is_same<T, char>::value || std::is_same<T, unsigned char>::value>>
|
||||
boost::optional<Manifest>
|
||||
deserializeManifest(std::vector<T> const& v)
|
||||
{
|
||||
return deserializeManifest(makeSlice(v));
|
||||
}
|
||||
/** @} */
|
||||
|
||||
inline
|
||||
bool
|
||||
operator==(Manifest const& lhs, Manifest const& rhs)
|
||||
{
|
||||
// In theory, comparing the two serialized strings should be
|
||||
// sufficient.
|
||||
return lhs.sequence == rhs.sequence &&
|
||||
lhs.masterKey == rhs.masterKey &&
|
||||
lhs.signingKey == rhs.signingKey &&
|
||||
lhs.domain == rhs.domain &&
|
||||
lhs.serialized == rhs.serialized;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
operator!=(Manifest const& lhs, Manifest const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
struct ValidatorToken
|
||||
{
|
||||
std::string manifest;
|
||||
|
||||
@@ -27,44 +27,128 @@
|
||||
#include <ripple/json/json_reader.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/Sign.h>
|
||||
#include <boost/algorithm/clamp.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
boost::optional<Manifest>
|
||||
Manifest::make_Manifest (std::string s)
|
||||
boost::optional<Manifest> deserializeManifest(Slice s)
|
||||
{
|
||||
if (s.empty())
|
||||
return boost::none;
|
||||
|
||||
static SOTemplate const manifestFormat (
|
||||
[](SOTemplate& t)
|
||||
{
|
||||
// A manifest must include:
|
||||
// - the master public key
|
||||
t.push_back (SOElement (sfPublicKey, SOE_REQUIRED));
|
||||
|
||||
// - a signature with that public key
|
||||
t.push_back (SOElement (sfMasterSignature, SOE_REQUIRED));
|
||||
|
||||
// - a sequence number
|
||||
t.push_back (SOElement (sfSequence, SOE_REQUIRED));
|
||||
|
||||
// It may, optionally, contain:
|
||||
// - a version number which defaults to 0
|
||||
t.push_back (SOElement (sfVersion, SOE_DEFAULT));
|
||||
|
||||
// - a domain name
|
||||
t.push_back (SOElement (sfDomain, SOE_OPTIONAL));
|
||||
|
||||
// - an ephemeral signing key that can be changed as necessary
|
||||
t.push_back (SOElement (sfSigningPubKey, SOE_OPTIONAL));
|
||||
|
||||
// - a signature using the ephemeral signing key, if it is present
|
||||
t.push_back (SOElement (sfSignature, SOE_OPTIONAL));
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
STObject st (sfGeneric);
|
||||
SerialIter sit (s.data (), s.size ());
|
||||
st.set (sit);
|
||||
SerialIter sit{ s };
|
||||
STObject st{ sit, sfGeneric };
|
||||
|
||||
st.applyTemplate(manifestFormat);
|
||||
|
||||
// We only understand "version 0" manifests at this time:
|
||||
if (st.isFieldPresent(sfVersion) && st.getFieldU16(sfVersion) != 0)
|
||||
return boost::none;
|
||||
|
||||
auto const pk = st.getFieldVL (sfPublicKey);
|
||||
|
||||
if (! publicKeyType (makeSlice(pk)))
|
||||
return boost::none;
|
||||
|
||||
auto const opt_seq = get (st, sfSequence);
|
||||
auto const opt_msig = get (st, sfMasterSignature);
|
||||
if (!opt_seq || !opt_msig)
|
||||
return boost::none;
|
||||
Manifest m;
|
||||
m.serialized.assign(reinterpret_cast<char const*>(s.data()), s.size());
|
||||
m.masterKey = PublicKey(makeSlice(pk));
|
||||
m.sequence = st.getFieldU32 (sfSequence);
|
||||
|
||||
// Signing key and signature are not required for
|
||||
// master key revocations
|
||||
if (*opt_seq != std::numeric_limits<std::uint32_t>::max ())
|
||||
if (st.isFieldPresent(sfDomain))
|
||||
{
|
||||
auto const spk = st.getFieldVL (sfSigningPubKey);
|
||||
if (! publicKeyType (makeSlice(spk)))
|
||||
auto const d = st.getFieldVL(sfDomain);
|
||||
|
||||
// The domain must be between 4 and 128 characters long
|
||||
if (boost::algorithm::clamp(d.size(), 4, 128) != d.size())
|
||||
return boost::none;
|
||||
if (! get (st, sfSignature))
|
||||
|
||||
m.domain.assign (reinterpret_cast<char const*>(d.data()), d.size());
|
||||
|
||||
// This regular expression should do a decent job of weeding out
|
||||
// obviously wrong domain names but it isn't perfect. It does not
|
||||
// really support IDNs. If this turns out to be an issue, a more
|
||||
// thorough regex can be used or this check can just be removed.
|
||||
static boost::regex const re(
|
||||
"^" // Beginning of line
|
||||
"(" // Beginning of a segment
|
||||
"(?!-)" // - must not begin with '-'
|
||||
"[a-zA-Z0-9-]{1,63}" // - only alphanumeric and '-'
|
||||
"(?<!-)" // - must not end with '-'
|
||||
"\\." // segment separator
|
||||
")+" // 1 or more segments
|
||||
"[A-Za-z]{2,63}" // TLD
|
||||
"$" // End of line
|
||||
, boost::regex_constants::optimize);
|
||||
|
||||
if (!boost::regex_match(m.domain, re))
|
||||
return boost::none;
|
||||
return Manifest (std::move (s), PublicKey (makeSlice(pk)),
|
||||
PublicKey (makeSlice(spk)), *opt_seq);
|
||||
}
|
||||
|
||||
return Manifest (std::move (s), PublicKey (makeSlice(pk)),
|
||||
PublicKey(), *opt_seq);
|
||||
bool const hasEphemeralKey = st.isFieldPresent(sfSigningPubKey);
|
||||
bool const hasEphemeralSig = st.isFieldPresent(sfSignature);
|
||||
|
||||
if (m.revoked())
|
||||
{
|
||||
// Revocation manifests should not specify a new signing key
|
||||
// or a signing key signature.
|
||||
if (hasEphemeralKey)
|
||||
return boost::none;
|
||||
|
||||
if (hasEphemeralSig)
|
||||
return boost::none;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular manifests should contain a signing key and an
|
||||
// associated signature.
|
||||
if (!hasEphemeralKey)
|
||||
return boost::none;
|
||||
|
||||
if (!hasEphemeralSig)
|
||||
return boost::none;
|
||||
|
||||
auto const spk = st.getFieldVL(sfSigningPubKey);
|
||||
|
||||
if (!publicKeyType (makeSlice(spk)))
|
||||
return boost::none;
|
||||
|
||||
m.signingKey = PublicKey(makeSlice(spk));
|
||||
}
|
||||
|
||||
return std::move(m);
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
@@ -101,17 +185,6 @@ Stream& logMftAct (
|
||||
return s;
|
||||
}
|
||||
|
||||
Manifest::Manifest (std::string s,
|
||||
PublicKey pk,
|
||||
PublicKey spk,
|
||||
std::uint32_t seq)
|
||||
: serialized (std::move (s))
|
||||
, masterKey (std::move (pk))
|
||||
, signingKey (std::move (spk))
|
||||
, sequence (seq)
|
||||
{
|
||||
}
|
||||
|
||||
bool Manifest::verify () const
|
||||
{
|
||||
STObject st (sfGeneric);
|
||||
@@ -194,7 +267,7 @@ ValidatorToken::make_ValidatorToken(std::vector<std::string> const& tokenBlob)
|
||||
token["validation_secret_key"].isString())
|
||||
{
|
||||
auto const ret = strUnHex (token["validation_secret_key"].asString());
|
||||
if (! ret.second || ! ret.first.size ())
|
||||
if (! ret.second || ret.first.empty())
|
||||
return boost::none;
|
||||
|
||||
return ValidatorToken(
|
||||
@@ -355,7 +428,7 @@ ManifestCache::load (
|
||||
{
|
||||
std::string serialized;
|
||||
convert (sociRawData, serialized);
|
||||
if (auto mo = Manifest::make_Manifest (std::move (serialized)))
|
||||
if (auto mo = deserializeManifest(serialized))
|
||||
{
|
||||
if (!mo->verify())
|
||||
{
|
||||
@@ -384,8 +457,7 @@ ManifestCache::load (
|
||||
|
||||
if (! configManifest.empty())
|
||||
{
|
||||
auto mo = Manifest::make_Manifest (
|
||||
base64_decode(configManifest));
|
||||
auto mo = deserializeManifest(base64_decode(configManifest));
|
||||
if (! mo)
|
||||
{
|
||||
JLOG (j_.error()) << "Malformed validator_token in config";
|
||||
@@ -419,8 +491,7 @@ ManifestCache::load (
|
||||
for (auto const& line : configRevocation)
|
||||
revocationStr += beast::rfc2616::trim(line);
|
||||
|
||||
auto mo = Manifest::make_Manifest (
|
||||
base64_decode(revocationStr));
|
||||
auto mo = deserializeManifest(base64_decode(revocationStr));
|
||||
|
||||
if (! mo || ! mo->revoked() ||
|
||||
applyManifest (std::move(*mo)) == ManifestDisposition::invalid)
|
||||
|
||||
@@ -44,8 +44,7 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j)
|
||||
{
|
||||
auto const pk = derivePublicKey(
|
||||
KeyType::secp256k1, token->validationSecret);
|
||||
auto const m = Manifest::make_Manifest(
|
||||
base64_decode(token->manifest));
|
||||
auto const m = deserializeManifest(base64_decode(token->manifest));
|
||||
|
||||
if (! m || pk != m->signingKey)
|
||||
{
|
||||
|
||||
@@ -291,8 +291,7 @@ ValidatorList::applyList (
|
||||
|
||||
for (auto const& valManifest : manifests)
|
||||
{
|
||||
auto m = Manifest::make_Manifest (
|
||||
base64_decode(valManifest));
|
||||
auto m = deserializeManifest(base64_decode(valManifest));
|
||||
|
||||
if (! m || ! keyListings_.count (m->masterKey))
|
||||
{
|
||||
@@ -322,7 +321,7 @@ ValidatorList::verify (
|
||||
std::string const& blob,
|
||||
std::string const& signature)
|
||||
{
|
||||
auto m = Manifest::make_Manifest (base64_decode(manifest));
|
||||
auto m = deserializeManifest(base64_decode(manifest));
|
||||
|
||||
if (! m || ! publisherLists_.count (m->masterKey))
|
||||
return ListDisposition::untrusted;
|
||||
|
||||
@@ -44,27 +44,27 @@ invoke_preflight (PreflightContext const& ctx)
|
||||
{
|
||||
switch(ctx.tx.getTxnType())
|
||||
{
|
||||
case ttACCOUNT_SET: return SetAccount ::preflight(ctx);
|
||||
case ttCHECK_CANCEL: return CancelCheck ::preflight(ctx);
|
||||
case ttCHECK_CASH: return CashCheck ::preflight(ctx);
|
||||
case ttCHECK_CREATE: return CreateCheck ::preflight(ctx);
|
||||
case ttDEPOSIT_PREAUTH: return DepositPreauth ::preflight(ctx);
|
||||
case ttOFFER_CANCEL: return CancelOffer ::preflight(ctx);
|
||||
case ttOFFER_CREATE: return CreateOffer ::preflight(ctx);
|
||||
case ttESCROW_CREATE: return EscrowCreate ::preflight(ctx);
|
||||
case ttESCROW_FINISH: return EscrowFinish ::preflight(ctx);
|
||||
case ttESCROW_CANCEL: return EscrowCancel ::preflight(ctx);
|
||||
case ttPAYCHAN_CLAIM: return PayChanClaim ::preflight(ctx);
|
||||
case ttPAYCHAN_CREATE: return PayChanCreate ::preflight(ctx);
|
||||
case ttPAYCHAN_FUND: return PayChanFund ::preflight(ctx);
|
||||
case ttPAYMENT: return Payment ::preflight(ctx);
|
||||
case ttREGULAR_KEY_SET: return SetRegularKey ::preflight(ctx);
|
||||
case ttSIGNER_LIST_SET: return SetSignerList ::preflight(ctx);
|
||||
case ttTICKET_CANCEL: return CancelTicket ::preflight(ctx);
|
||||
case ttTICKET_CREATE: return CreateTicket ::preflight(ctx);
|
||||
case ttTRUST_SET: return SetTrust ::preflight(ctx);
|
||||
case ttACCOUNT_SET: return SetAccount ::preflight(ctx);
|
||||
case ttCHECK_CANCEL: return CancelCheck ::preflight(ctx);
|
||||
case ttCHECK_CASH: return CashCheck ::preflight(ctx);
|
||||
case ttCHECK_CREATE: return CreateCheck ::preflight(ctx);
|
||||
case ttDEPOSIT_PREAUTH: return DepositPreauth ::preflight(ctx);
|
||||
case ttOFFER_CANCEL: return CancelOffer ::preflight(ctx);
|
||||
case ttOFFER_CREATE: return CreateOffer ::preflight(ctx);
|
||||
case ttESCROW_CREATE: return EscrowCreate ::preflight(ctx);
|
||||
case ttESCROW_FINISH: return EscrowFinish ::preflight(ctx);
|
||||
case ttESCROW_CANCEL: return EscrowCancel ::preflight(ctx);
|
||||
case ttPAYCHAN_CLAIM: return PayChanClaim ::preflight(ctx);
|
||||
case ttPAYCHAN_CREATE: return PayChanCreate ::preflight(ctx);
|
||||
case ttPAYCHAN_FUND: return PayChanFund ::preflight(ctx);
|
||||
case ttPAYMENT: return Payment ::preflight(ctx);
|
||||
case ttREGULAR_KEY_SET: return SetRegularKey ::preflight(ctx);
|
||||
case ttSIGNER_LIST_SET: return SetSignerList ::preflight(ctx);
|
||||
case ttTICKET_CANCEL: return CancelTicket ::preflight(ctx);
|
||||
case ttTICKET_CREATE: return CreateTicket ::preflight(ctx);
|
||||
case ttTRUST_SET: return SetTrust ::preflight(ctx);
|
||||
case ttAMENDMENT:
|
||||
case ttFEE: return Change ::preflight(ctx);
|
||||
case ttFEE: return Change ::preflight(ctx);
|
||||
default:
|
||||
assert(false);
|
||||
return temUNKNOWN;
|
||||
@@ -113,27 +113,27 @@ invoke_preclaim (PreclaimContext const& ctx)
|
||||
{
|
||||
switch(ctx.tx.getTxnType())
|
||||
{
|
||||
case ttACCOUNT_SET: return invoke_preclaim<SetAccount>(ctx);
|
||||
case ttCHECK_CANCEL: return invoke_preclaim<CancelCheck>(ctx);
|
||||
case ttCHECK_CASH: return invoke_preclaim<CashCheck>(ctx);
|
||||
case ttCHECK_CREATE: return invoke_preclaim<CreateCheck>(ctx);
|
||||
case ttDEPOSIT_PREAUTH: return invoke_preclaim<DepositPreauth>(ctx);
|
||||
case ttOFFER_CANCEL: return invoke_preclaim<CancelOffer>(ctx);
|
||||
case ttOFFER_CREATE: return invoke_preclaim<CreateOffer>(ctx);
|
||||
case ttESCROW_CREATE: return invoke_preclaim<EscrowCreate>(ctx);
|
||||
case ttESCROW_FINISH: return invoke_preclaim<EscrowFinish>(ctx);
|
||||
case ttESCROW_CANCEL: return invoke_preclaim<EscrowCancel>(ctx);
|
||||
case ttPAYCHAN_CLAIM: return invoke_preclaim<PayChanClaim>(ctx);
|
||||
case ttPAYCHAN_CREATE: return invoke_preclaim<PayChanCreate>(ctx);
|
||||
case ttPAYCHAN_FUND: return invoke_preclaim<PayChanFund>(ctx);
|
||||
case ttPAYMENT: return invoke_preclaim<Payment>(ctx);
|
||||
case ttREGULAR_KEY_SET: return invoke_preclaim<SetRegularKey>(ctx);
|
||||
case ttSIGNER_LIST_SET: return invoke_preclaim<SetSignerList>(ctx);
|
||||
case ttTICKET_CANCEL: return invoke_preclaim<CancelTicket>(ctx);
|
||||
case ttTICKET_CREATE: return invoke_preclaim<CreateTicket>(ctx);
|
||||
case ttTRUST_SET: return invoke_preclaim<SetTrust>(ctx);
|
||||
case ttACCOUNT_SET: return invoke_preclaim<SetAccount>(ctx);
|
||||
case ttCHECK_CANCEL: return invoke_preclaim<CancelCheck>(ctx);
|
||||
case ttCHECK_CASH: return invoke_preclaim<CashCheck>(ctx);
|
||||
case ttCHECK_CREATE: return invoke_preclaim<CreateCheck>(ctx);
|
||||
case ttDEPOSIT_PREAUTH: return invoke_preclaim<DepositPreauth>(ctx);
|
||||
case ttOFFER_CANCEL: return invoke_preclaim<CancelOffer>(ctx);
|
||||
case ttOFFER_CREATE: return invoke_preclaim<CreateOffer>(ctx);
|
||||
case ttESCROW_CREATE: return invoke_preclaim<EscrowCreate>(ctx);
|
||||
case ttESCROW_FINISH: return invoke_preclaim<EscrowFinish>(ctx);
|
||||
case ttESCROW_CANCEL: return invoke_preclaim<EscrowCancel>(ctx);
|
||||
case ttPAYCHAN_CLAIM: return invoke_preclaim<PayChanClaim>(ctx);
|
||||
case ttPAYCHAN_CREATE: return invoke_preclaim<PayChanCreate>(ctx);
|
||||
case ttPAYCHAN_FUND: return invoke_preclaim<PayChanFund>(ctx);
|
||||
case ttPAYMENT: return invoke_preclaim<Payment>(ctx);
|
||||
case ttREGULAR_KEY_SET: return invoke_preclaim<SetRegularKey>(ctx);
|
||||
case ttSIGNER_LIST_SET: return invoke_preclaim<SetSignerList>(ctx);
|
||||
case ttTICKET_CANCEL: return invoke_preclaim<CancelTicket>(ctx);
|
||||
case ttTICKET_CREATE: return invoke_preclaim<CreateTicket>(ctx);
|
||||
case ttTRUST_SET: return invoke_preclaim<SetTrust>(ctx);
|
||||
case ttAMENDMENT:
|
||||
case ttFEE: return invoke_preclaim<Change>(ctx);
|
||||
case ttFEE: return invoke_preclaim<Change>(ctx);
|
||||
default:
|
||||
assert(false);
|
||||
return temUNKNOWN;
|
||||
@@ -148,27 +148,27 @@ invoke_calculateBaseFee(
|
||||
{
|
||||
switch (tx.getTxnType())
|
||||
{
|
||||
case ttACCOUNT_SET: return SetAccount::calculateBaseFee(view, tx);
|
||||
case ttCHECK_CANCEL: return CancelCheck::calculateBaseFee(view, tx);
|
||||
case ttCHECK_CASH: return CashCheck::calculateBaseFee(view, tx);
|
||||
case ttCHECK_CREATE: return CreateCheck::calculateBaseFee(view, tx);
|
||||
case ttDEPOSIT_PREAUTH: return DepositPreauth::calculateBaseFee(view, tx);
|
||||
case ttOFFER_CANCEL: return CancelOffer::calculateBaseFee(view, tx);
|
||||
case ttOFFER_CREATE: return CreateOffer::calculateBaseFee(view, tx);
|
||||
case ttESCROW_CREATE: return EscrowCreate::calculateBaseFee(view, tx);
|
||||
case ttESCROW_FINISH: return EscrowFinish::calculateBaseFee(view, tx);
|
||||
case ttESCROW_CANCEL: return EscrowCancel::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_CLAIM: return PayChanClaim::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_CREATE: return PayChanCreate::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_FUND: return PayChanFund::calculateBaseFee(view, tx);
|
||||
case ttPAYMENT: return Payment::calculateBaseFee(view, tx);
|
||||
case ttREGULAR_KEY_SET: return SetRegularKey::calculateBaseFee(view, tx);
|
||||
case ttSIGNER_LIST_SET: return SetSignerList::calculateBaseFee(view, tx);
|
||||
case ttTICKET_CANCEL: return CancelTicket::calculateBaseFee(view, tx);
|
||||
case ttTICKET_CREATE: return CreateTicket::calculateBaseFee(view, tx);
|
||||
case ttTRUST_SET: return SetTrust::calculateBaseFee(view, tx);
|
||||
case ttACCOUNT_SET: return SetAccount::calculateBaseFee(view, tx);
|
||||
case ttCHECK_CANCEL: return CancelCheck::calculateBaseFee(view, tx);
|
||||
case ttCHECK_CASH: return CashCheck::calculateBaseFee(view, tx);
|
||||
case ttCHECK_CREATE: return CreateCheck::calculateBaseFee(view, tx);
|
||||
case ttDEPOSIT_PREAUTH: return DepositPreauth::calculateBaseFee(view, tx);
|
||||
case ttOFFER_CANCEL: return CancelOffer::calculateBaseFee(view, tx);
|
||||
case ttOFFER_CREATE: return CreateOffer::calculateBaseFee(view, tx);
|
||||
case ttESCROW_CREATE: return EscrowCreate::calculateBaseFee(view, tx);
|
||||
case ttESCROW_FINISH: return EscrowFinish::calculateBaseFee(view, tx);
|
||||
case ttESCROW_CANCEL: return EscrowCancel::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_CLAIM: return PayChanClaim::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_CREATE: return PayChanCreate::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_FUND: return PayChanFund::calculateBaseFee(view, tx);
|
||||
case ttPAYMENT: return Payment::calculateBaseFee(view, tx);
|
||||
case ttREGULAR_KEY_SET: return SetRegularKey::calculateBaseFee(view, tx);
|
||||
case ttSIGNER_LIST_SET: return SetSignerList::calculateBaseFee(view, tx);
|
||||
case ttTICKET_CANCEL: return CancelTicket::calculateBaseFee(view, tx);
|
||||
case ttTICKET_CREATE: return CreateTicket::calculateBaseFee(view, tx);
|
||||
case ttTRUST_SET: return SetTrust::calculateBaseFee(view, tx);
|
||||
case ttAMENDMENT:
|
||||
case ttFEE: return Change::calculateBaseFee(view, tx);
|
||||
case ttFEE: return Change::calculateBaseFee(view, tx);
|
||||
default:
|
||||
assert(false);
|
||||
return 0;
|
||||
@@ -194,25 +194,25 @@ invoke_calculateConsequences(STTx const& tx)
|
||||
{
|
||||
switch (tx.getTxnType())
|
||||
{
|
||||
case ttACCOUNT_SET: return invoke_calculateConsequences<SetAccount>(tx);
|
||||
case ttCHECK_CANCEL: return invoke_calculateConsequences<CancelCheck>(tx);
|
||||
case ttCHECK_CASH: return invoke_calculateConsequences<CashCheck>(tx);
|
||||
case ttCHECK_CREATE: return invoke_calculateConsequences<CreateCheck>(tx);
|
||||
case ttDEPOSIT_PREAUTH: return invoke_calculateConsequences<DepositPreauth>(tx);
|
||||
case ttOFFER_CANCEL: return invoke_calculateConsequences<CancelOffer>(tx);
|
||||
case ttOFFER_CREATE: return invoke_calculateConsequences<CreateOffer>(tx);
|
||||
case ttESCROW_CREATE: return invoke_calculateConsequences<EscrowCreate>(tx);
|
||||
case ttESCROW_FINISH: return invoke_calculateConsequences<EscrowFinish>(tx);
|
||||
case ttESCROW_CANCEL: return invoke_calculateConsequences<EscrowCancel>(tx);
|
||||
case ttPAYCHAN_CLAIM: return invoke_calculateConsequences<PayChanClaim>(tx);
|
||||
case ttPAYCHAN_CREATE: return invoke_calculateConsequences<PayChanCreate>(tx);
|
||||
case ttPAYCHAN_FUND: return invoke_calculateConsequences<PayChanFund>(tx);
|
||||
case ttPAYMENT: return invoke_calculateConsequences<Payment>(tx);
|
||||
case ttREGULAR_KEY_SET: return invoke_calculateConsequences<SetRegularKey>(tx);
|
||||
case ttSIGNER_LIST_SET: return invoke_calculateConsequences<SetSignerList>(tx);
|
||||
case ttTICKET_CANCEL: return invoke_calculateConsequences<CancelTicket>(tx);
|
||||
case ttTICKET_CREATE: return invoke_calculateConsequences<CreateTicket>(tx);
|
||||
case ttTRUST_SET: return invoke_calculateConsequences<SetTrust>(tx);
|
||||
case ttACCOUNT_SET: return invoke_calculateConsequences<SetAccount>(tx);
|
||||
case ttCHECK_CANCEL: return invoke_calculateConsequences<CancelCheck>(tx);
|
||||
case ttCHECK_CASH: return invoke_calculateConsequences<CashCheck>(tx);
|
||||
case ttCHECK_CREATE: return invoke_calculateConsequences<CreateCheck>(tx);
|
||||
case ttDEPOSIT_PREAUTH: return invoke_calculateConsequences<DepositPreauth>(tx);
|
||||
case ttOFFER_CANCEL: return invoke_calculateConsequences<CancelOffer>(tx);
|
||||
case ttOFFER_CREATE: return invoke_calculateConsequences<CreateOffer>(tx);
|
||||
case ttESCROW_CREATE: return invoke_calculateConsequences<EscrowCreate>(tx);
|
||||
case ttESCROW_FINISH: return invoke_calculateConsequences<EscrowFinish>(tx);
|
||||
case ttESCROW_CANCEL: return invoke_calculateConsequences<EscrowCancel>(tx);
|
||||
case ttPAYCHAN_CLAIM: return invoke_calculateConsequences<PayChanClaim>(tx);
|
||||
case ttPAYCHAN_CREATE: return invoke_calculateConsequences<PayChanCreate>(tx);
|
||||
case ttPAYCHAN_FUND: return invoke_calculateConsequences<PayChanFund>(tx);
|
||||
case ttPAYMENT: return invoke_calculateConsequences<Payment>(tx);
|
||||
case ttREGULAR_KEY_SET: return invoke_calculateConsequences<SetRegularKey>(tx);
|
||||
case ttSIGNER_LIST_SET: return invoke_calculateConsequences<SetSignerList>(tx);
|
||||
case ttTICKET_CANCEL: return invoke_calculateConsequences<CancelTicket>(tx);
|
||||
case ttTICKET_CREATE: return invoke_calculateConsequences<CreateTicket>(tx);
|
||||
case ttTRUST_SET: return invoke_calculateConsequences<SetTrust>(tx);
|
||||
case ttAMENDMENT:
|
||||
case ttFEE:
|
||||
// fall through to default
|
||||
@@ -229,27 +229,27 @@ invoke_apply (ApplyContext& ctx)
|
||||
{
|
||||
switch(ctx.tx.getTxnType())
|
||||
{
|
||||
case ttACCOUNT_SET: { SetAccount p(ctx); return p(); }
|
||||
case ttCHECK_CANCEL: { CancelCheck p(ctx); return p(); }
|
||||
case ttCHECK_CASH: { CashCheck p(ctx); return p(); }
|
||||
case ttCHECK_CREATE: { CreateCheck p(ctx); return p(); }
|
||||
case ttDEPOSIT_PREAUTH: { DepositPreauth p(ctx); return p(); }
|
||||
case ttOFFER_CANCEL: { CancelOffer p(ctx); return p(); }
|
||||
case ttOFFER_CREATE: { CreateOffer p(ctx); return p(); }
|
||||
case ttESCROW_CREATE: { EscrowCreate p(ctx); return p(); }
|
||||
case ttESCROW_FINISH: { EscrowFinish p(ctx); return p(); }
|
||||
case ttESCROW_CANCEL: { EscrowCancel p(ctx); return p(); }
|
||||
case ttPAYCHAN_CLAIM: { PayChanClaim p(ctx); return p(); }
|
||||
case ttPAYCHAN_CREATE: { PayChanCreate p(ctx); return p(); }
|
||||
case ttPAYCHAN_FUND: { PayChanFund p(ctx); return p(); }
|
||||
case ttPAYMENT: { Payment p(ctx); return p(); }
|
||||
case ttREGULAR_KEY_SET: { SetRegularKey p(ctx); return p(); }
|
||||
case ttSIGNER_LIST_SET: { SetSignerList p(ctx); return p(); }
|
||||
case ttTICKET_CANCEL: { CancelTicket p(ctx); return p(); }
|
||||
case ttTICKET_CREATE: { CreateTicket p(ctx); return p(); }
|
||||
case ttTRUST_SET: { SetTrust p(ctx); return p(); }
|
||||
case ttACCOUNT_SET: { SetAccount p(ctx); return p(); }
|
||||
case ttCHECK_CANCEL: { CancelCheck p(ctx); return p(); }
|
||||
case ttCHECK_CASH: { CashCheck p(ctx); return p(); }
|
||||
case ttCHECK_CREATE: { CreateCheck p(ctx); return p(); }
|
||||
case ttDEPOSIT_PREAUTH: { DepositPreauth p(ctx); return p(); }
|
||||
case ttOFFER_CANCEL: { CancelOffer p(ctx); return p(); }
|
||||
case ttOFFER_CREATE: { CreateOffer p(ctx); return p(); }
|
||||
case ttESCROW_CREATE: { EscrowCreate p(ctx); return p(); }
|
||||
case ttESCROW_FINISH: { EscrowFinish p(ctx); return p(); }
|
||||
case ttESCROW_CANCEL: { EscrowCancel p(ctx); return p(); }
|
||||
case ttPAYCHAN_CLAIM: { PayChanClaim p(ctx); return p(); }
|
||||
case ttPAYCHAN_CREATE: { PayChanCreate p(ctx); return p(); }
|
||||
case ttPAYCHAN_FUND: { PayChanFund p(ctx); return p(); }
|
||||
case ttPAYMENT: { Payment p(ctx); return p(); }
|
||||
case ttREGULAR_KEY_SET : { SetRegularKey p(ctx); return p(); }
|
||||
case ttSIGNER_LIST_SET : { SetSignerList p(ctx); return p(); }
|
||||
case ttTICKET_CANCEL: { CancelTicket p(ctx); return p(); }
|
||||
case ttTICKET_CREATE: { CreateTicket p(ctx); return p(); }
|
||||
case ttTRUST_SET: { SetTrust p(ctx); return p(); }
|
||||
case ttAMENDMENT:
|
||||
case ttFEE: { Change p(ctx); return p(); }
|
||||
case ttFEE: { Change p(ctx); return p(); }
|
||||
default:
|
||||
assert(false);
|
||||
return { temUNKNOWN, false };
|
||||
|
||||
@@ -669,7 +669,7 @@ OverlayImpl::onManifests (
|
||||
{
|
||||
auto& s = m->list ().Get (i).stobject ();
|
||||
|
||||
if (auto mo = Manifest::make_Manifest (s))
|
||||
if (auto mo = deserializeManifest(s))
|
||||
{
|
||||
uint256 const hash = mo->hash ();
|
||||
if (!hashRouter.addSuppressionPeer (hash, from->id ())) {
|
||||
@@ -691,8 +691,7 @@ OverlayImpl::onManifests (
|
||||
|
||||
if (result == ManifestDisposition::accepted)
|
||||
{
|
||||
app_.getOPs().pubManifest (
|
||||
*Manifest::make_Manifest(serialized));
|
||||
app_.getOPs().pubManifest (*deserializeManifest(serialized));
|
||||
}
|
||||
|
||||
if (result == ManifestDisposition::accepted)
|
||||
|
||||
@@ -194,7 +194,6 @@ JSS ( frozen_balances ); // out: GatewayBalances
|
||||
JSS ( full ); // in: LedgerClearer, handlers/Ledger
|
||||
JSS ( full_reply ); // out: PathFind
|
||||
JSS ( fullbelow_size ); // in: GetCounts
|
||||
JSS ( generator ); // in: LedgerEntry
|
||||
JSS ( good ); // out: RPCVersion
|
||||
JSS ( hash ); // out: NetworkOPs, InboundLedger,
|
||||
// LedgerToJson, STTx; field
|
||||
|
||||
@@ -142,8 +142,7 @@ operator== (PublicKey const& lhs,
|
||||
PublicKey const& rhs)
|
||||
{
|
||||
return lhs.size() == rhs.size() &&
|
||||
std::memcmp(lhs.data(),
|
||||
rhs.data(), rhs.size()) == 0;
|
||||
std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -153,7 +152,7 @@ operator< (PublicKey const& lhs,
|
||||
{
|
||||
return std::lexicographical_compare(
|
||||
lhs.data(), lhs.data() + lhs.size(),
|
||||
rhs.data(), rhs.data() + rhs.size());
|
||||
rhs.data(), rhs.data() + rhs.size());
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
|
||||
@@ -338,6 +338,9 @@ extern SF_U16 const sfLedgerEntryType;
|
||||
extern SF_U16 const sfTransactionType;
|
||||
extern SF_U16 const sfSignerWeight;
|
||||
|
||||
// 16-bit integers (uncommon)
|
||||
extern SF_U16 const sfVersion;
|
||||
|
||||
// 32-bit integers (common)
|
||||
extern SF_U32 const sfFlags;
|
||||
extern SF_U32 const sfSourceTag;
|
||||
|
||||
@@ -77,6 +77,20 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/** Create a template and pass it to the callback to be populated.
|
||||
|
||||
The callback will typically consist of one or more calls to the
|
||||
@ref push_back member function on the object that it is passed.
|
||||
|
||||
@see push_back
|
||||
*/
|
||||
SOTemplate(std::function<void(SOTemplate&)> callback)
|
||||
: SOTemplate()
|
||||
{
|
||||
if (callback)
|
||||
callback(*this);
|
||||
}
|
||||
|
||||
/* Provide for the enumeration of fields */
|
||||
iterator_range all () const
|
||||
{
|
||||
|
||||
@@ -264,7 +264,7 @@ enum TECcodes : TERUnderlyingType
|
||||
tecINVARIANT_FAILED = 147,
|
||||
tecEXPIRED = 148,
|
||||
tecDUPLICATE = 149,
|
||||
tecKILLED = 150
|
||||
tecKILLED = 150,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -32,32 +32,32 @@ namespace ripple {
|
||||
*/
|
||||
enum TxType
|
||||
{
|
||||
ttINVALID = -1,
|
||||
ttINVALID = -1,
|
||||
|
||||
ttPAYMENT = 0,
|
||||
ttESCROW_CREATE = 1,
|
||||
ttESCROW_FINISH = 2,
|
||||
ttACCOUNT_SET = 3,
|
||||
ttESCROW_CANCEL = 4,
|
||||
ttREGULAR_KEY_SET = 5,
|
||||
ttNICKNAME_SET = 6, // open
|
||||
ttOFFER_CREATE = 7,
|
||||
ttOFFER_CANCEL = 8,
|
||||
no_longer_used = 9,
|
||||
ttTICKET_CREATE = 10,
|
||||
ttTICKET_CANCEL = 11,
|
||||
ttSIGNER_LIST_SET = 12,
|
||||
ttPAYCHAN_CREATE = 13,
|
||||
ttPAYCHAN_FUND = 14,
|
||||
ttPAYCHAN_CLAIM = 15,
|
||||
ttCHECK_CREATE = 16,
|
||||
ttCHECK_CASH = 17,
|
||||
ttCHECK_CANCEL = 18,
|
||||
ttDEPOSIT_PREAUTH = 19,
|
||||
ttTRUST_SET = 20,
|
||||
ttPAYMENT = 0,
|
||||
ttESCROW_CREATE = 1,
|
||||
ttESCROW_FINISH = 2,
|
||||
ttACCOUNT_SET = 3,
|
||||
ttESCROW_CANCEL = 4,
|
||||
ttREGULAR_KEY_SET = 5,
|
||||
ttNICKNAME_SET = 6, // open
|
||||
ttOFFER_CREATE = 7,
|
||||
ttOFFER_CANCEL = 8,
|
||||
no_longer_used = 9,
|
||||
ttTICKET_CREATE = 10,
|
||||
ttTICKET_CANCEL = 11,
|
||||
ttSIGNER_LIST_SET = 12,
|
||||
ttPAYCHAN_CREATE = 13,
|
||||
ttPAYCHAN_FUND = 14,
|
||||
ttPAYCHAN_CLAIM = 15,
|
||||
ttCHECK_CREATE = 16,
|
||||
ttCHECK_CASH = 17,
|
||||
ttCHECK_CANCEL = 18,
|
||||
ttDEPOSIT_PREAUTH = 19,
|
||||
ttTRUST_SET = 20,
|
||||
|
||||
ttAMENDMENT = 100,
|
||||
ttFEE = 101,
|
||||
ttAMENDMENT = 100,
|
||||
ttFEE = 101,
|
||||
};
|
||||
|
||||
/** Manages the list of known transaction formats.
|
||||
|
||||
@@ -116,7 +116,7 @@ detail::supportedAmendments ()
|
||||
"fix1515",
|
||||
"fix1578",
|
||||
"MultiSignReserve",
|
||||
"fixTakerDryOfferRemoval"
|
||||
"fixTakerDryOfferRemoval",
|
||||
};
|
||||
return supported;
|
||||
}
|
||||
|
||||
@@ -188,14 +188,16 @@ PublicKey::PublicKey (Slice const& slice)
|
||||
PublicKey::PublicKey (PublicKey const& other)
|
||||
: size_ (other.size_)
|
||||
{
|
||||
std::memcpy(buf_, other.buf_, size_);
|
||||
if (size_)
|
||||
std::memcpy(buf_, other.buf_, size_);
|
||||
};
|
||||
|
||||
PublicKey&
|
||||
PublicKey::operator=(PublicKey const& other)
|
||||
{
|
||||
size_ = other.size_;
|
||||
std::memcpy(buf_, other.buf_, size_);
|
||||
if (size_)
|
||||
std::memcpy(buf_, other.buf_, size_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,9 +88,12 @@ SF_U8 const sfTransactionResult = make::one<SF_U8::type>(&sfTransactionResult, S
|
||||
SF_U8 const sfTickSize = make::one<SF_U8::type>(&sfTickSize, STI_UINT8, 16, "TickSize");
|
||||
|
||||
// 16-bit integers
|
||||
SF_U16 const sfLedgerEntryType = make::one<SF_U16::type>(&sfLedgerEntryType, STI_UINT16, 1, "LedgerEntryType", SField::sMD_Never);
|
||||
SF_U16 const sfTransactionType = make::one<SF_U16::type>(&sfTransactionType, STI_UINT16, 2, "TransactionType");
|
||||
SF_U16 const sfSignerWeight = make::one<SF_U16::type>(&sfSignerWeight, STI_UINT16, 3, "SignerWeight");
|
||||
SF_U16 const sfLedgerEntryType = make::one<SF_U16::type>(&sfLedgerEntryType, STI_UINT16, 1, "LedgerEntryType", SField::sMD_Never);
|
||||
SF_U16 const sfTransactionType = make::one<SF_U16::type>(&sfTransactionType, STI_UINT16, 2, "TransactionType");
|
||||
SF_U16 const sfSignerWeight = make::one<SF_U16::type>(&sfSignerWeight, STI_UINT16, 3, "SignerWeight");
|
||||
|
||||
// 16-bit integers (uncommon)
|
||||
SF_U16 const sfVersion = make::one<SF_U16::type>(&sfVersion, STI_UINT16, 16, "Version");
|
||||
|
||||
// 32-bit integers (common)
|
||||
SF_U32 const sfFlags = make::one<SF_U32::type>(&sfFlags, STI_UINT32, 2, "Flags");
|
||||
@@ -196,10 +199,10 @@ SF_Amount const sfDeliveredAmount = make::one<SF_Amount::type>(&sfDeliveredAmoun
|
||||
|
||||
// variable length (common)
|
||||
SF_Blob const sfPublicKey = make::one<SF_Blob::type>(&sfPublicKey, STI_VL, 1, "PublicKey");
|
||||
SF_Blob const sfSigningPubKey = make::one<SF_Blob::type>(&sfSigningPubKey, STI_VL, 3, "SigningPubKey");
|
||||
SF_Blob const sfSignature = make::one<SF_Blob::type>(&sfSignature, STI_VL, 6, "Signature", SField::sMD_Default, SField::notSigning);
|
||||
SF_Blob const sfMessageKey = make::one<SF_Blob::type>(&sfMessageKey, STI_VL, 2, "MessageKey");
|
||||
SF_Blob const sfSigningPubKey = make::one<SF_Blob::type>(&sfSigningPubKey, STI_VL, 3, "SigningPubKey");
|
||||
SF_Blob const sfTxnSignature = make::one<SF_Blob::type>(&sfTxnSignature, STI_VL, 4, "TxnSignature", SField::sMD_Default, SField::notSigning);
|
||||
SF_Blob const sfSignature = make::one<SF_Blob::type>(&sfSignature, STI_VL, 6, "Signature", SField::sMD_Default, SField::notSigning);
|
||||
SF_Blob const sfDomain = make::one<SF_Blob::type>(&sfDomain, STI_VL, 7, "Domain");
|
||||
SF_Blob const sfFundCode = make::one<SF_Blob::type>(&sfFundCode, STI_VL, 8, "FundCode");
|
||||
SF_Blob const sfRemoveCode = make::one<SF_Blob::type>(&sfRemoveCode, STI_VL, 9, "RemoveCode");
|
||||
@@ -215,7 +218,6 @@ SF_Blob const sfFulfillment = make::one<SF_Blob::type>(&sfFulfillment, S
|
||||
SF_Blob const sfCondition = make::one<SF_Blob::type>(&sfCondition, STI_VL, 17, "Condition");
|
||||
SF_Blob const sfMasterSignature = make::one<SF_Blob::type>(&sfMasterSignature, STI_VL, 18, "MasterSignature", SField::sMD_Default, SField::notSigning);
|
||||
|
||||
|
||||
// account
|
||||
SF_Account const sfAccount = make::one<SF_Account::type>(&sfAccount, STI_ACCOUNT, 1, "Account");
|
||||
SF_Account const sfOwner = make::one<SF_Account::type>(&sfOwner, STI_ACCOUNT, 2, "Owner");
|
||||
|
||||
@@ -186,10 +186,6 @@ Json::Value doLedgerEntry (RPC::Context& context)
|
||||
context.params[jss::escrow][jss::seq].asUInt()).key;
|
||||
}
|
||||
}
|
||||
else if (context.params.isMember (jss::generator))
|
||||
{
|
||||
jvResult[jss::error] = "deprecatedFeature";
|
||||
}
|
||||
else if (context.params.isMember (jss::offer))
|
||||
{
|
||||
expectedType = ltOFFER;
|
||||
|
||||
@@ -127,9 +127,58 @@ public:
|
||||
static_cast<char const*> (s.data()), s.size()));
|
||||
}
|
||||
|
||||
std::string
|
||||
makeRevocationString (
|
||||
SecretKey const& sk,
|
||||
KeyType type,
|
||||
bool invalidSig = false)
|
||||
{
|
||||
auto const pk = derivePublicKey(type, sk);
|
||||
|
||||
STObject st(sfGeneric);
|
||||
st[sfSequence] = std::numeric_limits<std::uint32_t>::max ();
|
||||
st[sfPublicKey] = pk;
|
||||
|
||||
sign(st, HashPrefix::manifest, type,
|
||||
invalidSig ? randomSecretKey() : sk, sfMasterSignature);
|
||||
BEAST_EXPECT(invalidSig ^ verify(
|
||||
st, HashPrefix::manifest, pk, sfMasterSignature));
|
||||
|
||||
Serializer s;
|
||||
st.add(s);
|
||||
|
||||
return base64_encode (std::string(
|
||||
static_cast<char const*> (s.data()), s.size()));
|
||||
}
|
||||
|
||||
Manifest
|
||||
make_Manifest
|
||||
(SecretKey const& sk, KeyType type, SecretKey const& ssk, KeyType stype,
|
||||
makeRevocation (
|
||||
SecretKey const& sk, KeyType type, bool invalidSig = false)
|
||||
{
|
||||
auto const pk = derivePublicKey(type, sk);
|
||||
|
||||
STObject st(sfGeneric);
|
||||
st[sfSequence] = std::numeric_limits<std::uint32_t>::max ();
|
||||
st[sfPublicKey] = pk;
|
||||
|
||||
sign(st, HashPrefix::manifest, type,
|
||||
invalidSig ? randomSecretKey() : sk, sfMasterSignature);
|
||||
BEAST_EXPECT(invalidSig ^ verify(
|
||||
st, HashPrefix::manifest, pk, sfMasterSignature));
|
||||
|
||||
Serializer s;
|
||||
st.add(s);
|
||||
|
||||
std::string const m (static_cast<char const*> (s.data()), s.size());
|
||||
if (auto r = deserializeManifest(std::move(m)))
|
||||
return std::move(*r);
|
||||
Throw<std::runtime_error> ("Could not create a revocation manifest");
|
||||
return *deserializeManifest(std::move(m)); // Silence compiler warning.
|
||||
}
|
||||
|
||||
Manifest
|
||||
makeManifest (
|
||||
SecretKey const& sk, KeyType type, SecretKey const& ssk, KeyType stype,
|
||||
int seq, bool invalidSig = false)
|
||||
{
|
||||
auto const pk = derivePublicKey(type, sk);
|
||||
@@ -152,38 +201,22 @@ public:
|
||||
st.add(s);
|
||||
|
||||
std::string const m (static_cast<char const*> (s.data()), s.size());
|
||||
if (auto r = Manifest::make_Manifest (std::move (m)))
|
||||
if (auto r = deserializeManifest(std::move(m)))
|
||||
return std::move (*r);
|
||||
Throw<std::runtime_error> ("Could not create a manifest");
|
||||
return *Manifest::make_Manifest(std::move(m)); // Silence compiler warning.
|
||||
}
|
||||
|
||||
std::string
|
||||
makeRevocation
|
||||
(SecretKey const& sk, KeyType type, bool invalidSig = false)
|
||||
{
|
||||
auto const pk = derivePublicKey(type, sk);
|
||||
|
||||
STObject st(sfGeneric);
|
||||
st[sfSequence] = std::numeric_limits<std::uint32_t>::max ();
|
||||
st[sfPublicKey] = pk;
|
||||
|
||||
sign(st, HashPrefix::manifest, type,
|
||||
invalidSig ? randomSecretKey() : sk, sfMasterSignature);
|
||||
BEAST_EXPECT(invalidSig ^ verify(
|
||||
st, HashPrefix::manifest, pk, sfMasterSignature));
|
||||
|
||||
Serializer s;
|
||||
st.add(s);
|
||||
|
||||
return base64_encode (std::string(
|
||||
static_cast<char const*> (s.data()), s.size()));
|
||||
return *deserializeManifest(std::move(m)); // Silence compiler warning.
|
||||
}
|
||||
|
||||
Manifest
|
||||
clone (Manifest const& m)
|
||||
{
|
||||
return Manifest (m.serialized, m.masterKey, m.signingKey, m.sequence);
|
||||
Manifest m2;
|
||||
m2.serialized = m.serialized;
|
||||
m2.masterKey = m.masterKey;
|
||||
m2.signingKey = m.signingKey;
|
||||
m2.sequence = m.sequence;
|
||||
m2.domain = m.domain;
|
||||
return m2;
|
||||
}
|
||||
|
||||
void testLoadStore (ManifestCache& m)
|
||||
@@ -197,24 +230,22 @@ public:
|
||||
DatabaseCon dbCon(setup, dbName, WalletDBInit, WalletDBCount);
|
||||
|
||||
auto getPopulatedManifests =
|
||||
[](ManifestCache const& cache) -> std::vector<Manifest const*>
|
||||
{
|
||||
std::vector<Manifest const*> result;
|
||||
result.reserve (32);
|
||||
cache.for_each_manifest (
|
||||
[&result](Manifest const& m)
|
||||
{result.push_back (&m);});
|
||||
return result;
|
||||
};
|
||||
[](ManifestCache const& cache) -> std::vector<Manifest const*>
|
||||
{
|
||||
std::vector<Manifest const*> result;
|
||||
result.reserve (32);
|
||||
cache.for_each_manifest (
|
||||
[&result](Manifest const& m) {result.push_back (&m);});
|
||||
return result;
|
||||
};
|
||||
auto sort =
|
||||
[](std::vector<Manifest const*> mv) -> std::vector<Manifest const*>
|
||||
{
|
||||
std::sort (mv.begin (),
|
||||
mv.end (),
|
||||
[](Manifest const* lhs, Manifest const* rhs)
|
||||
{return lhs->serialized < rhs->serialized;});
|
||||
return mv;
|
||||
};
|
||||
[](std::vector<Manifest const*> mv) -> std::vector<Manifest const*>
|
||||
{
|
||||
std::sort (mv.begin (), mv.end (),
|
||||
[](Manifest const* lhs, Manifest const* rhs)
|
||||
{ return lhs->serialized < rhs->serialized; });
|
||||
return mv;
|
||||
};
|
||||
std::vector<Manifest const*> const inManifests (
|
||||
sort (getPopulatedManifests (m)));
|
||||
|
||||
@@ -318,13 +349,13 @@ public:
|
||||
BEAST_EXPECT(! loaded.revoked(pk));
|
||||
|
||||
std::vector<std::string> const badSigRevocation =
|
||||
{ makeRevocation (sk, keyType, true /* invalidSig */) };
|
||||
{ makeRevocationString (sk, keyType, true) };
|
||||
BEAST_EXPECT(! loaded.load (
|
||||
dbCon, "ValidatorManifests", emptyManifest, badSigRevocation));
|
||||
BEAST_EXPECT(! loaded.revoked(pk));
|
||||
|
||||
std::vector<std::string> const cfgRevocation =
|
||||
{ makeRevocation (sk, keyType) };
|
||||
{ makeRevocationString (sk, keyType) };
|
||||
BEAST_EXPECT(loaded.load (
|
||||
dbCon, "ValidatorManifests", emptyManifest, cfgRevocation));
|
||||
|
||||
@@ -341,7 +372,7 @@ public:
|
||||
auto const sk = randomSecretKey();
|
||||
auto const pk = derivePublicKey(KeyType::ed25519, sk);
|
||||
auto const kp = randomKeyPair(KeyType::secp256k1);
|
||||
auto const m = make_Manifest (
|
||||
auto const m = makeManifest (
|
||||
sk, KeyType::ed25519, kp.second, KeyType::secp256k1, 0);
|
||||
|
||||
STObject st(sfGeneric);
|
||||
@@ -374,10 +405,9 @@ public:
|
||||
// getMasterKey should return the listed validator master key
|
||||
// for that ephemeral public key
|
||||
auto const kp0 = randomKeyPair(KeyType::secp256k1);
|
||||
auto const m0 = make_Manifest (
|
||||
sk, KeyType::ed25519, kp0.second, KeyType::secp256k1, 0);
|
||||
BEAST_EXPECT(cache.applyManifest(clone (m0)) ==
|
||||
ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(ManifestDisposition::accepted ==
|
||||
cache.applyManifest(makeManifest (
|
||||
sk, KeyType::ed25519, kp0.second, KeyType::secp256k1, 0)));
|
||||
BEAST_EXPECT(cache.getSigningKey(pk) == kp0.first);
|
||||
BEAST_EXPECT(cache.getMasterKey(kp0.first) == pk);
|
||||
|
||||
@@ -386,10 +416,9 @@ public:
|
||||
// getMasterKey should only return a master key for the latest
|
||||
// ephemeral public key
|
||||
auto const kp1 = randomKeyPair(KeyType::secp256k1);
|
||||
auto const m1 = make_Manifest (
|
||||
sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 1);
|
||||
BEAST_EXPECT(cache.applyManifest(clone (m1)) ==
|
||||
ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(ManifestDisposition::accepted ==
|
||||
cache.applyManifest(makeManifest (
|
||||
sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 1)));
|
||||
BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
|
||||
BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
|
||||
BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
|
||||
@@ -397,27 +426,22 @@ public:
|
||||
// getSigningKey and getMasterKey should return the same keys if
|
||||
// a new manifest is applied with the same signing key but a higher
|
||||
// sequence
|
||||
auto const m2 = make_Manifest (
|
||||
sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 2);
|
||||
BEAST_EXPECT(cache.applyManifest(clone (m2)) ==
|
||||
ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(ManifestDisposition::accepted ==
|
||||
cache.applyManifest(makeManifest (
|
||||
sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 2)));
|
||||
BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
|
||||
BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
|
||||
BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
|
||||
|
||||
// getSigningKey should return boost::none for a
|
||||
// revoked master public key
|
||||
// getSigningKey should return boost::none for a revoked master public key
|
||||
// getMasterKey should return boost::none for an ephemeral public key
|
||||
// from a revoked master public key
|
||||
auto const kpMax = randomKeyPair(KeyType::secp256k1);
|
||||
auto const mMax = make_Manifest (
|
||||
sk, KeyType::ed25519, kpMax.second, KeyType::secp256k1,
|
||||
std::numeric_limits<std::uint32_t>::max ());
|
||||
BEAST_EXPECT(cache.applyManifest(clone (mMax)) ==
|
||||
ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(ManifestDisposition::accepted ==
|
||||
cache.applyManifest(makeRevocation (
|
||||
sk, KeyType::ed25519)));
|
||||
BEAST_EXPECT(cache.revoked(pk));
|
||||
BEAST_EXPECT(cache.getSigningKey(pk) == pk);
|
||||
BEAST_EXPECT(cache.getMasterKey(kpMax.first) == kpMax.first);
|
||||
BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
|
||||
BEAST_EXPECT(cache.getMasterKey(kp1.first) == kp1.first);
|
||||
}
|
||||
|
||||
@@ -460,10 +484,45 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testMakeManifest()
|
||||
void testManifestVersioning()
|
||||
{
|
||||
testcase ("make_Manifest");
|
||||
testcase ("Versioning");
|
||||
|
||||
auto const sk = generateSecretKey (KeyType::ed25519, randomSeed ());
|
||||
auto const pk = derivePublicKey(KeyType::ed25519, sk);
|
||||
|
||||
auto const ssk = generateSecretKey (KeyType::secp256k1, randomSeed ());
|
||||
auto const spk = derivePublicKey(KeyType::secp256k1, ssk);
|
||||
|
||||
auto buildManifestObject = [&](std::uint16_t version)
|
||||
{
|
||||
STObject st(sfGeneric);
|
||||
st[sfSequence] = 3;
|
||||
st[sfPublicKey] = pk;
|
||||
st[sfSigningPubKey] = spk;
|
||||
|
||||
if (version != 0)
|
||||
st[sfVersion] = version;
|
||||
|
||||
sign(st, HashPrefix::manifest, KeyType::ed25519, sk, sfMasterSignature);
|
||||
sign(st, HashPrefix::manifest, KeyType::secp256k1, ssk);
|
||||
|
||||
Serializer s;
|
||||
st.add(s);
|
||||
|
||||
return std::string (static_cast<char const*>(s.data()), s.size());
|
||||
};
|
||||
|
||||
// We understand version 0 manifests:
|
||||
BEAST_EXPECT(deserializeManifest(buildManifestObject(0)));
|
||||
|
||||
// We don't understand any other versions:
|
||||
BEAST_EXPECT(!deserializeManifest(buildManifestObject(1)));
|
||||
BEAST_EXPECT(!deserializeManifest(buildManifestObject(2001)));
|
||||
}
|
||||
|
||||
void testManifestDeserialization()
|
||||
{
|
||||
std::array<KeyType, 2> const keyTypes {{
|
||||
KeyType::ed25519,
|
||||
KeyType::secp256k1 }};
|
||||
@@ -497,7 +556,8 @@ public:
|
||||
auto const spk = derivePublicKey(sKeyType, ssk);
|
||||
|
||||
auto buildManifestObject = [&](
|
||||
std::uint32_t const& seq,
|
||||
std::uint32_t seq,
|
||||
boost::optional<std::string> domain,
|
||||
bool noSigningPublic = false,
|
||||
bool noSignature = false)
|
||||
{
|
||||
@@ -505,11 +565,13 @@ public:
|
||||
st[sfSequence] = seq;
|
||||
st[sfPublicKey] = pk;
|
||||
|
||||
if (domain)
|
||||
st[sfDomain] = makeSlice(*domain);
|
||||
|
||||
if (! noSigningPublic)
|
||||
st[sfSigningPubKey] = spk;
|
||||
|
||||
sign(st, HashPrefix::manifest, keyType, sk,
|
||||
sfMasterSignature);
|
||||
sign(st, HashPrefix::manifest, keyType, sk, sfMasterSignature);
|
||||
|
||||
if (! noSignature)
|
||||
sign(st, HashPrefix::manifest, sKeyType, ssk);
|
||||
@@ -517,201 +579,329 @@ public:
|
||||
return st;
|
||||
};
|
||||
|
||||
auto const st = buildManifestObject(++sequence);
|
||||
{
|
||||
testcase << "deserializeManifest: normal manifest (" <<
|
||||
to_string(keyType) << " + " <<
|
||||
to_string(sKeyType) << ")";
|
||||
|
||||
{ // valid manifest without domain
|
||||
auto const st = buildManifestObject(
|
||||
++sequence, boost::none);
|
||||
|
||||
auto const m = toString(st);
|
||||
auto const manifest = deserializeManifest(m);
|
||||
|
||||
BEAST_EXPECT(manifest);
|
||||
BEAST_EXPECT(manifest->masterKey == pk);
|
||||
BEAST_EXPECT(manifest->signingKey == spk);
|
||||
BEAST_EXPECT(manifest->sequence == sequence);
|
||||
BEAST_EXPECT(manifest->serialized == m);
|
||||
BEAST_EXPECT(manifest->domain.empty());
|
||||
BEAST_EXPECT(manifest->verify());
|
||||
}
|
||||
|
||||
{ // invalid manifest (empty domain)
|
||||
auto const st = buildManifestObject(
|
||||
++sequence, std::string{});
|
||||
|
||||
BEAST_EXPECT(!deserializeManifest(toString(st)));
|
||||
}
|
||||
|
||||
{ // invalid manifest (domain too short)
|
||||
auto const st = buildManifestObject(
|
||||
++sequence, std::string{"a.b"});
|
||||
BEAST_EXPECT(!deserializeManifest(toString(st)));
|
||||
}
|
||||
{ // invalid manifest (domain too long)
|
||||
std::string s(254, 'a');
|
||||
auto const st = buildManifestObject(
|
||||
++sequence, s + ".example.com");
|
||||
BEAST_EXPECT(!deserializeManifest(toString(st)));
|
||||
}
|
||||
{ // invalid manifest (domain component too long)
|
||||
std::string s(72, 'a');
|
||||
auto const st = buildManifestObject(
|
||||
++sequence, s + ".example.com");
|
||||
BEAST_EXPECT(!deserializeManifest(toString(st)));
|
||||
}
|
||||
|
||||
auto const st = buildManifestObject(
|
||||
++sequence, std::string{"example.com"});
|
||||
|
||||
{
|
||||
// valid manifest with domain
|
||||
auto const m = toString(st);
|
||||
auto const manifest = deserializeManifest(m);
|
||||
|
||||
BEAST_EXPECT(manifest);
|
||||
BEAST_EXPECT(manifest->masterKey == pk);
|
||||
BEAST_EXPECT(manifest->signingKey == spk);
|
||||
BEAST_EXPECT(manifest->sequence == sequence);
|
||||
BEAST_EXPECT(manifest->serialized == m);
|
||||
BEAST_EXPECT(manifest->domain == "example.com");
|
||||
BEAST_EXPECT(manifest->verify());
|
||||
}
|
||||
{
|
||||
// valid manifest with invalid signature
|
||||
auto badSigSt = st;
|
||||
badSigSt[sfPublicKey] = badSigSt[sfSigningPubKey];
|
||||
|
||||
auto const m = toString(badSigSt);
|
||||
auto const manifest = deserializeManifest(m);
|
||||
|
||||
BEAST_EXPECT(manifest);
|
||||
BEAST_EXPECT(manifest->masterKey == spk);
|
||||
BEAST_EXPECT(manifest->signingKey == spk);
|
||||
BEAST_EXPECT(manifest->sequence == sequence);
|
||||
BEAST_EXPECT(manifest->serialized == m);
|
||||
BEAST_EXPECT(manifest->domain == "example.com");
|
||||
BEAST_EXPECT(!manifest->verify());
|
||||
}
|
||||
{
|
||||
// reject missing sequence
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfSequence));
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject missing public key
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfPublicKey));
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject invalid public key type
|
||||
auto badSt = st;
|
||||
badSt[sfPublicKey] = badKey;
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject short public key
|
||||
auto badSt = st;
|
||||
badSt[sfPublicKey] = shortKey;
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject missing signing public key
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfSigningPubKey));
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject invalid signing public key type
|
||||
auto badSt = st;
|
||||
badSt[sfSigningPubKey] = badKey;
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject short signing public key
|
||||
auto badSt = st;
|
||||
badSt[sfSigningPubKey] = shortKey;
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject missing signature
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfMasterSignature));
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject missing signing key signature
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfSignature));
|
||||
BEAST_EXPECT(!deserializeManifest(toString(badSt)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// valid manifest
|
||||
auto const m = toString(st);
|
||||
testcase << "deserializeManifest: revocation manifest (" <<
|
||||
to_string(keyType) << " + " <<
|
||||
to_string(sKeyType) << ")";
|
||||
|
||||
auto const manifest = Manifest::make_Manifest (m);
|
||||
// valid revocation
|
||||
{
|
||||
auto const st = buildManifestObject(
|
||||
std::numeric_limits<std::uint32_t>::max(),
|
||||
boost::none, true, true);
|
||||
|
||||
BEAST_EXPECT(manifest);
|
||||
BEAST_EXPECT(manifest->masterKey == pk);
|
||||
BEAST_EXPECT(manifest->signingKey == spk);
|
||||
BEAST_EXPECT(manifest->sequence == sequence);
|
||||
BEAST_EXPECT(manifest->serialized == m);
|
||||
BEAST_EXPECT(manifest->verify());
|
||||
}
|
||||
{
|
||||
// valid manifest with invalid signature
|
||||
auto badSigSt = st;
|
||||
badSigSt[sfPublicKey] = badSigSt[sfSigningPubKey];
|
||||
auto const m = toString(st);
|
||||
auto const manifest = deserializeManifest(m);
|
||||
|
||||
auto const m = toString(badSigSt);
|
||||
auto const manifest = Manifest::make_Manifest (m);
|
||||
BEAST_EXPECT(manifest);
|
||||
BEAST_EXPECT(manifest->masterKey == pk);
|
||||
BEAST_EXPECT(manifest->signingKey == PublicKey());
|
||||
BEAST_EXPECT(manifest->revoked());
|
||||
BEAST_EXPECT(manifest->domain.empty());
|
||||
BEAST_EXPECT(manifest->serialized == m);
|
||||
BEAST_EXPECT(manifest->verify());
|
||||
}
|
||||
|
||||
BEAST_EXPECT(manifest);
|
||||
BEAST_EXPECT(manifest->masterKey == spk);
|
||||
BEAST_EXPECT(manifest->signingKey == spk);
|
||||
BEAST_EXPECT(manifest->sequence == sequence);
|
||||
BEAST_EXPECT(manifest->serialized == m);
|
||||
BEAST_EXPECT(! manifest->verify());
|
||||
}
|
||||
{
|
||||
// reject missing sequence
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfSequence));
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject missing public key
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfPublicKey));
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject invalid public key type
|
||||
auto badSt = st;
|
||||
badSt[sfPublicKey] = badKey;
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject short public key
|
||||
auto badSt = st;
|
||||
badSt[sfPublicKey] = shortKey;
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject missing signing public key
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfSigningPubKey));
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject invalid signing public key type
|
||||
auto badSt = st;
|
||||
badSt[sfSigningPubKey] = badKey;
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject short signing public key
|
||||
auto badSt = st;
|
||||
badSt[sfSigningPubKey] = shortKey;
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject missing signature
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfMasterSignature));
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{
|
||||
// reject missing signing key signature
|
||||
auto badSt = st;
|
||||
BEAST_EXPECT(badSt.delField(sfSignature));
|
||||
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
||||
}
|
||||
{ // can't specify an ephemeral signing key
|
||||
auto const st = buildManifestObject(
|
||||
std::numeric_limits<std::uint32_t>::max(),
|
||||
boost::none, true, false);
|
||||
|
||||
// test revocations (max sequence revoking the master key)
|
||||
auto testRevocation = [&](STObject const& st)
|
||||
{
|
||||
auto const m = toString(st);
|
||||
auto const manifest = Manifest::make_Manifest (m);
|
||||
BEAST_EXPECT(!deserializeManifest(toString(st)));
|
||||
}
|
||||
{ // can't specify an ephemeral signature
|
||||
auto const st = buildManifestObject(
|
||||
std::numeric_limits<std::uint32_t>::max(),
|
||||
boost::none, false, true);
|
||||
|
||||
BEAST_EXPECT(manifest);
|
||||
BEAST_EXPECT(manifest->masterKey == pk);
|
||||
BEAST_EXPECT(manifest->signingKey == PublicKey());
|
||||
BEAST_EXPECT(manifest->sequence ==
|
||||
std::numeric_limits<std::uint32_t>::max ());
|
||||
BEAST_EXPECT(manifest->serialized == m);
|
||||
BEAST_EXPECT(manifest->verify());
|
||||
};
|
||||
BEAST_EXPECT(!deserializeManifest(toString(st)));
|
||||
}
|
||||
{ // can't specify an ephemeral key & signature
|
||||
auto const st = buildManifestObject(
|
||||
std::numeric_limits<std::uint32_t>::max (),
|
||||
boost::none, false, false);
|
||||
|
||||
// valid revocation
|
||||
{
|
||||
auto const revSt = buildManifestObject(
|
||||
std::numeric_limits<std::uint32_t>::max ());
|
||||
testRevocation(revSt);
|
||||
}
|
||||
|
||||
// signing key and signature are optional in revocation
|
||||
{
|
||||
auto const revSt = buildManifestObject(
|
||||
std::numeric_limits<std::uint32_t>::max (),
|
||||
true /* no signing key */);
|
||||
testRevocation(revSt);
|
||||
}
|
||||
{
|
||||
auto const revSt = buildManifestObject(
|
||||
std::numeric_limits<std::uint32_t>::max (),
|
||||
false, true /* no signature */);
|
||||
testRevocation(revSt);
|
||||
|
||||
}
|
||||
{
|
||||
auto const revSt = buildManifestObject(
|
||||
std::numeric_limits<std::uint32_t>::max (),
|
||||
true /* no signing key */,
|
||||
true /* no signature */);
|
||||
testRevocation(revSt);
|
||||
BEAST_EXPECT(!deserializeManifest(toString(st)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testManifestDomainNames()
|
||||
{
|
||||
testcase ("Manifest Domain Names");
|
||||
|
||||
auto const sk1 = generateSecretKey (KeyType::secp256k1, randomSeed());
|
||||
auto const pk1 = derivePublicKey(KeyType::secp256k1, sk1);
|
||||
|
||||
auto const sk2 = generateSecretKey (KeyType::secp256k1, randomSeed());
|
||||
auto const pk2 = derivePublicKey(KeyType::secp256k1, sk2);
|
||||
|
||||
auto test = [&](std::string domain)
|
||||
{
|
||||
STObject st(sfGeneric);
|
||||
st[sfSequence] = 7;
|
||||
st[sfPublicKey] = pk1;
|
||||
st[sfDomain] = makeSlice(domain);
|
||||
st[sfSigningPubKey] = pk2;
|
||||
|
||||
sign(st, HashPrefix::manifest, KeyType::secp256k1, sk1, sfMasterSignature);
|
||||
sign(st, HashPrefix::manifest, KeyType::secp256k1, sk2);
|
||||
|
||||
Serializer s;
|
||||
st.add(s);
|
||||
|
||||
return deserializeManifest(
|
||||
std::string(static_cast<char const*> (s.data()), s.size()));
|
||||
};
|
||||
|
||||
BEAST_EXPECT(test("example.com"));
|
||||
BEAST_EXPECT(test("test.example.com"));
|
||||
BEAST_EXPECT(test("example-domain.com"));
|
||||
BEAST_EXPECT(test("xn--mxavchb.gr"));
|
||||
BEAST_EXPECT(test("test.xn--mxavchb.gr"));
|
||||
BEAST_EXPECT(test("123.gr"));
|
||||
BEAST_EXPECT(test("x.yz"));
|
||||
BEAST_EXPECT(test(std::string(63, 'a') + ".example.com"));
|
||||
BEAST_EXPECT(test(std::string(63, 'a') + "." + std::string(63, 'b')));
|
||||
|
||||
|
||||
// No period
|
||||
BEAST_EXPECT(!test("example"));
|
||||
|
||||
// Leading period:
|
||||
BEAST_EXPECT(!test(".com"));
|
||||
BEAST_EXPECT(!test(".example.com"));
|
||||
|
||||
// A trailing period is technically valid but we don't allow it
|
||||
BEAST_EXPECT(!test("example.com."));
|
||||
|
||||
// A component can't start or end with a dash
|
||||
BEAST_EXPECT(!test("-example.com"));
|
||||
BEAST_EXPECT(!test("example-.com"));
|
||||
|
||||
// Empty component:
|
||||
BEAST_EXPECT(!test("double..periods.example.com"));
|
||||
|
||||
// TLD too short or too long:
|
||||
BEAST_EXPECT(!test("example.x"));
|
||||
BEAST_EXPECT(!test("example." + std::string(64, 'a')));
|
||||
|
||||
// Invalid characters:
|
||||
BEAST_EXPECT(!test("example.com-org"));
|
||||
BEAST_EXPECT(!test("bang!.com"));
|
||||
BEAST_EXPECT(!test("bang!.example.com"));
|
||||
|
||||
// Too short
|
||||
BEAST_EXPECT(!test("a.b"));
|
||||
|
||||
// Single component too long:
|
||||
BEAST_EXPECT(!test(std::string(64, 'a') + ".com"));
|
||||
BEAST_EXPECT(!test(std::string(64, 'a') + ".example.com"));
|
||||
|
||||
// Multiple components too long:
|
||||
BEAST_EXPECT(!test(std::string(64, 'a') + "." + std::string(64, 'b')));
|
||||
BEAST_EXPECT(!test(std::string(64, 'a') + "." + std::string(64, 'b')));
|
||||
|
||||
// Overall too long:
|
||||
BEAST_EXPECT(!test(std::string(63, 'a') + "." + std::string(63, 'b') + ".example.com"));
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
ManifestCache cache;
|
||||
{
|
||||
testcase ("apply");
|
||||
auto const accepted = ManifestDisposition::accepted;
|
||||
auto const stale = ManifestDisposition::stale;
|
||||
auto const invalid = ManifestDisposition::invalid;
|
||||
|
||||
auto const sk_a = randomSecretKey();
|
||||
auto const pk_a = derivePublicKey(KeyType::ed25519, sk_a);
|
||||
auto const kp_a = randomKeyPair(KeyType::secp256k1);
|
||||
auto const s_a0 = make_Manifest (
|
||||
auto const s_a0 = makeManifest (
|
||||
sk_a, KeyType::ed25519, kp_a.second, KeyType::secp256k1, 0);
|
||||
auto const s_a1 = make_Manifest (
|
||||
auto const s_a1 = makeManifest (
|
||||
sk_a, KeyType::ed25519, kp_a.second, KeyType::secp256k1, 1);
|
||||
auto const s_aMax = make_Manifest (
|
||||
sk_a, KeyType::ed25519, kp_a.second, KeyType::secp256k1,
|
||||
std::numeric_limits<std::uint32_t>::max ());
|
||||
auto const s_aMax = makeRevocation (sk_a, KeyType::ed25519);
|
||||
|
||||
auto const sk_b = randomSecretKey();
|
||||
auto const kp_b = randomKeyPair(KeyType::secp256k1);
|
||||
auto const s_b0 = make_Manifest (
|
||||
auto const s_b0 = makeManifest (
|
||||
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 0);
|
||||
auto const s_b1 = make_Manifest (
|
||||
auto const s_b1 = makeManifest (
|
||||
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 1);
|
||||
auto const s_b2 = make_Manifest (
|
||||
auto const s_b2 = makeManifest (
|
||||
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 2,
|
||||
true); // invalidSig
|
||||
auto const fake = s_b1.serialized + '\0';
|
||||
|
||||
// applyManifest should accept new manifests with
|
||||
// higher sequence numbers
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == accepted);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == ManifestDisposition::stale);
|
||||
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == accepted);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == ManifestDisposition::stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == ManifestDisposition::stale);
|
||||
|
||||
// applyManifest should accept manifests with max sequence numbers
|
||||
// that revoke the master public key
|
||||
BEAST_EXPECT(!cache.revoked (pk_a));
|
||||
BEAST_EXPECT(s_aMax.revoked ());
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_aMax)) == accepted);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_aMax)) == stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_aMax)) == ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_aMax)) == ManifestDisposition::stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == ManifestDisposition::stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == ManifestDisposition::stale);
|
||||
BEAST_EXPECT(cache.revoked (pk_a));
|
||||
|
||||
// applyManifest should reject manifests with invalid signatures
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_b0)) == accepted);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_b0)) == stale);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_b0)) == ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_b0)) == ManifestDisposition::stale);
|
||||
|
||||
BEAST_EXPECT(!Manifest::make_Manifest(fake));
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_b2)) == invalid);
|
||||
BEAST_EXPECT(!deserializeManifest(fake));
|
||||
BEAST_EXPECT(cache.applyManifest (clone (s_b2)) == ManifestDisposition::invalid);
|
||||
}
|
||||
testLoadStore (cache);
|
||||
testGetSignature ();
|
||||
testGetKeys ();
|
||||
testValidatorToken ();
|
||||
testMakeManifest ();
|
||||
testManifestDeserialization ();
|
||||
testManifestDomainNames ();
|
||||
testManifestVersioning ();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -91,8 +91,7 @@ public:
|
||||
auto const tokenPublicKey =
|
||||
derivePublicKey(KeyType::secp256k1, tokenSecretKey);
|
||||
|
||||
auto const m = Manifest::make_Manifest(
|
||||
base64_decode(tokenManifest));
|
||||
auto const m = deserializeManifest(base64_decode(tokenManifest));
|
||||
BEAST_EXPECT(m);
|
||||
NodeID const tokenNodeID = calcNodeID(m->masterKey);
|
||||
|
||||
|
||||
@@ -68,11 +68,32 @@ private:
|
||||
STObject st(sfGeneric);
|
||||
st[sfSequence] = seq;
|
||||
st[sfPublicKey] = pk;
|
||||
st[sfSigningPubKey] = spk;
|
||||
|
||||
sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk);
|
||||
sign(st, HashPrefix::manifest, *publicKeyType(pk), sk,
|
||||
sfMasterSignature);
|
||||
if (seq != std::numeric_limits<std::uint32_t>::max())
|
||||
{
|
||||
st[sfSigningPubKey] = spk;
|
||||
sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk);
|
||||
}
|
||||
|
||||
sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature);
|
||||
|
||||
Serializer s;
|
||||
st.add(s);
|
||||
|
||||
return std::string(static_cast<char const*> (s.data()), s.size());
|
||||
}
|
||||
|
||||
static
|
||||
std::string
|
||||
makeRevocationString (
|
||||
PublicKey const& pk,
|
||||
SecretKey const& sk)
|
||||
{
|
||||
STObject st(sfGeneric);
|
||||
st[sfSequence] = std::numeric_limits<std::uint32_t>::max();
|
||||
st[sfPublicKey] = pk;
|
||||
|
||||
sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature);
|
||||
|
||||
Serializer s;
|
||||
st.add(s);
|
||||
@@ -162,8 +183,8 @@ private:
|
||||
|
||||
jtx::Env env (*this);
|
||||
PublicKey emptyLocalKey;
|
||||
std::vector<std::string> emptyCfgKeys;
|
||||
std::vector<std::string> emptyCfgPublishers;
|
||||
std::vector<std::string> const emptyCfgKeys;
|
||||
std::vector<std::string> const emptyCfgPublishers;
|
||||
|
||||
auto const localSigningKeys = randomKeyPair(KeyType::secp256k1);
|
||||
auto const localSigningPublic = localSigningKeys.first;
|
||||
@@ -220,7 +241,7 @@ private:
|
||||
localSigningPublic, emptyCfgKeys, emptyCfgPublishers));
|
||||
BEAST_EXPECT(trustedKeys->listed (localSigningPublic));
|
||||
|
||||
manifests.applyManifest (*Manifest::make_Manifest(cfgManifest));
|
||||
manifests.applyManifest (*deserializeManifest(cfgManifest));
|
||||
BEAST_EXPECT(trustedKeys->load (
|
||||
localSigningPublic, emptyCfgKeys, emptyCfgPublishers));
|
||||
|
||||
@@ -253,23 +274,16 @@ private:
|
||||
BEAST_EXPECT(trustedKeys->listed (masterNode2));
|
||||
|
||||
// load should reject invalid config keys
|
||||
std::vector<std::string> badKeys({"NotAPublicKey"});
|
||||
BEAST_EXPECT(!trustedKeys->load (
|
||||
emptyLocalKey, badKeys, emptyCfgPublishers));
|
||||
|
||||
badKeys[0] = format (randomNode(), "!");
|
||||
BEAST_EXPECT(!trustedKeys->load (
|
||||
emptyLocalKey, badKeys, emptyCfgPublishers));
|
||||
|
||||
badKeys[0] = format (randomNode(), "! Comment");
|
||||
BEAST_EXPECT(!trustedKeys->load (
|
||||
emptyLocalKey, badKeys, emptyCfgPublishers));
|
||||
BEAST_EXPECT(!trustedKeys->load (emptyLocalKey,
|
||||
{ "NotAPublicKey" }, emptyCfgPublishers));
|
||||
BEAST_EXPECT(!trustedKeys->load (emptyLocalKey,
|
||||
{ format (randomNode(), "!") }, emptyCfgPublishers));
|
||||
|
||||
// load terminates when encountering an invalid entry
|
||||
auto const goodKey = randomNode();
|
||||
badKeys.push_back (format (goodKey));
|
||||
BEAST_EXPECT(!trustedKeys->load (
|
||||
emptyLocalKey, badKeys, emptyCfgPublishers));
|
||||
BEAST_EXPECT(!trustedKeys->load (emptyLocalKey,
|
||||
{ format (randomNode(), "!"), format (goodKey) },
|
||||
emptyCfgPublishers));
|
||||
BEAST_EXPECT(!trustedKeys->listed (goodKey));
|
||||
}
|
||||
{
|
||||
@@ -310,7 +324,7 @@ private:
|
||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||
manifests, manifests, env.timeKeeper(), env.journal);
|
||||
|
||||
manifests.applyManifest (*Manifest::make_Manifest(cfgManifest));
|
||||
manifests.applyManifest (*deserializeManifest(cfgManifest));
|
||||
|
||||
BEAST_EXPECT(trustedKeys->load (
|
||||
localSigningPublic, cfgKeys, emptyCfgPublishers));
|
||||
@@ -369,7 +383,7 @@ private:
|
||||
auto const pubRevokedSigning = randomKeyPair(KeyType::secp256k1);
|
||||
// make this manifest revoked (seq num = max)
|
||||
// -- thus should not be loaded
|
||||
pubManifests.applyManifest (*Manifest::make_Manifest (
|
||||
pubManifests.applyManifest (*deserializeManifest (
|
||||
makeManifestString (
|
||||
pubRevokedPublic,
|
||||
pubRevokedSecret,
|
||||
@@ -530,10 +544,8 @@ private:
|
||||
// do not apply list with revoked publisher key
|
||||
// applied list is removed due to revoked publisher key
|
||||
auto const signingKeysMax = randomKeyPair(KeyType::secp256k1);
|
||||
auto maxManifest = base64_encode(makeManifestString (
|
||||
publisherPublic, publisherSecret,
|
||||
pubSigningKeys2.first, pubSigningKeys2.second,
|
||||
std::numeric_limits<std::uint32_t>::max ()));
|
||||
auto maxManifest = base64_encode(makeRevocationString (
|
||||
publisherPublic, publisherSecret));
|
||||
|
||||
auto const sequence5 = 5;
|
||||
auto const blob5 = makeList (
|
||||
@@ -647,7 +659,7 @@ private:
|
||||
BEAST_EXPECT(!trustedKeys->trusted (signingPublic1));
|
||||
|
||||
// Should trust the ephemeral signing key from the applied manifest
|
||||
auto m1 = Manifest::make_Manifest (makeManifestString (
|
||||
auto m1 = deserializeManifest(makeManifestString(
|
||||
masterPublic, masterPrivate,
|
||||
signingPublic1, signingKeys1.second, 1));
|
||||
|
||||
@@ -663,7 +675,7 @@ private:
|
||||
// from the newest applied manifest
|
||||
auto const signingKeys2 = randomKeyPair(KeyType::secp256k1);
|
||||
auto const signingPublic2 = signingKeys2.first;
|
||||
auto m2 = Manifest::make_Manifest (makeManifestString (
|
||||
auto m2 = deserializeManifest(makeManifestString(
|
||||
masterPublic, masterPrivate,
|
||||
signingPublic2, signingKeys2.second, 2));
|
||||
BEAST_EXPECT(
|
||||
@@ -680,10 +692,8 @@ private:
|
||||
auto const signingKeysMax = randomKeyPair(KeyType::secp256k1);
|
||||
auto const signingPublicMax = signingKeysMax.first;
|
||||
activeValidators.emplace (calcNodeID(signingPublicMax));
|
||||
auto mMax = Manifest::make_Manifest (makeManifestString (
|
||||
masterPublic, masterPrivate,
|
||||
signingPublicMax, signingKeysMax.second,
|
||||
std::numeric_limits<std::uint32_t>::max ()));
|
||||
auto mMax = deserializeManifest(makeRevocationString(
|
||||
masterPublic, masterPrivate));
|
||||
|
||||
BEAST_EXPECT(mMax->revoked ());
|
||||
BEAST_EXPECT(
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/app/misc/Manifest.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
@@ -744,21 +746,6 @@ class LedgerRPC_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void testLedgerEntryGenerator()
|
||||
{
|
||||
testcase ("ledger_entry Request Generator");
|
||||
using namespace test::jtx;
|
||||
Env env {*this};
|
||||
|
||||
// All generator requests are deprecated.
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::generator] = 5;
|
||||
jvParams[jss::ledger_hash] = to_string (env.closed()->info().hash);
|
||||
Json::Value const jrr = env.rpc (
|
||||
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||
checkErrorValue (jrr, "deprecatedFeature", "");
|
||||
}
|
||||
|
||||
void testLedgerEntryOffer()
|
||||
{
|
||||
testcase ("ledger_entry Request Offer");
|
||||
@@ -1516,7 +1503,6 @@ public:
|
||||
testLedgerEntryDepositPreauth();
|
||||
testLedgerEntryDirectory();
|
||||
testLedgerEntryEscrow();
|
||||
testLedgerEntryGenerator();
|
||||
testLedgerEntryOffer();
|
||||
testLedgerEntryPayChan();
|
||||
testLedgerEntryRippleState();
|
||||
|
||||
Reference in New Issue
Block a user