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:
Scott Schurr
2015-06-22 10:49:03 -07:00
committed by Nik Bougalis
parent ceeb36039e
commit 9e69bd5c56
33 changed files with 851 additions and 3323 deletions

View File

@@ -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