mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Simple multisigning (RIPD-182):
With this changeset two-level multisigning is removed from the
codebase and replaced with single-level multisigning.
Additionally, SignerLists in the ledger are prepared for the
possibility of multiple SignerLists per account. This was done
by adding a defaulted 32-bit SignerListID to each SignerList.
The SignerListIndex calculation incorporates the SignerListID.
There are three known missing elements:
1. Multisigned transactions should require higher fees than
regular (single-signed) transaction. That's not yet
implemented.
2. It should be possible to disable the master key on an account
if that account is multisign enabled (has a signer list).
That's not yet implemented.
3. Documentation about multisigning needs to be improved.
Multisigning is still compiled out of the code base. To enable
multisigning for a stand-alone rippled, change the
RIPPLE_ENABLE_MULTI_SIGN macro (in BeastConfig.h) to "1" and
rebuild.
This commit also addresses:
o RIPD-912: Remove multisign APIs from STObject, and
o RIPD-944: Replace common_transactor with jtx at call sites.
This commit is contained in:
committed by
Nik Bougalis
parent
ceeb36039e
commit
9e69bd5c56
@@ -32,6 +32,11 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// We're prepared for there to be multiple signer lists in the future,
|
||||
// but we don't need them yet. So for the time being we're manually
|
||||
// setting the sfSignerListID to zero in all cases.
|
||||
static std::uint32_t const defaultSignerListID_ = 0;
|
||||
|
||||
std::tuple<TER, std::uint32_t,
|
||||
std::vector<SignerEntries::SignerEntry>,
|
||||
SetSignerList::Operation>
|
||||
@@ -47,17 +52,15 @@ SetSignerList::determineOperation(STTx const& tx,
|
||||
bool const hasSignerEntries(tx.isFieldPresent(sfSignerEntries));
|
||||
if (quorum && hasSignerEntries)
|
||||
{
|
||||
SignerEntries::Decoded signers(
|
||||
SignerEntries::deserialize(tx, j, "transaction"));
|
||||
auto signers = SignerEntries::deserialize(tx, j, "transaction");
|
||||
|
||||
if (signers.ter != tesSUCCESS)
|
||||
return std::make_tuple(signers.ter,
|
||||
quorum, sign, op);
|
||||
if (signers.second != tesSUCCESS)
|
||||
return std::make_tuple(signers.second, quorum, sign, op);
|
||||
|
||||
std::sort(signers.vec.begin(), signers.vec.end());
|
||||
std::sort(signers.first.begin(), signers.first.end());
|
||||
|
||||
// Save deserialized list for later.
|
||||
sign = std::move(signers.vec);
|
||||
sign = std::move(signers.first);
|
||||
op = set;
|
||||
}
|
||||
else if ((quorum == 0) && !hasSignerEntries)
|
||||
@@ -154,11 +157,10 @@ SetSignerList::validateQuorumAndSignerEntries (
|
||||
// Reject if there are too many or too few entries in the list.
|
||||
{
|
||||
std::size_t const signerCount = signers.size ();
|
||||
if ((signerCount < SignerEntries::minEntries)
|
||||
|| (signerCount > SignerEntries::maxEntries))
|
||||
if ((signerCount < STTx::minMultiSigners)
|
||||
|| (signerCount > STTx::maxMultiSigners))
|
||||
{
|
||||
if (j.trace) 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;
|
||||
}
|
||||
}
|
||||
@@ -168,8 +170,7 @@ SetSignerList::validateQuorumAndSignerEntries (
|
||||
if (std::adjacent_find (
|
||||
signers.begin (), signers.end ()) != signers.end ())
|
||||
{
|
||||
if (j.trace) j.trace <<
|
||||
"Duplicate signers in signer list";
|
||||
JLOG(j.trace) << "Duplicate signers in signer list";
|
||||
return temBAD_SIGNER;
|
||||
}
|
||||
|
||||
@@ -178,12 +179,18 @@ SetSignerList::validateQuorumAndSignerEntries (
|
||||
std::uint64_t allSignersWeight (0);
|
||||
for (auto const& signer : signers)
|
||||
{
|
||||
std::uint32_t const weight = signer.weight;
|
||||
if (weight <= 0)
|
||||
{
|
||||
JLOG(j.trace) << "Every signer must have a positive weight.";
|
||||
return temBAD_WEIGHT;
|
||||
}
|
||||
|
||||
allSignersWeight += signer.weight;
|
||||
|
||||
if (signer.account == account)
|
||||
{
|
||||
if (j.trace) j.trace <<
|
||||
"A signer may not self reference account.";
|
||||
JLOG(j.trace) << "A signer may not self reference account.";
|
||||
return temBAD_SIGNER;
|
||||
}
|
||||
|
||||
@@ -192,8 +199,7 @@ SetSignerList::validateQuorumAndSignerEntries (
|
||||
}
|
||||
if ((quorum <= 0) || (allSignersWeight < quorum))
|
||||
{
|
||||
if (j.trace) j.trace <<
|
||||
"Quorum is unreachable";
|
||||
JLOG(j.trace) << "Quorum is unreachable";
|
||||
return temBAD_QUORUM;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
@@ -241,9 +247,8 @@ SetSignerList::replaceSignerList (uint256 const& index)
|
||||
TER result = dirAdd(ctx_.view (),
|
||||
hint, getOwnerDirIndex (account_), index, describer);
|
||||
|
||||
if (j_.trace) j_.trace <<
|
||||
"Create signer list for account " <<
|
||||
account_ << ": " << transHuman (result);
|
||||
JLOG(j_.trace) << "Create signer list for account " <<
|
||||
toBase58(account_) << ": " << transHuman (result);
|
||||
|
||||
if (result != tesSUCCESS)
|
||||
return result;
|
||||
@@ -270,7 +275,7 @@ SetSignerList::destroySignerList (uint256 const& index)
|
||||
|
||||
// We have to examine the current SignerList so we know how much to
|
||||
// reduce the OwnerCount.
|
||||
std::uint32_t removeFromOwnerCount = 0;
|
||||
std::int32_t removeFromOwnerCount = 0;
|
||||
auto const k = keylet::signers(account_);
|
||||
SLE::pointer accountSignersList =
|
||||
view().peek (k);
|
||||
@@ -278,7 +283,7 @@ SetSignerList::destroySignerList (uint256 const& index)
|
||||
{
|
||||
STArray const& actualList =
|
||||
accountSignersList->getFieldArray (sfSignerEntries);
|
||||
removeFromOwnerCount = ownerCountDelta (actualList.size ());
|
||||
removeFromOwnerCount = ownerCountDelta (actualList.size ()) * -1;
|
||||
}
|
||||
|
||||
// Remove the node from the account directory.
|
||||
@@ -306,6 +311,9 @@ SetSignerList::writeSignersToLedger (SLE::pointer ledgerEntry)
|
||||
// Assign the quorum.
|
||||
ledgerEntry->setFieldU32 (sfSignerQuorum, quorum_);
|
||||
|
||||
// For now, assign the default SignerListID.
|
||||
ledgerEntry->setFieldU32 (sfSignerListID, defaultSignerListID_);
|
||||
|
||||
// Create the SignerListArray one SignerEntry at a time.
|
||||
STArray toLedger (signers_.size ());
|
||||
for (auto const& entry : signers_)
|
||||
@@ -329,24 +337,13 @@ SetSignerList::ownerCountDelta (std::size_t entryCount)
|
||||
// o Accounting for the number of entries in the list.
|
||||
// We can get away with that because lists are not adjusted incrementally;
|
||||
// we add or remove an entire list.
|
||||
|
||||
// The wiki (https://wiki.ripple.com/Multisign#Fees_2) currently says
|
||||
// (December 2014) the reserve should be
|
||||
// Reserve * (N + 1) / 2
|
||||
// That's not making sense to me right now, since I'm working in
|
||||
// integral OwnerCount units. If, say, N is 4 I don't know how to return
|
||||
// 4.5 units as an integer.
|
||||
//
|
||||
// So, just to get started, I'm saying that:
|
||||
// The rule is:
|
||||
// o Simply having a SignerList costs 2 OwnerCount units.
|
||||
// o And each signer in the list costs 1 more OwnerCount unit.
|
||||
// So, at a minimum, adding a SignerList with 2 entries costs 4 OwnerCount
|
||||
// So, at a minimum, adding a SignerList with 1 entry costs 3 OwnerCount
|
||||
// units. A SignerList with 8 entries would cost 10 OwnerCount units.
|
||||
//
|
||||
// It's worth noting that once this reserve policy has gotten into the
|
||||
// wild it will be very difficult to change. So think hard about what
|
||||
// we want for the long term.
|
||||
return 2 + entryCount;
|
||||
return 2 + entryCount;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
Reference in New Issue
Block a user