mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-21 02:55:50 +00:00
Introduce the ExpandedSignerList amendment:
The amendment increases the maximum sign of an account's signer list from 8 to 32. Like all new features, the associated amendment is configured with a default vote of "no" and server operators will have to vote for it explicitly if they believe it is useful.
This commit is contained in:
committed by
manojsdoshi
parent
04bd5878f1
commit
01c37fed69
@@ -82,6 +82,7 @@ target_sources (xrpl_core PRIVATE
|
|||||||
src/ripple/protocol/impl/PublicKey.cpp
|
src/ripple/protocol/impl/PublicKey.cpp
|
||||||
src/ripple/protocol/impl/Quality.cpp
|
src/ripple/protocol/impl/Quality.cpp
|
||||||
src/ripple/protocol/impl/Rate2.cpp
|
src/ripple/protocol/impl/Rate2.cpp
|
||||||
|
src/ripple/protocol/impl/Rules.cpp
|
||||||
src/ripple/protocol/impl/SField.cpp
|
src/ripple/protocol/impl/SField.cpp
|
||||||
src/ripple/protocol/impl/SOTemplate.cpp
|
src/ripple/protocol/impl/SOTemplate.cpp
|
||||||
src/ripple/protocol/impl/STAccount.cpp
|
src/ripple/protocol/impl/STAccount.cpp
|
||||||
@@ -209,6 +210,7 @@ install (
|
|||||||
src/ripple/protocol/PublicKey.h
|
src/ripple/protocol/PublicKey.h
|
||||||
src/ripple/protocol/Quality.h
|
src/ripple/protocol/Quality.h
|
||||||
src/ripple/protocol/Rate.h
|
src/ripple/protocol/Rate.h
|
||||||
|
src/ripple/protocol/Rules.h
|
||||||
src/ripple/protocol/SField.h
|
src/ripple/protocol/SField.h
|
||||||
src/ripple/protocol/SOTemplate.h
|
src/ripple/protocol/SOTemplate.h
|
||||||
src/ripple/protocol/STAccount.h
|
src/ripple/protocol/STAccount.h
|
||||||
|
|||||||
@@ -632,7 +632,7 @@ RCLConsensus::Adaptor::doAccept(
|
|||||||
auto const lastVal = ledgerMaster_.getValidatedLedger();
|
auto const lastVal = ledgerMaster_.getValidatedLedger();
|
||||||
std::optional<Rules> rules;
|
std::optional<Rules> rules;
|
||||||
if (lastVal)
|
if (lastVal)
|
||||||
rules.emplace(*lastVal, app_.config().features);
|
rules = makeRulesGivenLedger(*lastVal, app_.config().features);
|
||||||
else
|
else
|
||||||
rules.emplace(app_.config().features);
|
rules.emplace(app_.config().features);
|
||||||
app_.openLedger().accept(
|
app_.openLedger().accept(
|
||||||
|
|||||||
@@ -626,7 +626,7 @@ Ledger::setup(Config const& config)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rules_ = Rules(*this, config.features);
|
rules_ = makeRulesGivenLedger(*this, config.features);
|
||||||
}
|
}
|
||||||
catch (SHAMapMissingNode const&)
|
catch (SHAMapMissingNode const&)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1749,7 +1749,7 @@ NetworkOPsImp::switchLastClosedLedger(
|
|||||||
auto const lastVal = app_.getLedgerMaster().getValidatedLedger();
|
auto const lastVal = app_.getLedgerMaster().getValidatedLedger();
|
||||||
std::optional<Rules> rules;
|
std::optional<Rules> rules;
|
||||||
if (lastVal)
|
if (lastVal)
|
||||||
rules.emplace(*lastVal, app_.config().features);
|
rules = makeRulesGivenLedger(*lastVal, app_.config().features);
|
||||||
else
|
else
|
||||||
rules.emplace(app_.config().features);
|
rules.emplace(app_.config().features);
|
||||||
app_.openLedger().accept(
|
app_.openLedger().accept(
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ SetSignerList::preflight(PreflightContext const& ctx)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j);
|
auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j);
|
||||||
|
|
||||||
if (std::get<0>(result) != tesSUCCESS)
|
if (std::get<0>(result) != tesSUCCESS)
|
||||||
return std::get<0>(result);
|
return std::get<0>(result);
|
||||||
|
|
||||||
@@ -98,7 +99,11 @@ SetSignerList::preflight(PreflightContext const& ctx)
|
|||||||
// Validate our settings.
|
// Validate our settings.
|
||||||
auto const account = ctx.tx.getAccountID(sfAccount);
|
auto const account = ctx.tx.getAccountID(sfAccount);
|
||||||
NotTEC const ter = validateQuorumAndSignerEntries(
|
NotTEC const ter = validateQuorumAndSignerEntries(
|
||||||
std::get<1>(result), std::get<2>(result), account, ctx.j);
|
std::get<1>(result),
|
||||||
|
std::get<2>(result),
|
||||||
|
account,
|
||||||
|
ctx.j,
|
||||||
|
ctx.rules);
|
||||||
if (ter != tesSUCCESS)
|
if (ter != tesSUCCESS)
|
||||||
{
|
{
|
||||||
return ter;
|
return ter;
|
||||||
@@ -149,7 +154,7 @@ SetSignerList::preCompute()
|
|||||||
// is valid until the featureMultiSignReserve amendment passes. Once it
|
// is valid until the featureMultiSignReserve amendment passes. Once it
|
||||||
// passes then just 1 OwnerCount is associated with a SignerList.
|
// passes then just 1 OwnerCount is associated with a SignerList.
|
||||||
static int
|
static int
|
||||||
signerCountBasedOwnerCountDelta(std::size_t entryCount)
|
signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const& rules)
|
||||||
{
|
{
|
||||||
// We always compute the full change in OwnerCount, taking into account:
|
// We always compute the full change in OwnerCount, taking into account:
|
||||||
// o The fact that we're adding/removing a SignerList and
|
// o The fact that we're adding/removing a SignerList and
|
||||||
@@ -164,9 +169,10 @@ signerCountBasedOwnerCountDelta(std::size_t entryCount)
|
|||||||
// units. A SignerList with 8 entries would cost 10 OwnerCount units.
|
// units. A SignerList with 8 entries would cost 10 OwnerCount units.
|
||||||
//
|
//
|
||||||
// The static_cast should always be safe since entryCount should always
|
// The static_cast should always be safe since entryCount should always
|
||||||
// be in the range from 1 to 8. We've got a lot of room to grow.
|
// be in the range from 1 to 8 (or 32 if ExpandedSignerList is enabled).
|
||||||
|
// We've got a lot of room to grow.
|
||||||
assert(entryCount >= STTx::minMultiSigners);
|
assert(entryCount >= STTx::minMultiSigners);
|
||||||
assert(entryCount <= STTx::maxMultiSigners);
|
assert(entryCount <= STTx::maxMultiSigners(&rules));
|
||||||
return 2 + static_cast<int>(entryCount);
|
return 2 + static_cast<int>(entryCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +201,8 @@ removeSignersFromLedger(
|
|||||||
{
|
{
|
||||||
STArray const& actualList = signers->getFieldArray(sfSignerEntries);
|
STArray const& actualList = signers->getFieldArray(sfSignerEntries);
|
||||||
removeFromOwnerCount =
|
removeFromOwnerCount =
|
||||||
signerCountBasedOwnerCountDelta(actualList.size()) * -1;
|
signerCountBasedOwnerCountDelta(actualList.size(), view.rules()) *
|
||||||
|
-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the node from the account directory.
|
// Remove the node from the account directory.
|
||||||
@@ -238,13 +245,14 @@ SetSignerList::validateQuorumAndSignerEntries(
|
|||||||
std::uint32_t quorum,
|
std::uint32_t quorum,
|
||||||
std::vector<SignerEntries::SignerEntry> const& signers,
|
std::vector<SignerEntries::SignerEntry> const& signers,
|
||||||
AccountID const& account,
|
AccountID const& account,
|
||||||
beast::Journal j)
|
beast::Journal j,
|
||||||
|
Rules const& rules)
|
||||||
{
|
{
|
||||||
// Reject if there are too many or too few entries in the list.
|
// Reject if there are too many or too few entries in the list.
|
||||||
{
|
{
|
||||||
std::size_t const signerCount = signers.size();
|
std::size_t const signerCount = signers.size();
|
||||||
if ((signerCount < STTx::minMultiSigners) ||
|
if ((signerCount < STTx::minMultiSigners) ||
|
||||||
(signerCount > STTx::maxMultiSigners))
|
(signerCount > STTx::maxMultiSigners(&rules)))
|
||||||
{
|
{
|
||||||
JLOG(j.trace()) << "Too many or too few signers in signer list.";
|
JLOG(j.trace()) << "Too many or too few signers in signer list.";
|
||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
@@ -259,6 +267,9 @@ SetSignerList::validateQuorumAndSignerEntries(
|
|||||||
return temBAD_SIGNER;
|
return temBAD_SIGNER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is the ExpandedSignerList amendment active?
|
||||||
|
bool const expandedSignerList = rules.enabled(featureExpandedSignerList);
|
||||||
|
|
||||||
// Make sure no signers reference this account. Also make sure the
|
// Make sure no signers reference this account. Also make sure the
|
||||||
// quorum can be reached.
|
// quorum can be reached.
|
||||||
std::uint64_t allSignersWeight(0);
|
std::uint64_t allSignersWeight(0);
|
||||||
@@ -279,6 +290,14 @@ SetSignerList::validateQuorumAndSignerEntries(
|
|||||||
return temBAD_SIGNER;
|
return temBAD_SIGNER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signer.tag && !expandedSignerList)
|
||||||
|
{
|
||||||
|
JLOG(j.trace()) << "Malformed transaction: sfWalletLocator "
|
||||||
|
"specified in SignerEntry "
|
||||||
|
<< "but featureExpandedSignerList is not enabled.";
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't verify that the signer accounts exist. Non-existent accounts
|
// Don't verify that the signer accounts exist. Non-existent accounts
|
||||||
// may be phantom accounts (which are permitted).
|
// may be phantom accounts (which are permitted).
|
||||||
}
|
}
|
||||||
@@ -321,7 +340,8 @@ SetSignerList::replaceSignerList()
|
|||||||
std::uint32_t flags{lsfOneOwnerCount};
|
std::uint32_t flags{lsfOneOwnerCount};
|
||||||
if (!ctx_.view().rules().enabled(featureMultiSignReserve))
|
if (!ctx_.view().rules().enabled(featureMultiSignReserve))
|
||||||
{
|
{
|
||||||
addedOwnerCount = signerCountBasedOwnerCountDelta(signers_.size());
|
addedOwnerCount = signerCountBasedOwnerCountDelta(
|
||||||
|
signers_.size(), ctx_.view().rules());
|
||||||
flags = 0;
|
flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,6 +409,9 @@ SetSignerList::writeSignersToSLE(
|
|||||||
if (flags) // Only set flags if they are non-default (default is zero).
|
if (flags) // Only set flags if they are non-default (default is zero).
|
||||||
ledgerEntry->setFieldU32(sfFlags, flags);
|
ledgerEntry->setFieldU32(sfFlags, flags);
|
||||||
|
|
||||||
|
bool const expandedSignerList =
|
||||||
|
ctx_.view().rules().enabled(featureExpandedSignerList);
|
||||||
|
|
||||||
// Create the SignerListArray one SignerEntry at a time.
|
// Create the SignerListArray one SignerEntry at a time.
|
||||||
STArray toLedger(signers_.size());
|
STArray toLedger(signers_.size());
|
||||||
for (auto const& entry : signers_)
|
for (auto const& entry : signers_)
|
||||||
@@ -398,6 +421,11 @@ SetSignerList::writeSignersToSLE(
|
|||||||
obj.reserve(2);
|
obj.reserve(2);
|
||||||
obj.setAccountID(sfAccount, entry.account);
|
obj.setAccountID(sfAccount, entry.account);
|
||||||
obj.setFieldU16(sfSignerWeight, entry.weight);
|
obj.setFieldU16(sfSignerWeight, entry.weight);
|
||||||
|
|
||||||
|
// This is a defensive check to make absolutely sure we will never write
|
||||||
|
// a tag into the ledger while featureExpandedSignerList is not enabled
|
||||||
|
if (expandedSignerList && entry.tag)
|
||||||
|
obj.setFieldH256(sfWalletLocator, *(entry.tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the SignerEntries.
|
// Assign the SignerEntries.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <ripple/app/tx/impl/Transactor.h>
|
#include <ripple/app/tx/impl/Transactor.h>
|
||||||
#include <ripple/basics/Log.h>
|
#include <ripple/basics/Log.h>
|
||||||
#include <ripple/protocol/Indexes.h>
|
#include <ripple/protocol/Indexes.h>
|
||||||
|
#include <ripple/protocol/Rules.h>
|
||||||
#include <ripple/protocol/STArray.h>
|
#include <ripple/protocol/STArray.h>
|
||||||
#include <ripple/protocol/STObject.h>
|
#include <ripple/protocol/STObject.h>
|
||||||
#include <ripple/protocol/STTx.h>
|
#include <ripple/protocol/STTx.h>
|
||||||
@@ -83,7 +84,8 @@ private:
|
|||||||
std::uint32_t quorum,
|
std::uint32_t quorum,
|
||||||
std::vector<SignerEntries::SignerEntry> const& signers,
|
std::vector<SignerEntries::SignerEntry> const& signers,
|
||||||
AccountID const& account,
|
AccountID const& account,
|
||||||
beast::Journal j);
|
beast::Journal j,
|
||||||
|
Rules const&);
|
||||||
|
|
||||||
TER
|
TER
|
||||||
replaceSignerList();
|
replaceSignerList();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <ripple/protocol/STArray.h>
|
#include <ripple/protocol/STArray.h>
|
||||||
#include <ripple/protocol/STObject.h>
|
#include <ripple/protocol/STObject.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ SignerEntries::deserialize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SignerEntry> accountVec;
|
std::vector<SignerEntry> accountVec;
|
||||||
accountVec.reserve(STTx::maxMultiSigners);
|
accountVec.reserve(STTx::maxMultiSigners());
|
||||||
|
|
||||||
STArray const& sEntries(obj.getFieldArray(sfSignerEntries));
|
STArray const& sEntries(obj.getFieldArray(sfSignerEntries));
|
||||||
for (STObject const& sEntry : sEntries)
|
for (STObject const& sEntry : sEntries)
|
||||||
@@ -57,7 +58,9 @@ SignerEntries::deserialize(
|
|||||||
// Extract SignerEntry fields.
|
// Extract SignerEntry fields.
|
||||||
AccountID const account = sEntry.getAccountID(sfAccount);
|
AccountID const account = sEntry.getAccountID(sfAccount);
|
||||||
std::uint16_t const weight = sEntry.getFieldU16(sfSignerWeight);
|
std::uint16_t const weight = sEntry.getFieldU16(sfSignerWeight);
|
||||||
accountVec.emplace_back(account, weight);
|
std::optional<uint256> const tag = sEntry.at(~sfWalletLocator);
|
||||||
|
|
||||||
|
accountVec.emplace_back(account, weight, tag);
|
||||||
}
|
}
|
||||||
return accountVec;
|
return accountVec;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,11 @@
|
|||||||
#include <ripple/app/tx/impl/Transactor.h> // NotTEC
|
#include <ripple/app/tx/impl/Transactor.h> // NotTEC
|
||||||
#include <ripple/basics/Expected.h> //
|
#include <ripple/basics/Expected.h> //
|
||||||
#include <ripple/beast/utility/Journal.h> // beast::Journal
|
#include <ripple/beast/utility/Journal.h> // beast::Journal
|
||||||
|
#include <ripple/protocol/Rules.h> // Rules
|
||||||
#include <ripple/protocol/STTx.h> // STTx::maxMultiSigners
|
#include <ripple/protocol/STTx.h> // STTx::maxMultiSigners
|
||||||
#include <ripple/protocol/TER.h> // temMALFORMED
|
#include <ripple/protocol/TER.h> // temMALFORMED
|
||||||
#include <ripple/protocol/UintTypes.h> // AccountID
|
#include <ripple/protocol/UintTypes.h> // AccountID
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -42,9 +44,13 @@ public:
|
|||||||
{
|
{
|
||||||
AccountID account;
|
AccountID account;
|
||||||
std::uint16_t weight;
|
std::uint16_t weight;
|
||||||
|
std::optional<uint256> tag;
|
||||||
|
|
||||||
SignerEntry(AccountID const& inAccount, std::uint16_t inWeight)
|
SignerEntry(
|
||||||
: account(inAccount), weight(inWeight)
|
AccountID const& inAccount,
|
||||||
|
std::uint16_t inWeight,
|
||||||
|
std::optional<uint256> inTag)
|
||||||
|
: account(inAccount), weight(inWeight), tag(inTag)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ checkValidity(
|
|||||||
? STTx::RequireFullyCanonicalSig::yes
|
? STTx::RequireFullyCanonicalSig::yes
|
||||||
: STTx::RequireFullyCanonicalSig::no;
|
: STTx::RequireFullyCanonicalSig::no;
|
||||||
|
|
||||||
auto const sigVerify = tx.checkSign(requireCanonicalSig);
|
auto const sigVerify = tx.checkSign(requireCanonicalSig, rules);
|
||||||
if (!sigVerify)
|
if (!sigVerify)
|
||||||
{
|
{
|
||||||
router.setFlags(id, SF_SIGBAD);
|
router.setFlags(id, SF_SIGBAD);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <ripple/ledger/detail/ReadViewFwdRange.h>
|
#include <ripple/ledger/detail/ReadViewFwdRange.h>
|
||||||
#include <ripple/protocol/Indexes.h>
|
#include <ripple/protocol/Indexes.h>
|
||||||
#include <ripple/protocol/Protocol.h>
|
#include <ripple/protocol/Protocol.h>
|
||||||
|
#include <ripple/protocol/Rules.h>
|
||||||
#include <ripple/protocol/STAmount.h>
|
#include <ripple/protocol/STAmount.h>
|
||||||
#include <ripple/protocol/STLedgerEntry.h>
|
#include <ripple/protocol/STLedgerEntry.h>
|
||||||
#include <ripple/protocol/STTx.h>
|
#include <ripple/protocol/STTx.h>
|
||||||
@@ -125,64 +126,6 @@ struct LedgerInfo
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
class DigestAwareReadView;
|
|
||||||
|
|
||||||
/** Rules controlling protocol behavior. */
|
|
||||||
class Rules
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
class Impl;
|
|
||||||
|
|
||||||
std::shared_ptr<Impl const> impl_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Rules(Rules const&) = default;
|
|
||||||
Rules&
|
|
||||||
operator=(Rules const&) = default;
|
|
||||||
|
|
||||||
Rules() = delete;
|
|
||||||
|
|
||||||
/** Construct an empty rule set.
|
|
||||||
|
|
||||||
These are the rules reflected by
|
|
||||||
the genesis ledger.
|
|
||||||
*/
|
|
||||||
explicit Rules(std::unordered_set<uint256, beast::uhash<>> const& presets);
|
|
||||||
|
|
||||||
/** Construct rules from a ledger.
|
|
||||||
|
|
||||||
The ledger contents are analyzed for rules
|
|
||||||
and amendments and extracted to the object.
|
|
||||||
*/
|
|
||||||
explicit Rules(
|
|
||||||
DigestAwareReadView const& ledger,
|
|
||||||
std::unordered_set<uint256, beast::uhash<>> const& presets);
|
|
||||||
|
|
||||||
/** Returns `true` if a feature is enabled. */
|
|
||||||
bool
|
|
||||||
enabled(uint256 const& id) const;
|
|
||||||
|
|
||||||
/** Returns `true` if these rules don't match the ledger. */
|
|
||||||
bool
|
|
||||||
changed(DigestAwareReadView const& ledger) const;
|
|
||||||
|
|
||||||
/** Returns `true` if two rule sets are identical.
|
|
||||||
|
|
||||||
@note This is for diagnostics. To determine if new
|
|
||||||
rules should be constructed, call changed() first instead.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
operator==(Rules const&) const;
|
|
||||||
|
|
||||||
bool
|
|
||||||
operator!=(Rules const& other) const
|
|
||||||
{
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/** A view into a ledger.
|
/** A view into a ledger.
|
||||||
|
|
||||||
This interface provides read access to state
|
This interface provides read access to state
|
||||||
@@ -423,6 +366,11 @@ getCloseAgree(LedgerInfo const& info)
|
|||||||
void
|
void
|
||||||
addRaw(LedgerInfo const&, Serializer&, bool includeHash = false);
|
addRaw(LedgerInfo const&, Serializer&, bool includeHash = false);
|
||||||
|
|
||||||
|
Rules
|
||||||
|
makeRulesGivenLedger(
|
||||||
|
DigestAwareReadView const& ledger,
|
||||||
|
std::unordered_set<uint256, beast::uhash<>> const& presets);
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|
||||||
#include <ripple/ledger/detail/ReadViewFwdRange.ipp>
|
#include <ripple/ledger/detail/ReadViewFwdRange.ipp>
|
||||||
|
|||||||
@@ -21,108 +21,6 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
class Rules::Impl
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::unordered_set<uint256, hardened_hash<>> set_;
|
|
||||||
std::optional<uint256> digest_;
|
|
||||||
std::unordered_set<uint256, beast::uhash<>> const& presets_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Impl(std::unordered_set<uint256, beast::uhash<>> const& presets)
|
|
||||||
: presets_(presets)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit Impl(
|
|
||||||
DigestAwareReadView const& ledger,
|
|
||||||
std::unordered_set<uint256, beast::uhash<>> const& presets)
|
|
||||||
: presets_(presets)
|
|
||||||
{
|
|
||||||
auto const k = keylet::amendments();
|
|
||||||
digest_ = ledger.digest(k.key);
|
|
||||||
if (!digest_)
|
|
||||||
return;
|
|
||||||
auto const sle = ledger.read(k);
|
|
||||||
if (!sle)
|
|
||||||
{
|
|
||||||
// LogicError() ?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& item : sle->getFieldV256(sfAmendments))
|
|
||||||
set_.insert(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
enabled(uint256 const& feature) const
|
|
||||||
{
|
|
||||||
if (presets_.count(feature) > 0)
|
|
||||||
return true;
|
|
||||||
return set_.count(feature) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
changed(DigestAwareReadView const& ledger) const
|
|
||||||
{
|
|
||||||
auto const digest = ledger.digest(keylet::amendments().key);
|
|
||||||
if (!digest && !digest_)
|
|
||||||
return false;
|
|
||||||
if (!digest || !digest_)
|
|
||||||
return true;
|
|
||||||
return *digest != *digest_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
operator==(Impl const& other) const
|
|
||||||
{
|
|
||||||
if (!digest_ && !other.digest_)
|
|
||||||
return true;
|
|
||||||
if (!digest_ || !other.digest_)
|
|
||||||
return false;
|
|
||||||
return *digest_ == *other.digest_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Rules::Rules(
|
|
||||||
DigestAwareReadView const& ledger,
|
|
||||||
std::unordered_set<uint256, beast::uhash<>> const& presets)
|
|
||||||
: impl_(std::make_shared<Impl>(ledger, presets))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Rules::Rules(std::unordered_set<uint256, beast::uhash<>> const& presets)
|
|
||||||
: impl_(std::make_shared<Impl>(presets))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Rules::enabled(uint256 const& id) const
|
|
||||||
{
|
|
||||||
assert(impl_);
|
|
||||||
return impl_->enabled(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Rules::changed(DigestAwareReadView const& ledger) const
|
|
||||||
{
|
|
||||||
assert(impl_);
|
|
||||||
return impl_->changed(ledger);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Rules::operator==(Rules const& other) const
|
|
||||||
{
|
|
||||||
assert(impl_ && other.impl_);
|
|
||||||
if (impl_.get() == other.impl_.get())
|
|
||||||
return true;
|
|
||||||
return *impl_ == *other.impl_;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
ReadView::sles_type::sles_type(ReadView const& view) : ReadViewFwdRange(view)
|
ReadView::sles_type::sles_type(ReadView const& view) : ReadViewFwdRange(view)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -167,4 +65,20 @@ ReadView::txs_type::end() const -> iterator
|
|||||||
return iterator(view_, view_->txsEnd());
|
return iterator(view_, view_->txsEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rules
|
||||||
|
makeRulesGivenLedger(
|
||||||
|
DigestAwareReadView const& ledger,
|
||||||
|
std::unordered_set<uint256, beast::uhash<>> const& presets)
|
||||||
|
{
|
||||||
|
Keylet const k = keylet::amendments();
|
||||||
|
std::optional digest = ledger.digest(k.key);
|
||||||
|
if (digest)
|
||||||
|
{
|
||||||
|
auto const sle = ledger.read(k);
|
||||||
|
if (sle)
|
||||||
|
return Rules(presets, digest, sle->getFieldV256(sfAmendments));
|
||||||
|
}
|
||||||
|
return Rules(presets);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -373,6 +373,12 @@ message RootIndex
|
|||||||
bytes value = 1;
|
bytes value = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message WalletLocator
|
||||||
|
{
|
||||||
|
// 32 bytes
|
||||||
|
bytes value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// *** Messages wrapping variable length byte arrays ***
|
// *** Messages wrapping variable length byte arrays ***
|
||||||
|
|
||||||
@@ -586,6 +592,8 @@ message SignerEntry
|
|||||||
Account account = 1;
|
Account account = 1;
|
||||||
|
|
||||||
SignerWeight signer_weight = 2;
|
SignerWeight signer_weight = 2;
|
||||||
|
|
||||||
|
WalletLocator wallet_locator = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next field: 3
|
// Next field: 3
|
||||||
|
|||||||
@@ -334,6 +334,7 @@ extern uint256 const fixSTAmountCanonicalize;
|
|||||||
extern uint256 const fixRmSmallIncreasedQOffers;
|
extern uint256 const fixRmSmallIncreasedQOffers;
|
||||||
extern uint256 const featureCheckCashMakesTrustLine;
|
extern uint256 const featureCheckCashMakesTrustLine;
|
||||||
extern uint256 const featureNonFungibleTokensV1;
|
extern uint256 const featureNonFungibleTokensV1;
|
||||||
|
extern uint256 const featureExpandedSignerList;
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|
||||||
|
|||||||
86
src/ripple/protocol/Rules.h
Normal file
86
src/ripple/protocol/Rules.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef RIPPLE_LEDGER_RULES_H_INCLUDED
|
||||||
|
#define RIPPLE_LEDGER_RULES_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/base_uint.h>
|
||||||
|
#include <ripple/beast/hash/uhash.h>
|
||||||
|
#include <ripple/protocol/STVector256.h>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
class DigestAwareReadView;
|
||||||
|
|
||||||
|
/** Rules controlling protocol behavior. */
|
||||||
|
class Rules
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
// Carrying impl by shared_ptr makes Rules comparatively cheap to pass
|
||||||
|
// by value.
|
||||||
|
std::shared_ptr<Impl const> impl_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Rules(Rules const&) = default;
|
||||||
|
|
||||||
|
Rules&
|
||||||
|
operator=(Rules const&) = default;
|
||||||
|
|
||||||
|
Rules() = delete;
|
||||||
|
|
||||||
|
/** Construct an empty rule set.
|
||||||
|
|
||||||
|
These are the rules reflected by
|
||||||
|
the genesis ledger.
|
||||||
|
*/
|
||||||
|
explicit Rules(std::unordered_set<uint256, beast::uhash<>> const& presets);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Allow a friend function to construct Rules.
|
||||||
|
friend Rules
|
||||||
|
makeRulesGivenLedger(
|
||||||
|
DigestAwareReadView const& ledger,
|
||||||
|
std::unordered_set<uint256, beast::uhash<>> const& presets);
|
||||||
|
|
||||||
|
Rules(
|
||||||
|
std::unordered_set<uint256, beast::uhash<>> const& presets,
|
||||||
|
std::optional<uint256> const& digest,
|
||||||
|
STVector256 const& amendments);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Returns `true` if a feature is enabled. */
|
||||||
|
bool
|
||||||
|
enabled(uint256 const& feature) const;
|
||||||
|
|
||||||
|
/** Returns `true` if two rule sets are identical.
|
||||||
|
|
||||||
|
@note This is for diagnostics.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
operator==(Rules const&) const;
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(Rules const& other) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
|
#endif
|
||||||
@@ -21,7 +21,9 @@
|
|||||||
#define RIPPLE_PROTOCOL_STTX_H_INCLUDED
|
#define RIPPLE_PROTOCOL_STTX_H_INCLUDED
|
||||||
|
|
||||||
#include <ripple/basics/Expected.h>
|
#include <ripple/basics/Expected.h>
|
||||||
|
#include <ripple/protocol/Feature.h>
|
||||||
#include <ripple/protocol/PublicKey.h>
|
#include <ripple/protocol/PublicKey.h>
|
||||||
|
#include <ripple/protocol/Rules.h>
|
||||||
#include <ripple/protocol/STObject.h>
|
#include <ripple/protocol/STObject.h>
|
||||||
#include <ripple/protocol/SecretKey.h>
|
#include <ripple/protocol/SecretKey.h>
|
||||||
#include <ripple/protocol/SeqProxy.h>
|
#include <ripple/protocol/SeqProxy.h>
|
||||||
@@ -47,7 +49,16 @@ class STTx final : public STObject, public CountedObject<STTx>
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
static std::size_t const minMultiSigners = 1;
|
static std::size_t const minMultiSigners = 1;
|
||||||
static std::size_t const maxMultiSigners = 8;
|
|
||||||
|
// if rules are not supplied then the largest possible value is returned
|
||||||
|
static std::size_t
|
||||||
|
maxMultiSigners(Rules const* rules = 0)
|
||||||
|
{
|
||||||
|
if (rules && !rules->enabled(featureExpandedSignerList))
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
STTx() = delete;
|
STTx() = delete;
|
||||||
STTx(STTx const& other) = default;
|
STTx(STTx const& other) = default;
|
||||||
@@ -108,7 +119,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
enum class RequireFullyCanonicalSig : bool { no, yes };
|
enum class RequireFullyCanonicalSig : bool { no, yes };
|
||||||
Expected<void, std::string>
|
Expected<void, std::string>
|
||||||
checkSign(RequireFullyCanonicalSig requireCanonicalSig) const;
|
checkSign(RequireFullyCanonicalSig requireCanonicalSig, Rules const& rules)
|
||||||
|
const;
|
||||||
|
|
||||||
// SQL Functions with metadata.
|
// SQL Functions with metadata.
|
||||||
static std::string const&
|
static std::string const&
|
||||||
@@ -130,7 +142,9 @@ private:
|
|||||||
checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const;
|
checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const;
|
||||||
|
|
||||||
Expected<void, std::string>
|
Expected<void, std::string>
|
||||||
checkMultiSign(RequireFullyCanonicalSig requireCanonicalSig) const;
|
checkMultiSign(
|
||||||
|
RequireFullyCanonicalSig requireCanonicalSig,
|
||||||
|
Rules const& rules) const;
|
||||||
|
|
||||||
STBase*
|
STBase*
|
||||||
copy(std::size_t n, void* buf) const override;
|
copy(std::size_t n, void* buf) const override;
|
||||||
|
|||||||
@@ -438,6 +438,7 @@ REGISTER_FIX (fixSTAmountCanonicalize, Supported::yes, DefaultVote::yes
|
|||||||
REGISTER_FIX (fixRmSmallIncreasedQOffers, Supported::yes, DefaultVote::yes);
|
REGISTER_FIX (fixRmSmallIncreasedQOffers, Supported::yes, DefaultVote::yes);
|
||||||
REGISTER_FEATURE(CheckCashMakesTrustLine, Supported::yes, DefaultVote::no);
|
REGISTER_FEATURE(CheckCashMakesTrustLine, Supported::yes, DefaultVote::no);
|
||||||
REGISTER_FEATURE(NonFungibleTokensV1, Supported::yes, DefaultVote::no);
|
REGISTER_FEATURE(NonFungibleTokensV1, Supported::yes, DefaultVote::no);
|
||||||
|
REGISTER_FEATURE(ExpandedSignerList, Supported::yes, DefaultVote::no);
|
||||||
|
|
||||||
// The following amendments have been active for at least two years. Their
|
// The following amendments have been active for at least two years. Their
|
||||||
// pre-amendment code has been removed and the identifiers are deprecated.
|
// pre-amendment code has been removed and the identifiers are deprecated.
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ InnerObjectFormats::InnerObjectFormats()
|
|||||||
{
|
{
|
||||||
{sfAccount, soeREQUIRED},
|
{sfAccount, soeREQUIRED},
|
||||||
{sfSignerWeight, soeREQUIRED},
|
{sfSignerWeight, soeREQUIRED},
|
||||||
|
{sfWalletLocator, soeOPTIONAL},
|
||||||
});
|
});
|
||||||
|
|
||||||
add(sfSigner.jsonName.c_str(),
|
add(sfSigner.jsonName.c_str(),
|
||||||
|
|||||||
103
src/ripple/protocol/impl/Rules.cpp
Normal file
103
src/ripple/protocol/impl/Rules.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <ripple/protocol/Rules.h>
|
||||||
|
|
||||||
|
#include <ripple/protocol/Indexes.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
class Rules::Impl
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::unordered_set<uint256, hardened_hash<>> set_;
|
||||||
|
std::optional<uint256> digest_;
|
||||||
|
std::unordered_set<uint256, beast::uhash<>> const& presets_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Impl(std::unordered_set<uint256, beast::uhash<>> const& presets)
|
||||||
|
: presets_(presets)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Impl(
|
||||||
|
std::unordered_set<uint256, beast::uhash<>> const& presets,
|
||||||
|
std::optional<uint256> const& digest,
|
||||||
|
STVector256 const& amendments)
|
||||||
|
: presets_(presets)
|
||||||
|
{
|
||||||
|
digest_ = digest;
|
||||||
|
set_.reserve(amendments.size());
|
||||||
|
set_.insert(amendments.begin(), amendments.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
enabled(uint256 const& feature) const
|
||||||
|
{
|
||||||
|
if (presets_.count(feature) > 0)
|
||||||
|
return true;
|
||||||
|
return set_.count(feature) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(Impl const& other) const
|
||||||
|
{
|
||||||
|
if (!digest_ && !other.digest_)
|
||||||
|
return true;
|
||||||
|
if (!digest_ || !other.digest_)
|
||||||
|
return false;
|
||||||
|
return *digest_ == *other.digest_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Rules::Rules(std::unordered_set<uint256, beast::uhash<>> const& presets)
|
||||||
|
: impl_(std::make_shared<Impl>(presets))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Rules::Rules(
|
||||||
|
std::unordered_set<uint256, beast::uhash<>> const& presets,
|
||||||
|
std::optional<uint256> const& digest,
|
||||||
|
STVector256 const& amendments)
|
||||||
|
: impl_(std::make_shared<Impl>(presets, digest, amendments))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Rules::enabled(uint256 const& feature) const
|
||||||
|
{
|
||||||
|
assert(impl_);
|
||||||
|
return impl_->enabled(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Rules::operator==(Rules const& other) const
|
||||||
|
{
|
||||||
|
assert(impl_ && other.impl_);
|
||||||
|
if (impl_.get() == other.impl_.get())
|
||||||
|
return true;
|
||||||
|
return *impl_ == *other.impl_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Rules::operator!=(Rules const& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
} // namespace ripple
|
||||||
@@ -206,7 +206,9 @@ STTx::sign(PublicKey const& publicKey, SecretKey const& secretKey)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expected<void, std::string>
|
Expected<void, std::string>
|
||||||
STTx::checkSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
STTx::checkSign(
|
||||||
|
RequireFullyCanonicalSig requireCanonicalSig,
|
||||||
|
Rules const& rules) const
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -214,8 +216,9 @@ STTx::checkSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
|||||||
// at the SigningPubKey. If it's empty we must be
|
// at the SigningPubKey. If it's empty we must be
|
||||||
// multi-signing. Otherwise we're single-signing.
|
// multi-signing. Otherwise we're single-signing.
|
||||||
Blob const& signingPubKey = getFieldVL(sfSigningPubKey);
|
Blob const& signingPubKey = getFieldVL(sfSigningPubKey);
|
||||||
return signingPubKey.empty() ? checkMultiSign(requireCanonicalSig)
|
return signingPubKey.empty()
|
||||||
: checkSingleSign(requireCanonicalSig);
|
? checkMultiSign(requireCanonicalSig, rules)
|
||||||
|
: checkSingleSign(requireCanonicalSig);
|
||||||
}
|
}
|
||||||
catch (std::exception const&)
|
catch (std::exception const&)
|
||||||
{
|
{
|
||||||
@@ -327,7 +330,9 @@ STTx::checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expected<void, std::string>
|
Expected<void, std::string>
|
||||||
STTx::checkMultiSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
STTx::checkMultiSign(
|
||||||
|
RequireFullyCanonicalSig requireCanonicalSig,
|
||||||
|
Rules const& rules) const
|
||||||
{
|
{
|
||||||
// Make sure the MultiSigners are present. Otherwise they are not
|
// Make sure the MultiSigners are present. Otherwise they are not
|
||||||
// attempting multi-signing and we just have a bad SigningPubKey.
|
// attempting multi-signing and we just have a bad SigningPubKey.
|
||||||
@@ -342,7 +347,8 @@ STTx::checkMultiSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
|||||||
STArray const& signers{getFieldArray(sfSigners)};
|
STArray const& signers{getFieldArray(sfSigners)};
|
||||||
|
|
||||||
// There are well known bounds that the number of signers must be within.
|
// There are well known bounds that the number of signers must be within.
|
||||||
if (signers.size() < minMultiSigners || signers.size() > maxMultiSigners)
|
if (signers.size() < minMultiSigners ||
|
||||||
|
signers.size() > maxMultiSigners(&rules))
|
||||||
return Unexpected("Invalid Signers array size.");
|
return Unexpected("Invalid Signers array size.");
|
||||||
|
|
||||||
// We can ease the computational load inside the loop a bit by
|
// We can ease the computational load inside the loop a bit by
|
||||||
|
|||||||
@@ -746,6 +746,14 @@ populateSignerListID(T& to, STObject const& from)
|
|||||||
[&to]() { return to.mutable_signer_list_id(); }, from, sfSignerListID);
|
[&to]() { return to.mutable_signer_list_id(); }, from, sfSignerListID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
populateWalletLocator(T& to, STObject const& from)
|
||||||
|
{
|
||||||
|
populateProtoPrimitive(
|
||||||
|
[&to]() { return to.mutable_wallet_locator(); }, from, sfWalletLocator);
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void
|
void
|
||||||
populateTicketSequence(T& to, STObject const& from)
|
populateTicketSequence(T& to, STObject const& from)
|
||||||
@@ -1012,6 +1020,7 @@ populateSignerEntries(T& to, STObject const& from)
|
|||||||
[](auto& innerObj, auto& innerProto) {
|
[](auto& innerObj, auto& innerProto) {
|
||||||
populateAccount(innerProto, innerObj);
|
populateAccount(innerProto, innerObj);
|
||||||
populateSignerWeight(innerProto, innerObj);
|
populateSignerWeight(innerProto, innerObj);
|
||||||
|
populateWalletLocator(innerProto, innerObj);
|
||||||
},
|
},
|
||||||
from,
|
from,
|
||||||
sfSignerEntries,
|
sfSignerEntries,
|
||||||
|
|||||||
@@ -34,6 +34,30 @@ class MultiSign_test : public beast::unit_test::suite
|
|||||||
jtx::Account const phase{"phase", KeyType::ed25519};
|
jtx::Account const phase{"phase", KeyType::ed25519};
|
||||||
jtx::Account const shade{"shade", KeyType::secp256k1};
|
jtx::Account const shade{"shade", KeyType::secp256k1};
|
||||||
jtx::Account const spook{"spook", KeyType::ed25519};
|
jtx::Account const spook{"spook", KeyType::ed25519};
|
||||||
|
jtx::Account const acc10{"acc10", KeyType::ed25519};
|
||||||
|
jtx::Account const acc11{"acc11", KeyType::ed25519};
|
||||||
|
jtx::Account const acc12{"acc12", KeyType::ed25519};
|
||||||
|
jtx::Account const acc13{"acc13", KeyType::ed25519};
|
||||||
|
jtx::Account const acc14{"acc14", KeyType::ed25519};
|
||||||
|
jtx::Account const acc15{"acc15", KeyType::ed25519};
|
||||||
|
jtx::Account const acc16{"acc16", KeyType::ed25519};
|
||||||
|
jtx::Account const acc17{"acc17", KeyType::ed25519};
|
||||||
|
jtx::Account const acc18{"acc18", KeyType::ed25519};
|
||||||
|
jtx::Account const acc19{"acc19", KeyType::ed25519};
|
||||||
|
jtx::Account const acc20{"acc20", KeyType::ed25519};
|
||||||
|
jtx::Account const acc21{"acc21", KeyType::ed25519};
|
||||||
|
jtx::Account const acc22{"acc22", KeyType::ed25519};
|
||||||
|
jtx::Account const acc23{"acc23", KeyType::ed25519};
|
||||||
|
jtx::Account const acc24{"acc24", KeyType::ed25519};
|
||||||
|
jtx::Account const acc25{"acc25", KeyType::ed25519};
|
||||||
|
jtx::Account const acc26{"acc26", KeyType::ed25519};
|
||||||
|
jtx::Account const acc27{"acc27", KeyType::ed25519};
|
||||||
|
jtx::Account const acc28{"acc28", KeyType::ed25519};
|
||||||
|
jtx::Account const acc29{"acc29", KeyType::ed25519};
|
||||||
|
jtx::Account const acc30{"acc30", KeyType::ed25519};
|
||||||
|
jtx::Account const acc31{"acc31", KeyType::ed25519};
|
||||||
|
jtx::Account const acc32{"acc32", KeyType::ed25519};
|
||||||
|
jtx::Account const acc33{"acc33", KeyType::ed25519};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void
|
void
|
||||||
@@ -159,22 +183,30 @@ public:
|
|||||||
{spook, 1}}),
|
{spook, 1}}),
|
||||||
ter(temBAD_QUORUM));
|
ter(temBAD_QUORUM));
|
||||||
|
|
||||||
// Make a signer list that's too big. Should fail.
|
// clang-format off
|
||||||
|
// Make a signer list that's too big. Should fail. (Even with
|
||||||
|
// ExpandedSignerList)
|
||||||
Account const spare("spare", KeyType::secp256k1);
|
Account const spare("spare", KeyType::secp256k1);
|
||||||
env(signers(
|
env(signers(
|
||||||
alice,
|
alice,
|
||||||
1,
|
1,
|
||||||
{{bogie, 1},
|
features[featureExpandedSignerList]
|
||||||
{demon, 1},
|
? std::vector<signer>{{bogie, 1}, {demon, 1}, {ghost, 1},
|
||||||
{ghost, 1},
|
{haunt, 1}, {jinni, 1}, {phase, 1},
|
||||||
{haunt, 1},
|
{shade, 1}, {spook, 1}, {spare, 1},
|
||||||
{jinni, 1},
|
{acc10, 1}, {acc11, 1}, {acc12, 1},
|
||||||
{phase, 1},
|
{acc13, 1}, {acc14, 1}, {acc15, 1},
|
||||||
{shade, 1},
|
{acc16, 1}, {acc17, 1}, {acc18, 1},
|
||||||
{spook, 1},
|
{acc19, 1}, {acc20, 1}, {acc21, 1},
|
||||||
{spare, 1}}),
|
{acc22, 1}, {acc23, 1}, {acc24, 1},
|
||||||
|
{acc25, 1}, {acc26, 1}, {acc27, 1},
|
||||||
|
{acc28, 1}, {acc29, 1}, {acc30, 1},
|
||||||
|
{acc31, 1}, {acc32, 1}, {acc33, 1}}
|
||||||
|
: std::vector<signer>{{bogie, 1}, {demon, 1}, {ghost, 1},
|
||||||
|
{haunt, 1}, {jinni, 1}, {phase, 1},
|
||||||
|
{shade, 1}, {spook, 1}, {spare, 1}}),
|
||||||
ter(temMALFORMED));
|
ter(temMALFORMED));
|
||||||
|
// clang-format on
|
||||||
env.close();
|
env.close();
|
||||||
env.require(owners(alice, 0));
|
env.require(owners(alice, 0));
|
||||||
}
|
}
|
||||||
@@ -1149,20 +1181,56 @@ public:
|
|||||||
"fails local checks: Invalid Signers array size.");
|
"fails local checks: Invalid Signers array size.");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Multisign 9 times should fail.
|
// Multisign 9 (!ExpandedSignerList) | 33 (ExpandedSignerList) times
|
||||||
|
// should fail.
|
||||||
JTx tx = env.jt(
|
JTx tx = env.jt(
|
||||||
noop(alice),
|
noop(alice),
|
||||||
fee(2 * baseFee),
|
fee(2 * baseFee),
|
||||||
msig(
|
|
||||||
bogie,
|
features[featureExpandedSignerList] ? msig(
|
||||||
bogie,
|
bogie,
|
||||||
bogie,
|
bogie,
|
||||||
bogie,
|
bogie,
|
||||||
bogie,
|
bogie,
|
||||||
bogie,
|
bogie,
|
||||||
bogie,
|
bogie,
|
||||||
bogie,
|
bogie,
|
||||||
bogie));
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie)
|
||||||
|
: msig(
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie,
|
||||||
|
bogie));
|
||||||
STTx local = *(tx.stx);
|
STTx local = *(tx.stx);
|
||||||
auto const info = submitSTTx(local);
|
auto const info = submitSTTx(local);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
@@ -1517,6 +1585,82 @@ public:
|
|||||||
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_signersWithTags(FeatureBitset features)
|
||||||
|
{
|
||||||
|
if (!features[featureExpandedSignerList])
|
||||||
|
return;
|
||||||
|
|
||||||
|
testcase("Signers With Tags");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
Env env{*this, features};
|
||||||
|
Account const alice{"alice", KeyType::ed25519};
|
||||||
|
env.fund(XRP(1000), alice);
|
||||||
|
env.close();
|
||||||
|
uint8_t tag1[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
||||||
|
|
||||||
|
uint8_t tag2[] =
|
||||||
|
"hello world some ascii 32b long"; // including 1 byte for NUL
|
||||||
|
|
||||||
|
uint256 bogie_tag = ripple::base_uint<256>::fromVoid(tag1);
|
||||||
|
uint256 demon_tag = ripple::base_uint<256>::fromVoid(tag2);
|
||||||
|
|
||||||
|
// Attach phantom signers to alice and use them for a transaction.
|
||||||
|
env(signers(alice, 1, {{bogie, 1, bogie_tag}, {demon, 1, demon_tag}}));
|
||||||
|
env.close();
|
||||||
|
env.require(owners(alice, features[featureMultiSignReserve] ? 1 : 4));
|
||||||
|
|
||||||
|
// This should work.
|
||||||
|
auto const baseFee = env.current()->fees().base;
|
||||||
|
std::uint32_t aliceSeq = env.seq(alice);
|
||||||
|
env(noop(alice), msig(bogie, demon), fee(3 * baseFee));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
|
||||||
|
|
||||||
|
// Either signer alone should work.
|
||||||
|
aliceSeq = env.seq(alice);
|
||||||
|
env(noop(alice), msig(bogie), fee(2 * baseFee));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
|
||||||
|
|
||||||
|
aliceSeq = env.seq(alice);
|
||||||
|
env(noop(alice), msig(demon), fee(2 * baseFee));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
|
||||||
|
|
||||||
|
// Duplicate signers should fail.
|
||||||
|
aliceSeq = env.seq(alice);
|
||||||
|
env(noop(alice), msig(demon, demon), fee(3 * baseFee), ter(temINVALID));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
||||||
|
|
||||||
|
// A non-signer should fail.
|
||||||
|
aliceSeq = env.seq(alice);
|
||||||
|
env(noop(alice),
|
||||||
|
msig(bogie, spook),
|
||||||
|
fee(3 * baseFee),
|
||||||
|
ter(tefBAD_SIGNATURE));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
||||||
|
|
||||||
|
// Don't meet the quorum. Should fail.
|
||||||
|
env(signers(alice, 2, {{bogie, 1}, {demon, 1}}));
|
||||||
|
aliceSeq = env.seq(alice);
|
||||||
|
env(noop(alice), msig(bogie), fee(2 * baseFee), ter(tefBAD_QUORUM));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
||||||
|
|
||||||
|
// Meet the quorum. Should succeed.
|
||||||
|
aliceSeq = env.seq(alice);
|
||||||
|
env(noop(alice), msig(bogie, demon), fee(3 * baseFee));
|
||||||
|
env.close();
|
||||||
|
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testAll(FeatureBitset features)
|
testAll(FeatureBitset features)
|
||||||
{
|
{
|
||||||
@@ -1537,6 +1681,7 @@ public:
|
|||||||
test_multisigningMultisigner(features);
|
test_multisigningMultisigner(features);
|
||||||
test_signForHash(features);
|
test_signForHash(features);
|
||||||
test_signersWithTickets(features);
|
test_signersWithTickets(features);
|
||||||
|
test_signersWithTags(features);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1545,10 +1690,13 @@ public:
|
|||||||
using namespace jtx;
|
using namespace jtx;
|
||||||
auto const all = supported_amendments();
|
auto const all = supported_amendments();
|
||||||
|
|
||||||
// The reserve required on a signer list changes based on.
|
// The reserve required on a signer list changes based on
|
||||||
// featureMultiSignReserve. Test both with and without.
|
// featureMultiSignReserve. Limits on the number of signers
|
||||||
testAll(all - featureMultiSignReserve);
|
// changes based on featureExpandedSignerList. Test both with and
|
||||||
testAll(all | featureMultiSignReserve);
|
// without.
|
||||||
|
testAll(all - featureMultiSignReserve - featureExpandedSignerList);
|
||||||
|
testAll(all - featureExpandedSignerList);
|
||||||
|
testAll(all);
|
||||||
test_amendmentTransition();
|
test_amendmentTransition();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
#include <ripple/protocol/Sign.h>
|
#include <ripple/protocol/Sign.h>
|
||||||
#include <ripple/protocol/UintTypes.h>
|
#include <ripple/protocol/UintTypes.h>
|
||||||
#include <ripple/protocol/jss.h>
|
#include <ripple/protocol/jss.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
#include <test/jtx/multisign.h>
|
#include <test/jtx/multisign.h>
|
||||||
#include <test/jtx/utility.h>
|
#include <test/jtx/utility.h>
|
||||||
|
|
||||||
@@ -46,6 +48,8 @@ signers(
|
|||||||
auto& je = ja[i][sfSignerEntry.getJsonName()];
|
auto& je = ja[i][sfSignerEntry.getJsonName()];
|
||||||
je[jss::Account] = e.account.human();
|
je[jss::Account] = e.account.human();
|
||||||
je[sfSignerWeight.getJsonName()] = e.weight;
|
je[sfSignerWeight.getJsonName()] = e.weight;
|
||||||
|
if (e.tag)
|
||||||
|
je[sfWalletLocator.getJsonName()] = to_string(*e.tag);
|
||||||
}
|
}
|
||||||
return jv;
|
return jv;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#define RIPPLE_TEST_JTX_MULTISIGN_H_INCLUDED
|
#define RIPPLE_TEST_JTX_MULTISIGN_H_INCLUDED
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
#include <test/jtx/Account.h>
|
#include <test/jtx/Account.h>
|
||||||
#include <test/jtx/amount.h>
|
#include <test/jtx/amount.h>
|
||||||
#include <test/jtx/owners.h>
|
#include <test/jtx/owners.h>
|
||||||
@@ -35,9 +36,13 @@ struct signer
|
|||||||
{
|
{
|
||||||
std::uint32_t weight;
|
std::uint32_t weight;
|
||||||
Account account;
|
Account account;
|
||||||
|
std::optional<uint256> tag;
|
||||||
|
|
||||||
signer(Account account_, std::uint32_t weight_ = 1)
|
signer(
|
||||||
: weight(weight_), account(std::move(account_))
|
Account account_,
|
||||||
|
std::uint32_t weight_ = 1,
|
||||||
|
std::optional<uint256> tag_ = std::nullopt)
|
||||||
|
: weight(weight_), account(std::move(account_)), tag(std::move(tag_))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <ripple/basics/Slice.h>
|
#include <ripple/basics/Slice.h>
|
||||||
#include <ripple/beast/unit_test.h>
|
#include <ripple/beast/unit_test.h>
|
||||||
#include <ripple/json/to_string.h>
|
#include <ripple/json/to_string.h>
|
||||||
|
#include <ripple/protocol/Rules.h>
|
||||||
#include <ripple/protocol/STAmount.h>
|
#include <ripple/protocol/STAmount.h>
|
||||||
#include <ripple/protocol/STParsedJSON.h>
|
#include <ripple/protocol/STParsedJSON.h>
|
||||||
#include <ripple/protocol/STTx.h>
|
#include <ripple/protocol/STTx.h>
|
||||||
@@ -27,7 +28,6 @@
|
|||||||
#include <ripple/protocol/TxFormats.h>
|
#include <ripple/protocol/TxFormats.h>
|
||||||
#include <ripple/protocol/UintTypes.h>
|
#include <ripple/protocol/UintTypes.h>
|
||||||
#include <ripple/protocol/messages.h>
|
#include <ripple/protocol/messages.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
@@ -1591,8 +1591,10 @@ public:
|
|||||||
});
|
});
|
||||||
j.sign(keypair.first, keypair.second);
|
j.sign(keypair.first, keypair.second);
|
||||||
|
|
||||||
|
Rules defaultRules{{}};
|
||||||
|
|
||||||
unexpected(
|
unexpected(
|
||||||
!j.checkSign(STTx::RequireFullyCanonicalSig::yes),
|
!j.checkSign(STTx::RequireFullyCanonicalSig::yes, defaultRules),
|
||||||
"Transaction fails signature test");
|
"Transaction fails signature test");
|
||||||
|
|
||||||
Serializer rawTxn;
|
Serializer rawTxn;
|
||||||
|
|||||||
Reference in New Issue
Block a user