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:
Scott Schurr
2018-10-18 18:43:02 -07:00
committed by Nik Bougalis
parent 01bd5a2646
commit 7724cca384
101 changed files with 6337 additions and 2287 deletions

View File

@@ -416,6 +416,15 @@ populateSignerQuorum(T& to, STObject const& from)
populateProtoPrimitive(
[&to]() { return to.mutable_signer_quorum(); }, from, sfSignerQuorum);
}
template <class T>
void
populateTicketCount(T& to, STObject const& from)
{
populateProtoPrimitive(
[&to]() { return to.mutable_count(); }, from, sfTicketCount);
}
template <class T>
void
populateLimitAmount(T& to, STObject const& from)
@@ -737,6 +746,16 @@ populateSignerListID(T& to, STObject const& from)
[&to]() { return to.mutable_signer_list_id(); }, from, sfSignerListID);
}
template <class T>
void
populateTicketSequence(T& to, STObject const& from)
{
populateProtoPrimitive(
[&to]() { return to.mutable_ticket_sequence(); },
from,
sfTicketSequence);
}
template <class T>
void
populateHashes(T& to, STObject const& from)
@@ -1143,6 +1162,12 @@ convert(org::xrpl::rpc::v1::SignerListSet& to, STObject const& from)
populateSignerEntries(to, from);
}
void
convert(org::xrpl::rpc::v1::TicketCreate& to, STObject const& from)
{
populateTicketCount(to, from);
}
void
convert(org::xrpl::rpc::v1::TrustSet& to, STObject const& from)
{
@@ -1474,6 +1499,22 @@ convert(org::xrpl::rpc::v1::NegativeUNL& to, STObject const& from)
populateFlags(to, from);
}
void
convert(org::xrpl::rpc::v1::TicketObject& to, STObject const& from)
{
populateAccount(to, from);
populateFlags(to, from);
populateOwnerNode(to, from);
populatePreviousTransactionID(to, from);
populatePreviousTransactionLedgerSequence(to, from);
populateTicketSequence(to, from);
}
void
setLedgerEntryType(
org::xrpl::rpc::v1::AffectedNode& proto,
@@ -1533,6 +1574,10 @@ setLedgerEntryType(
proto.set_ledger_entry_type(
org::xrpl::rpc::v1::LEDGER_ENTRY_TYPE_NEGATIVE_UNL);
break;
case ltTICKET:
proto.set_ledger_entry_type(
org::xrpl::rpc::v1::LEDGER_ENTRY_TYPE_TICKET);
break;
}
}
@@ -1581,6 +1626,9 @@ convert(T& to, STObject& from, std::uint16_t type)
case ltNEGATIVE_UNL:
RPC::convert(*to.mutable_negative_unl(), from);
break;
case ltTICKET:
RPC::convert(*to.mutable_ticket(), from);
break;
}
}
@@ -1698,55 +1746,72 @@ convert(org::xrpl::rpc::v1::Meta& to, std::shared_ptr<TxMeta> const& from)
void
convert(
org::xrpl::rpc::v1::QueueData& to,
std::map<TxSeq, TxQ::AccountTxDetails const> const& from)
std::vector<TxQ::TxDetails> const& from)
{
if (!from.empty())
{
to.set_txn_count(from.size());
to.set_lowest_sequence(from.begin()->first);
to.set_highest_sequence(from.rbegin()->first);
boost::optional<bool> anyAuthChanged(false);
boost::optional<XRPAmount> totalSpend(0);
std::uint32_t seqCount = 0;
std::uint32_t ticketCount = 0;
boost::optional<std::uint32_t> lowestSeq;
boost::optional<std::uint32_t> highestSeq;
boost::optional<std::uint32_t> lowestTicket;
boost::optional<std::uint32_t> highestTicket;
bool anyAuthChanged = false;
XRPAmount totalSpend(0);
for (auto const& [txSeq, txDetails] : from)
for (auto const& tx : from)
{
org::xrpl::rpc::v1::QueuedTransaction& qt = *to.add_transactions();
qt.mutable_sequence()->set_value(txSeq);
qt.set_fee_level(txDetails.feeLevel.fee());
if (txDetails.lastValid)
qt.mutable_last_ledger_sequence()->set_value(
*txDetails.lastValid);
if (txDetails.consequences)
if (tx.seqProxy.isSeq())
{
qt.mutable_fee()->set_drops(
txDetails.consequences->fee.drops());
auto spend = txDetails.consequences->potentialSpend +
txDetails.consequences->fee;
qt.mutable_max_spend_drops()->set_drops(spend.drops());
if (totalSpend)
*totalSpend += spend;
auto authChanged =
txDetails.consequences->category == TxConsequences::blocker;
if (authChanged)
anyAuthChanged.emplace(authChanged);
qt.set_auth_change(authChanged);
qt.mutable_sequence()->set_value(tx.seqProxy.value());
++seqCount;
if (!lowestSeq)
lowestSeq = tx.seqProxy.value();
highestSeq = tx.seqProxy.value();
}
else
{
if (anyAuthChanged && !*anyAuthChanged)
anyAuthChanged.reset();
totalSpend.reset();
qt.mutable_ticket()->set_value(tx.seqProxy.value());
++ticketCount;
if (!lowestTicket)
lowestTicket = tx.seqProxy.value();
highestTicket = tx.seqProxy.value();
}
qt.set_fee_level(tx.feeLevel.fee());
if (tx.lastValid)
qt.mutable_last_ledger_sequence()->set_value(*tx.lastValid);
qt.mutable_fee()->set_drops(tx.consequences.fee().drops());
auto const spend =
tx.consequences.potentialSpend() + tx.consequences.fee();
qt.mutable_max_spend_drops()->set_drops(spend.drops());
totalSpend += spend;
bool const authChanged = tx.consequences.isBlocker();
if (authChanged)
anyAuthChanged = true;
qt.set_auth_change(authChanged);
}
if (anyAuthChanged)
to.set_auth_change_queued(*anyAuthChanged);
if (totalSpend)
to.mutable_max_spend_drops_total()->set_drops(
(*totalSpend).drops());
if (seqCount)
to.set_sequence_count(seqCount);
if (ticketCount)
to.set_ticket_count(ticketCount);
if (lowestSeq)
to.set_lowest_sequence(*lowestSeq);
if (highestSeq)
to.set_highest_sequence(*highestSeq);
if (lowestTicket)
to.set_lowest_ticket(*lowestTicket);
if (highestTicket)
to.set_highest_ticket(*highestTicket);
to.set_auth_change_queued(anyAuthChanged);
to.mutable_max_spend_drops_total()->set_drops(totalSpend.drops());
}
}
@@ -1779,6 +1844,8 @@ convert(
populateSigners(to, fromObj);
populateTicketSequence(to, fromObj);
auto type = safe_cast<TxType>(fromObj.getFieldU16(sfTransactionType));
switch (type)
@@ -1837,6 +1904,9 @@ convert(
case TxType::ttACCOUNT_DELETE:
convert(*to.mutable_account_delete(), fromObj);
break;
case TxType::ttTICKET_CREATE:
convert(*to.mutable_ticket_create(), fromObj);
break;
default:
break;
}