mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
fix(amendment): Add missing fields for keylets to ledger objects (#5646)
This change adds a fix amendment (`fixIncludeKeyletFields`) that adds: * `sfSequence` to `Escrow` and `PayChannel` * `sfOwner` to `SignerList` * `sfOracleDocumentID` to `Oracle` This ensures that all ledger entries hold all the information needed to determine their keylet.
This commit is contained in:
@@ -287,9 +287,11 @@ delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept;
|
||||
Keylet
|
||||
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType);
|
||||
|
||||
// `seq` is stored as `sfXChainClaimID` in the object
|
||||
Keylet
|
||||
xChainClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
||||
|
||||
// `seq` is stored as `sfXChainAccountCreateCount` in the object
|
||||
Keylet
|
||||
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
// If you add an amendment here, then do not forget to increment `numFeatures`
|
||||
// in include/xrpl/protocol/Feature.h.
|
||||
|
||||
XRPL_FIX (IncludeKeyletFields, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -120,6 +120,7 @@ LEDGER_ENTRY(ltNFTOKEN_PAGE, 0x0050, NFTokenPage, nft_page, ({
|
||||
// All fields are soeREQUIRED because there is always a SignerEntries.
|
||||
// If there are no SignerEntries the node is deleted.
|
||||
LEDGER_ENTRY(ltSIGNER_LIST, 0x0053, SignerList, signer_list, ({
|
||||
{sfOwner, soeOPTIONAL},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfSignerQuorum, soeREQUIRED},
|
||||
{sfSignerEntries, soeREQUIRED},
|
||||
@@ -188,7 +189,7 @@ LEDGER_ENTRY(ltDIR_NODE, 0x0064, DirectoryNode, directory, ({
|
||||
{sfNFTokenID, soeOPTIONAL},
|
||||
{sfPreviousTxnID, soeOPTIONAL},
|
||||
{sfPreviousTxnLgrSeq, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL}
|
||||
{sfDomainID, soeOPTIONAL} // order book directories
|
||||
}))
|
||||
|
||||
/** The ledger object which lists details about amendments on the network.
|
||||
@@ -343,6 +344,7 @@ LEDGER_ENTRY(ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID, 0x0074, XChainOwnedCreateAc
|
||||
*/
|
||||
LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfSequence, soeOPTIONAL},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfCondition, soeOPTIONAL},
|
||||
@@ -365,6 +367,7 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
||||
LEDGER_ENTRY(ltPAYCHAN, 0x0078, PayChannel, payment_channel, ({
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfSequence, soeOPTIONAL},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfBalance, soeREQUIRED},
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
@@ -433,6 +436,7 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
|
||||
*/
|
||||
LEDGER_ENTRY(ltORACLE, 0x0080, Oracle, oracle, ({
|
||||
{sfOwner, soeREQUIRED},
|
||||
{sfOracleDocumentID, soeOPTIONAL},
|
||||
{sfProvider, soeREQUIRED},
|
||||
{sfPriceDataSeries, soeREQUIRED},
|
||||
{sfAssetClass, soeREQUIRED},
|
||||
|
||||
@@ -253,6 +253,14 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT((*sle)[sfSourceTag] == 1);
|
||||
BEAST_EXPECT((*sle)[sfDestinationTag] == 2);
|
||||
if (features[fixIncludeKeyletFields])
|
||||
{
|
||||
BEAST_EXPECT((*sle)[sfSequence] == seq);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfSequence));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1718,6 +1726,7 @@ public:
|
||||
FeatureBitset const all{testable_amendments()};
|
||||
testWithFeats(all);
|
||||
testWithFeats(all - featureTokenEscrow);
|
||||
testTags(all - fixIncludeKeyletFields);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ class MultiSign_test : public beast::unit_test::suite
|
||||
|
||||
public:
|
||||
void
|
||||
test_noReserve(FeatureBitset features)
|
||||
testNoReserve(FeatureBitset features)
|
||||
{
|
||||
testcase("No Reserve");
|
||||
|
||||
@@ -133,7 +133,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_signerListSet(FeatureBitset features)
|
||||
testSignerListSet(FeatureBitset features)
|
||||
{
|
||||
testcase("SignerListSet");
|
||||
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_phantomSigners(FeatureBitset features)
|
||||
testPhantomSigners(FeatureBitset features)
|
||||
{
|
||||
testcase("Phantom Signers");
|
||||
|
||||
@@ -282,7 +282,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_fee(FeatureBitset features)
|
||||
testFee(FeatureBitset features)
|
||||
{
|
||||
testcase("Fee");
|
||||
|
||||
@@ -346,7 +346,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_misorderedSigners(FeatureBitset features)
|
||||
testMisorderedSigners(FeatureBitset features)
|
||||
{
|
||||
testcase("Misordered Signers");
|
||||
|
||||
@@ -374,7 +374,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_masterSigners(FeatureBitset features)
|
||||
testMasterSigners(FeatureBitset features)
|
||||
{
|
||||
testcase("Master Signers");
|
||||
|
||||
@@ -429,7 +429,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_regularSigners(FeatureBitset features)
|
||||
testRegularSigners(FeatureBitset features)
|
||||
{
|
||||
testcase("Regular Signers");
|
||||
|
||||
@@ -494,7 +494,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_regularSignersUsingSubmitMulti(FeatureBitset features)
|
||||
testRegularSignersUsingSubmitMulti(FeatureBitset features)
|
||||
{
|
||||
testcase("Regular Signers Using submit_multisigned");
|
||||
|
||||
@@ -734,7 +734,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_heterogeneousSigners(FeatureBitset features)
|
||||
testHeterogeneousSigners(FeatureBitset features)
|
||||
{
|
||||
testcase("Heterogenious Signers");
|
||||
|
||||
@@ -881,7 +881,7 @@ public:
|
||||
// We want to always leave an account signable. Make sure the that we
|
||||
// disallow removing the last way a transaction may be signed.
|
||||
void
|
||||
test_keyDisable(FeatureBitset features)
|
||||
testKeyDisable(FeatureBitset features)
|
||||
{
|
||||
testcase("Key Disable");
|
||||
|
||||
@@ -963,7 +963,7 @@ public:
|
||||
// Verify that the first regular key can be made for free using the
|
||||
// master key, but not when multisigning.
|
||||
void
|
||||
test_regKey(FeatureBitset features)
|
||||
testRegKey(FeatureBitset features)
|
||||
{
|
||||
testcase("Regular Key");
|
||||
|
||||
@@ -1000,7 +1000,7 @@ public:
|
||||
|
||||
// See if every kind of transaction can be successfully multi-signed.
|
||||
void
|
||||
test_txTypes(FeatureBitset features)
|
||||
testTxTypes(FeatureBitset features)
|
||||
{
|
||||
testcase("Transaction Types");
|
||||
|
||||
@@ -1089,7 +1089,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_badSignatureText(FeatureBitset features)
|
||||
testBadSignatureText(FeatureBitset features)
|
||||
{
|
||||
testcase("Bad Signature Text");
|
||||
|
||||
@@ -1285,7 +1285,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_noMultiSigners(FeatureBitset features)
|
||||
testNoMultiSigners(FeatureBitset features)
|
||||
{
|
||||
testcase("No Multisigners");
|
||||
|
||||
@@ -1304,7 +1304,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_multisigningMultisigner(FeatureBitset features)
|
||||
testMultisigningMultisigner(FeatureBitset features)
|
||||
{
|
||||
testcase("Multisigning multisigner");
|
||||
|
||||
@@ -1381,7 +1381,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_signForHash(FeatureBitset features)
|
||||
testSignForHash(FeatureBitset features)
|
||||
{
|
||||
testcase("sign_for Hash");
|
||||
|
||||
@@ -1464,7 +1464,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_amendmentTransition()
|
||||
testAmendmentTransition()
|
||||
{
|
||||
testcase("Amendment Transition");
|
||||
|
||||
@@ -1559,7 +1559,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_signersWithTickets(FeatureBitset features)
|
||||
testSignersWithTickets(FeatureBitset features)
|
||||
{
|
||||
testcase("Signers With Tickets");
|
||||
|
||||
@@ -1600,7 +1600,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_signersWithTags(FeatureBitset features)
|
||||
testSignersWithTags(FeatureBitset features)
|
||||
{
|
||||
if (!features[featureExpandedSignerList])
|
||||
return;
|
||||
@@ -1680,7 +1680,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
test_signerListSetFlags(FeatureBitset features)
|
||||
testSignerListSetFlags(FeatureBitset features)
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
@@ -1702,27 +1702,57 @@ public:
|
||||
env.close();
|
||||
}
|
||||
|
||||
void
|
||||
testSignerListObject(FeatureBitset features)
|
||||
{
|
||||
testcase("SignerList Object");
|
||||
|
||||
// Verify that the SignerList object is created correctly.
|
||||
using namespace jtx;
|
||||
Env env{*this, features};
|
||||
Account const alice{"alice", KeyType::ed25519};
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
|
||||
// Attach phantom signers to alice.
|
||||
env(signers(alice, 1, {{bogie, 1}, {demon, 1}}));
|
||||
env.close();
|
||||
|
||||
// Verify that the SignerList object was created correctly.
|
||||
auto const& sle = env.le(keylet::signers(alice.id()));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->getFieldArray(sfSignerEntries).size() == 2);
|
||||
if (features[fixIncludeKeyletFields])
|
||||
{
|
||||
BEAST_EXPECT((*sle)[sfOwner] == alice.id());
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfOwner));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testAll(FeatureBitset features)
|
||||
{
|
||||
test_noReserve(features);
|
||||
test_signerListSet(features);
|
||||
test_phantomSigners(features);
|
||||
test_fee(features);
|
||||
test_misorderedSigners(features);
|
||||
test_masterSigners(features);
|
||||
test_regularSigners(features);
|
||||
test_regularSignersUsingSubmitMulti(features);
|
||||
test_heterogeneousSigners(features);
|
||||
test_keyDisable(features);
|
||||
test_regKey(features);
|
||||
test_txTypes(features);
|
||||
test_badSignatureText(features);
|
||||
test_noMultiSigners(features);
|
||||
test_multisigningMultisigner(features);
|
||||
test_signForHash(features);
|
||||
test_signersWithTickets(features);
|
||||
test_signersWithTags(features);
|
||||
testNoReserve(features);
|
||||
testSignerListSet(features);
|
||||
testPhantomSigners(features);
|
||||
testFee(features);
|
||||
testMisorderedSigners(features);
|
||||
testMasterSigners(features);
|
||||
testRegularSigners(features);
|
||||
testRegularSignersUsingSubmitMulti(features);
|
||||
testHeterogeneousSigners(features);
|
||||
testKeyDisable(features);
|
||||
testRegKey(features);
|
||||
testTxTypes(features);
|
||||
testBadSignatureText(features);
|
||||
testNoMultiSigners(features);
|
||||
testMultisigningMultisigner(features);
|
||||
testSignForHash(features);
|
||||
testSignersWithTickets(features);
|
||||
testSignersWithTags(features);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1739,10 +1769,13 @@ public:
|
||||
testAll(all - featureExpandedSignerList);
|
||||
testAll(all);
|
||||
|
||||
test_signerListSetFlags(all - fixInvalidTxFlags);
|
||||
test_signerListSetFlags(all);
|
||||
testSignerListSetFlags(all - fixInvalidTxFlags);
|
||||
testSignerListSetFlags(all);
|
||||
|
||||
test_amendmentTransition();
|
||||
testSignerListObject(all - fixIncludeKeyletFields);
|
||||
testSignerListObject(all);
|
||||
|
||||
testAmendmentTransition();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
testCreate()
|
||||
testCreate(FeatureBitset features)
|
||||
{
|
||||
testcase("Create");
|
||||
using namespace jtx;
|
||||
@@ -413,18 +413,30 @@ private:
|
||||
env, {.owner = owner, .series = series, .fee = baseFee});
|
||||
BEAST_EXPECT(oracle.exists());
|
||||
BEAST_EXPECT(ownerCount(env, owner) == (count + adj));
|
||||
auto const entry = oracle.ledgerEntry();
|
||||
BEAST_EXPECT(entry[jss::node][jss::Owner] == owner.human());
|
||||
if (features[fixIncludeKeyletFields])
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
entry[jss::node][jss::OracleDocumentID] ==
|
||||
oracle.documentID());
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(!entry[jss::node].isMember(jss::OracleDocumentID));
|
||||
}
|
||||
BEAST_EXPECT(oracle.expectLastUpdateTime(946694810));
|
||||
};
|
||||
|
||||
{
|
||||
// owner count is adjusted by 1
|
||||
Env env(*this);
|
||||
Env env(*this, features);
|
||||
test(env, {{"XRP", "USD", 740, 1}}, 1);
|
||||
}
|
||||
|
||||
{
|
||||
// owner count is adjusted by 2
|
||||
Env env(*this);
|
||||
Env env(*this, features);
|
||||
test(
|
||||
env,
|
||||
{{"XRP", "USD", 740, 1},
|
||||
@@ -438,7 +450,7 @@ private:
|
||||
|
||||
{
|
||||
// Different owner creates a new object
|
||||
Env env(*this);
|
||||
Env env(*this, features);
|
||||
auto const baseFee =
|
||||
static_cast<int>(env.current()->fees().base.drops());
|
||||
Account const some("some");
|
||||
@@ -864,7 +876,8 @@ public:
|
||||
auto const all = testable_amendments();
|
||||
testInvalidSet();
|
||||
testInvalidDelete();
|
||||
testCreate();
|
||||
testCreate(all);
|
||||
testCreate(all - fixIncludeKeyletFields);
|
||||
testDelete();
|
||||
testUpdate();
|
||||
testAmendment();
|
||||
|
||||
@@ -1852,6 +1852,14 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1);
|
||||
BEAST_EXPECT(!inOwnerDir(*env.current(), bob, chanSle));
|
||||
BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0);
|
||||
if (features[fixIncludeKeyletFields])
|
||||
{
|
||||
BEAST_EXPECT((*chanSle)[sfSequence] == env.seq(alice) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(!chanSle->isFieldPresent(sfSequence));
|
||||
}
|
||||
// close the channel
|
||||
env(claim(bob, chan), txflags(tfClose));
|
||||
BEAST_EXPECT(!channelExists(*env.current(), chan));
|
||||
@@ -2348,6 +2356,7 @@ public:
|
||||
testWithFeats(all - disallowIncoming);
|
||||
testWithFeats(all);
|
||||
testDepositAuthCreds();
|
||||
testMetaAndOwnership(all - fixIncludeKeyletFields);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -317,10 +317,10 @@ Oracle::ledgerEntry(
|
||||
|
||||
if (jr.isObject())
|
||||
{
|
||||
if (jr.isMember(jss::error))
|
||||
return jr;
|
||||
if (jr.isMember(jss::result) && jr[jss::result].isMember(jss::status))
|
||||
return jr[jss::result];
|
||||
else if (jr.isMember(jss::error))
|
||||
return jr;
|
||||
}
|
||||
return Json::nullValue;
|
||||
}
|
||||
|
||||
@@ -538,6 +538,11 @@ EscrowCreate::doApply()
|
||||
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
|
||||
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
|
||||
|
||||
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
||||
{
|
||||
(*slep)[sfSequence] = ctx_.tx.getSeqValue();
|
||||
}
|
||||
|
||||
if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
|
||||
{
|
||||
auto const xferRate = transferRate(ctx_.view(), amount);
|
||||
|
||||
@@ -286,6 +286,10 @@ PayChanCreate::doApply()
|
||||
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
|
||||
(*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
|
||||
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
|
||||
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
||||
{
|
||||
(*slep)[sfSequence] = ctx_.tx.getSeqValue();
|
||||
}
|
||||
|
||||
ctx_.view().insert(slep);
|
||||
|
||||
|
||||
@@ -271,6 +271,11 @@ SetOracle::doApply()
|
||||
if (ctx_.tx.isFieldPresent(sfURI))
|
||||
sle->setFieldVL(sfURI, ctx_.tx[sfURI]);
|
||||
sle->setFieldU32(sfLastUpdateTime, ctx_.tx[sfLastUpdateTime]);
|
||||
if (!sle->isFieldPresent(sfOracleDocumentID) &&
|
||||
ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
||||
{
|
||||
(*sle)[sfOracleDocumentID] = ctx_.tx[sfOracleDocumentID];
|
||||
}
|
||||
|
||||
auto const newCount = pairs.size() > 5 ? 2 : 1;
|
||||
auto const adjust = newCount - oldCount;
|
||||
@@ -285,6 +290,10 @@ SetOracle::doApply()
|
||||
|
||||
sle = std::make_shared<SLE>(oracleID);
|
||||
sle->setAccountID(sfOwner, ctx_.tx.getAccountID(sfAccount));
|
||||
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
||||
{
|
||||
(*sle)[sfOracleDocumentID] = ctx_.tx[sfOracleDocumentID];
|
||||
}
|
||||
sle->setFieldVL(sfProvider, ctx_.tx[sfProvider]);
|
||||
if (ctx_.tx.isFieldPresent(sfURI))
|
||||
sle->setFieldVL(sfURI, ctx_.tx[sfURI]);
|
||||
|
||||
@@ -37,7 +37,7 @@ 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;
|
||||
static std::uint32_t const DEFAULT_SIGNER_LIST_ID = 0;
|
||||
|
||||
std::tuple<
|
||||
NotTEC,
|
||||
@@ -424,8 +424,12 @@ SetSignerList::writeSignersToSLE(
|
||||
std::uint32_t flags) const
|
||||
{
|
||||
// Assign the quorum, default SignerListID, and flags.
|
||||
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
||||
{
|
||||
ledgerEntry->setAccountID(sfOwner, account_);
|
||||
}
|
||||
ledgerEntry->setFieldU32(sfSignerQuorum, quorum_);
|
||||
ledgerEntry->setFieldU32(sfSignerListID, defaultSignerListID_);
|
||||
ledgerEntry->setFieldU32(sfSignerListID, DEFAULT_SIGNER_LIST_ID);
|
||||
if (flags) // Only set flags if they are non-default (default is zero).
|
||||
ledgerEntry->setFieldU32(sfFlags, flags);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user