Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion include/xrpl/protocol/jss.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,9 @@ JSS(previous); // out: Reservations
JSS(previous_ledger); // out: LedgerPropose
JSS(price); // out: amm_info, AuctionSlot
JSS(proof); // in: BookOffers
JSS(proposal); // out: TransactionProposal
JSS(proposal_id); // in/out: TransactionProposal
JSS(proposal_status); // out: TransactionProposal
JSS(propose_seq); // out: LedgerPropose
JSS(proposers); // out: NetworkOPs, LedgerConsensus
JSS(protocol); // out: NetworkOPs, PeerImp
Expand All @@ -502,6 +505,7 @@ JSS(queue); // in: AccountInfo
JSS(queue_data); // out: AccountInfo
JSS(queued); // out: SubmitTransaction
JSS(queued_duration_us); //
JSS(quorum); // out: TransactionProposal
JSS(quote_asset); // in: get_aggregate_price
JSS(random); // out: Random
JSS(raw_meta); // out: AcceptedLedgerTx
Expand All @@ -524,7 +528,7 @@ JSS(result); // RPC
JSS(ripple_lines); // out: NetworkOPs
JSS(ripple_state); // in: LedgerEntr
JSS(ripplerpc); // XRPL RPC version
JSS(role); // out: Ping.cpp
JSS(role); // out: Ping.cpp, TransactionProposal
JSS(rpc); //
JSS(rt_accounts); // in: Subscribe, Unsubscribe
JSS(running_duration_us); //
Expand Down Expand Up @@ -552,8 +556,10 @@ JSS(shares); // out: VaultInfo
JSS(signature); // out: NetworkOPs, ChannelAuthorize
JSS(signature_target); // in: TransactionSign
JSS(signature_verified); // out: ChannelVerify
JSS(signed_weight); // out: TransactionProposal
JSS(signing_key); // out: NetworkOPs
JSS(signing_keys); // out: ValidatorList
JSS(signing_status); // out: TransactionProposal
JSS(signing_time); // out: NetworkOPs
JSS(signer_lists); // in/out: AccountInfo
JSS(size); // out: get_aggregate_price
Expand Down
33 changes: 19 additions & 14 deletions include/xrpl/tx/Transactor.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,25 @@ class Transactor
static NotTEC
checkSign(PreclaimContext const& ctx);

// Whether sigObject's signature fields currently authorize idAccount on
// this view. Public because it answers a pure ledger-state question, so
// read-only callers (e.g. the transaction_proposal RPC reporting a
// proposal's completeness) share one authorization rule with the
// transaction path and the two cannot drift.
static NotTEC
checkSign(
ReadView const& view,
ApplyFlags flags,
std::optional<uint256 const> const& parentBatchId,
AccountID const& idAccount,
STObject const& sigObject,
beast::Journal const j,
// A batch may carry an inner from an account that an earlier inner
// creates, so the signer account need not exist yet; when it does not,
// only its own master key may authorize it. Normal transactions require
// the account to already exist.
bool permitUncreatedAccount = false);

// Returns the fee in fee units, not scaled for load.
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
Expand Down Expand Up @@ -418,20 +437,6 @@ class Transactor
static XRPAmount
calculateOwnerReserveFee(ReadView const& view, STTx const& tx);

static NotTEC
checkSign(
ReadView const& view,
ApplyFlags flags,
std::optional<uint256 const> const& parentBatchId,
AccountID const& idAccount,
STObject const& sigObject,
beast::Journal const j,
// A batch may carry an inner from an account that an earlier inner
// creates, so the signer account need not exist yet; when it does not,
// only its own master key may authorize it. Normal transactions require
// the account to already exist.
bool permitUncreatedAccount = false);

// Base class always returns true
static bool
checkExtraFeatures(PreflightContext const& ctx);
Expand Down
99 changes: 99 additions & 0 deletions include/xrpl/tx/transactors/proposal/ProposalHelpers.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
#pragma once

#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>

#include <cstdint>
#include <optional>
#include <vector>

namespace xrpl::proposal {

Expand Down Expand Up @@ -71,4 +79,95 @@ proposalOwnerCount(STObject const& proposedTx)
: kProposalOwnerCount;
}

/**
* Delete a TransactionProposal ledger entry.
*
* Removes the entry from its Owner's directory, releases the reserve the
* proposal holds against the Owner, and erases the entry. Shared by every
* deletion path the spec defines (XLS-0103 §4.5): automatic cleanup when the
* proposed transaction's TicketSequence is consumed, and the
* TransactionProposalCancel / TransactionProposalSign cleanup paths once
* those transactions exist.
*
* A TransactionProposal cannot carry a reserve sponsor today (its type is
* not sponsorship-supported), so the release always lands on the Owner; it
* goes through decreaseOwnerCountForObject regardless, matching ticketDelete,
* so it would follow an sfSponsor recorded on the entry if the type ever
* becomes sponsorable.
*
* @param view The apply view for making changes
* @param sleProposal The TransactionProposal ledger entry to delete
* @param j Journal for logging
* @return tesSUCCESS, or tefBAD_LEDGER if the ledger contradicts the entry
*/
TER
deleteProposal(ApplyView& view, SLE::pointer const& sleProposal, beast::Journal j);

/**
* The role in which an account's authorization is required on a proposal.
*/
enum class SignerRole : std::uint8_t {
account, ///< the proposed transaction's initiator (Account, or Delegate if present)
batchParticipant, ///< an inner transaction's initiator in a proposed Batch
counterparty, ///< a Counterparty of the proposed transaction or of an inner
sponsor, ///< a co-signing Sponsor of the proposed transaction or of an inner
};

/**
* One required authorization on a proposal and whether the signatures
* collected so far currently satisfy it.
*/
struct SignerStatus
{
AccountID account;
SignerRole role;
/// Whether the collected signature material authorizes `account` on the
/// evaluated ledger (same rule the transaction path applies at preclaim).
bool satisfied = false;
/// Multi-signature progress: the weight the collected Signers entries
/// carry against `account`'s live SignerList. Present only when the
/// collected signature object holds a Signers array.
std::optional<std::uint32_t> signedWeight;
/// `account`'s live SignerQuorum. Present only when the account has a
/// SignerList on the evaluated ledger.
std::optional<std::uint32_t> quorum;
};

/**
* Completeness state of a proposal (XLS-0103 §8.1.2). Terminal-first: an
* expired proposal reports expired even when fully signed.
*/
enum class ProposalState : std::uint8_t { pending, complete, expired };

/**
* A proposal's completeness state plus the per-account detail it derives
* from.
*/
struct ProposalStatus
{
ProposalState state = ProposalState::pending;
std::vector<SignerStatus> signers;
};

/**
* Evaluate how far a TransactionProposal's collected signatures are from a
* submittable transaction on the given ledger.
*
* Signatures stored on the proposal were cryptographically verified when they
* were appended, so this only re-checks their authorization against live
* ledger state (SignerList membership and quorum, regular-key rotation,
* disabled master keys), mirroring what Transactor::checkSign would decide at
* submission time. For a proposed Batch, each inner initiator, counterparty,
* and co-signing sponsor other than the outer account is a separate required
* authorization collected through BatchSigners, mirroring
* Batch::preflightSigValidated. A LoanSet without an explicit Counterparty
* requires the owner of its LoanBroker instead, mirroring LoanSet::checkSign.
*
* @param view The ledger to evaluate against.
* @param sleProposal A TransactionProposal ledger entry of that ledger.
* @param j Journal for logging.
*/
ProposalStatus
evaluateProposal(ReadView const& view, SLE const& sleProposal, beast::Journal j);

} // namespace xrpl::proposal
18 changes: 18 additions & 0 deletions src/libxrpl/tx/Transactor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <xrpl/tx/SignerEntries.h>
#include <xrpl/tx/apply.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/transactors/proposal/ProposalHelpers.h>

#include <algorithm>
#include <cstddef>
Expand Down Expand Up @@ -864,8 +865,25 @@ Transactor::ticketDelete(
// Update the Ticket owner's reserve.
decreaseOwnerCountForObject(view, sleAccount, sleTicket, 1, j);

std::uint32_t const ticketSeq{(*sleTicket)[sfTicketSequence]};

// Remove Ticket from ledger.
view.erase(sleTicket);

// Once the Ticket is gone, a TransactionProposal keyed to it can never
// execute: its proposed transaction would fail with tefNO_TICKET. Clean
// up the stale proposal and release its Owner's reserve (XLS-0103 §4.5).
// This runs both when the proposal's own completed transaction consumes
// the ticket and when the account spends the ticket on something else.
if (view.rules().enabled(featureCosign))
{
if (auto const sleProposal = view.peek(keylet::txProposal(account, ticketSeq)))
{
if (TER const ter = proposal::deleteProposal(view, sleProposal, j); !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
}
}

return tesSUCCESS;
}

Expand Down
Loading
Loading