mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Implement enhanced Ticket support:
Tickets are a mechanism to allow for the "out-of-order" execution of transactions on the XRP Ledger. This commit, if merged, reworks the existing support for tickets and introduces support for 'ticket batching', completing the feature set needed for tickets. The code is gated under the newly-introduced `TicketBatch` amendment and the `Tickets` amendment, which is not presently active on the network, is being removed. The specification for this change can be found at: https://github.com/xrp-community/standards-drafts/issues/16
This commit is contained in:
committed by
Nik Bougalis
parent
01bd5a2646
commit
7724cca384
@@ -92,7 +92,6 @@ detail::supportedAmendments()
|
||||
// Removing them will cause servers to become amendment blocked.
|
||||
static std::vector<std::string> const supported{
|
||||
"MultiSign", // Unconditionally supported.
|
||||
// "Tickets",
|
||||
"TrustSetAuth", // Unconditionally supported.
|
||||
"FeeEscalation", // Unconditionally supported.
|
||||
// "OwnerPaysFee",
|
||||
@@ -132,7 +131,8 @@ detail::supportedAmendments()
|
||||
"fix1781",
|
||||
"HardenedValidations",
|
||||
"fixAmendmentMajorityCalc",
|
||||
//"NegativeUNL" // Commented out to prevent automatic enablement
|
||||
//"NegativeUNL", // Commented out to prevent automatic enablement
|
||||
//"TicketBatch", // Commented out to prevent automatic enablement
|
||||
};
|
||||
return supported;
|
||||
}
|
||||
@@ -160,7 +160,6 @@ bitsetIndexToFeature(size_t i)
|
||||
// clang-format off
|
||||
|
||||
uint256 const
|
||||
featureTickets = *getRegisteredFeature("Tickets"),
|
||||
featureOwnerPaysFee = *getRegisteredFeature("OwnerPaysFee"),
|
||||
featureFlow = *getRegisteredFeature("Flow"),
|
||||
featureCompareTakerFlowCross = *getRegisteredFeature("CompareTakerFlowCross"),
|
||||
@@ -186,7 +185,8 @@ uint256 const
|
||||
fix1781 = *getRegisteredFeature("fix1781"),
|
||||
featureHardenedValidations = *getRegisteredFeature("HardenedValidations"),
|
||||
fixAmendmentMajorityCalc = *getRegisteredFeature("fixAmendmentMajorityCalc"),
|
||||
featureNegativeUNL = *getRegisteredFeature("NegativeUNL");
|
||||
featureNegativeUNL = *getRegisteredFeature("NegativeUNL"),
|
||||
featureTicketBatch = *getRegisteredFeature("TicketBatch");
|
||||
|
||||
// The following amendments have been active for at least two years. Their
|
||||
// pre-amendment code has been removed and the identifiers are deprecated.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/SeqProxy.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <algorithm>
|
||||
@@ -107,10 +108,17 @@ getQuality(uint256 const& uBase)
|
||||
}
|
||||
|
||||
uint256
|
||||
getTicketIndex(AccountID const& account, std::uint32_t uSequence)
|
||||
getTicketIndex(AccountID const& account, std::uint32_t ticketSeq)
|
||||
{
|
||||
return indexHash(
|
||||
LedgerNameSpace::TICKET, account, std::uint32_t(uSequence));
|
||||
LedgerNameSpace::TICKET, account, std::uint32_t(ticketSeq));
|
||||
}
|
||||
|
||||
uint256
|
||||
getTicketIndex(AccountID const& account, SeqProxy ticketSeq)
|
||||
{
|
||||
assert(ticketSeq.isTicket());
|
||||
return getTicketIndex(account, ticketSeq.value());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -238,9 +246,15 @@ next_t::operator()(Keylet const& k) const
|
||||
}
|
||||
|
||||
Keylet
|
||||
ticket_t::operator()(AccountID const& id, std::uint32_t seq) const
|
||||
ticket_t::operator()(AccountID const& id, std::uint32_t ticketSeq) const
|
||||
{
|
||||
return {ltTICKET, getTicketIndex(id, seq)};
|
||||
return {ltTICKET, getTicketIndex(id, ticketSeq)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
ticket_t::operator()(AccountID const& id, SeqProxy ticketSeq) const
|
||||
{
|
||||
return {ltTICKET, getTicketIndex(id, ticketSeq)};
|
||||
}
|
||||
|
||||
// This function is presently static, since it's never accessed from anywhere
|
||||
|
||||
@@ -53,6 +53,7 @@ LedgerFormats::LedgerFormats()
|
||||
{sfTransferRate, soeOPTIONAL},
|
||||
{sfDomain, soeOPTIONAL},
|
||||
{sfTickSize, soeOPTIONAL},
|
||||
{sfTicketCount, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -155,10 +156,10 @@ LedgerFormats::LedgerFormats()
|
||||
ltTICKET,
|
||||
{
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfSequence, soeREQUIRED},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfTarget, soeOPTIONAL},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
{sfTicketSequence, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
|
||||
@@ -117,6 +117,8 @@ SF_U32 const sfCancelAfter(access, STI_UINT32, 36, "CancelAfter");
|
||||
SF_U32 const sfFinishAfter(access, STI_UINT32, 37, "FinishAfter");
|
||||
SF_U32 const sfSignerListID(access, STI_UINT32, 38, "SignerListID");
|
||||
SF_U32 const sfSettleDelay(access, STI_UINT32, 39, "SettleDelay");
|
||||
SF_U32 const sfTicketCount(access, STI_UINT32, 40, "TicketCount");
|
||||
SF_U32 const sfTicketSequence(access, STI_UINT32, 41, "TicketSequence");
|
||||
|
||||
// 64-bit integers
|
||||
SF_U64 const sfIndexNext(access, STI_UINT64, 1, "IndexNext");
|
||||
@@ -162,7 +164,7 @@ SF_U256 const sfBookDirectory(access, STI_HASH256, 16, "BookDirectory");
|
||||
SF_U256 const sfInvoiceID(access, STI_HASH256, 17, "InvoiceID");
|
||||
SF_U256 const sfNickname(access, STI_HASH256, 18, "Nickname");
|
||||
SF_U256 const sfAmendment(access, STI_HASH256, 19, "Amendment");
|
||||
SF_U256 const sfTicketID(access, STI_HASH256, 20, "TicketID");
|
||||
// 20 is currently unused
|
||||
SF_U256 const sfDigest(access, STI_HASH256, 21, "Digest");
|
||||
SF_U256 const sfPayChannel(access, STI_HASH256, 22, "Channel");
|
||||
SF_U256 const sfConsensusHash(access, STI_HASH256, 23, "ConsensusHash");
|
||||
@@ -234,7 +236,7 @@ SF_Account const sfDestination(access, STI_ACCOUNT, 3, "Destination");
|
||||
SF_Account const sfIssuer(access, STI_ACCOUNT, 4, "Issuer");
|
||||
SF_Account const sfAuthorize(access, STI_ACCOUNT, 5, "Authorize");
|
||||
SF_Account const sfUnauthorize(access, STI_ACCOUNT, 6, "Unauthorize");
|
||||
SF_Account const sfTarget(access, STI_ACCOUNT, 7, "Target");
|
||||
// 7 is currently unused
|
||||
SF_Account const sfRegularKey(access, STI_ACCOUNT, 8, "RegularKey");
|
||||
|
||||
// path set
|
||||
|
||||
@@ -160,6 +160,22 @@ STTx::getSignature() const
|
||||
}
|
||||
}
|
||||
|
||||
SeqProxy
|
||||
STTx::getSeqProxy() const
|
||||
{
|
||||
std::uint32_t const seq{getFieldU32(sfSequence)};
|
||||
if (seq != 0)
|
||||
return SeqProxy::sequence(seq);
|
||||
|
||||
boost::optional<std::uint32_t> const ticketSeq{operator[](
|
||||
~sfTicketSequence)};
|
||||
if (!ticketSeq)
|
||||
// No TicketSequence specified. Return the Sequence, whatever it is.
|
||||
return SeqProxy::sequence(seq);
|
||||
|
||||
return SeqProxy{SeqProxy::ticket, *ticketSeq};
|
||||
}
|
||||
|
||||
void
|
||||
STTx::sign(PublicKey const& publicKey, SecretKey const& secretKey)
|
||||
{
|
||||
@@ -250,8 +266,8 @@ STTx::getMetaSQL(
|
||||
|
||||
return str(
|
||||
boost::format(bfTrans) % to_string(getTransactionID()) %
|
||||
format->getName() % toBase58(getAccountID(sfAccount)) % getSequence() %
|
||||
inLedger % status % rTxn % escapedMetaData);
|
||||
format->getName() % toBase58(getAccountID(sfAccount)) %
|
||||
getFieldU32(sfSequence) % inLedger % status % rTxn % escapedMetaData);
|
||||
}
|
||||
|
||||
std::pair<bool, std::string>
|
||||
|
||||
@@ -100,6 +100,7 @@ transResults()
|
||||
MAKE_ERROR(tefBAD_AUTH_MASTER, "Auth for unclaimed account needs correct master key."),
|
||||
MAKE_ERROR(tefINVARIANT_FAILED, "Fee claim violated invariants for the transaction."),
|
||||
MAKE_ERROR(tefTOO_BIG, "Transaction affects too many items."),
|
||||
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
|
||||
|
||||
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
|
||||
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
|
||||
@@ -150,6 +151,7 @@ transResults()
|
||||
MAKE_ERROR(temBAD_TICK_SIZE, "Malformed: Tick size out of range."),
|
||||
MAKE_ERROR(temINVALID_ACCOUNT_ID, "Malformed: A field contains an invalid account ID."),
|
||||
MAKE_ERROR(temCANNOT_PREAUTH_SELF, "Malformed: An account may not preauthorize itself."),
|
||||
MAKE_ERROR(temINVALID_COUNT, "Malformed: Count field outside valid range."),
|
||||
|
||||
MAKE_ERROR(terRETRY, "Retry transaction."),
|
||||
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),
|
||||
@@ -162,6 +164,7 @@ transResults()
|
||||
MAKE_ERROR(terPRE_SEQ, "Missing/inapplicable prior transaction."),
|
||||
MAKE_ERROR(terOWNERS, "Non-zero owner count."),
|
||||
MAKE_ERROR(terQUEUED, "Held until escalated fee drops."),
|
||||
{terPRE_TICKET, {"terPRE_TICKET", "Ticket is not yet in ledger."}},
|
||||
|
||||
MAKE_ERROR(tesSUCCESS, "The transaction was applied. Only final in a validated ledger."),
|
||||
};
|
||||
|
||||
@@ -54,6 +54,7 @@ TxFormats::TxFormats()
|
||||
{sfSetFlag, soeOPTIONAL},
|
||||
{sfClearFlag, soeOPTIONAL},
|
||||
{sfTickSize, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -63,6 +64,7 @@ TxFormats::TxFormats()
|
||||
{sfLimitAmount, soeOPTIONAL},
|
||||
{sfQualityIn, soeOPTIONAL},
|
||||
{sfQualityOut, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -73,6 +75,7 @@ TxFormats::TxFormats()
|
||||
{sfTakerGets, soeREQUIRED},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
{sfOfferSequence, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -80,6 +83,7 @@ TxFormats::TxFormats()
|
||||
ttOFFER_CANCEL,
|
||||
{
|
||||
{sfOfferSequence, soeREQUIRED},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -87,6 +91,7 @@ TxFormats::TxFormats()
|
||||
ttREGULAR_KEY_SET,
|
||||
{
|
||||
{sfRegularKey, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -100,6 +105,7 @@ TxFormats::TxFormats()
|
||||
{sfInvoiceID, soeOPTIONAL},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfDeliverMin, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -112,6 +118,7 @@ TxFormats::TxFormats()
|
||||
{sfCancelAfter, soeOPTIONAL},
|
||||
{sfFinishAfter, soeOPTIONAL},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -122,6 +129,7 @@ TxFormats::TxFormats()
|
||||
{sfOfferSequence, soeREQUIRED},
|
||||
{sfFulfillment, soeOPTIONAL},
|
||||
{sfCondition, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -130,6 +138,7 @@ TxFormats::TxFormats()
|
||||
{
|
||||
{sfOwner, soeREQUIRED},
|
||||
{sfOfferSequence, soeREQUIRED},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -164,15 +173,8 @@ TxFormats::TxFormats()
|
||||
add(jss::TicketCreate,
|
||||
ttTICKET_CREATE,
|
||||
{
|
||||
{sfTarget, soeOPTIONAL},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::TicketCancel,
|
||||
ttTICKET_CANCEL,
|
||||
{
|
||||
{sfTicketID, soeREQUIRED},
|
||||
{sfTicketCount, soeREQUIRED},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -183,6 +185,7 @@ TxFormats::TxFormats()
|
||||
{
|
||||
{sfSignerQuorum, soeREQUIRED},
|
||||
{sfSignerEntries, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -195,6 +198,7 @@ TxFormats::TxFormats()
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfCancelAfter, soeOPTIONAL},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -204,6 +208,7 @@ TxFormats::TxFormats()
|
||||
{sfPayChannel, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -215,6 +220,7 @@ TxFormats::TxFormats()
|
||||
{sfBalance, soeOPTIONAL},
|
||||
{sfSignature, soeOPTIONAL},
|
||||
{sfPublicKey, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -226,6 +232,7 @@ TxFormats::TxFormats()
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfInvoiceID, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -235,6 +242,7 @@ TxFormats::TxFormats()
|
||||
{sfCheckID, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
{sfDeliverMin, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -242,6 +250,7 @@ TxFormats::TxFormats()
|
||||
ttCHECK_CANCEL,
|
||||
{
|
||||
{sfCheckID, soeREQUIRED},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -250,6 +259,7 @@ TxFormats::TxFormats()
|
||||
{
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -258,6 +268,7 @@ TxFormats::TxFormats()
|
||||
{
|
||||
{sfAuthorize, soeOPTIONAL},
|
||||
{sfUnauthorize, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user