Transactor Architecture
← Back to Transactors: Understanding the Lifecycle of a Transaction
Introduction
Every transaction that modifies the XRP Ledger—whether it's a payment, an offer, a trust line, or any other operation—is processed by a Transactor. The transactor architecture provides a consistent, safe, and extensible framework for implementing transaction types while ensuring that the ledger remains in a valid state.
Understanding this architecture is essential for anyone who wants to implement new transaction types, debug validation failures, or contribute to the rippled codebase. This section explores the layered design of the transaction engine and how each component contributes to the safety and correctness of ledger modifications.
The Transactor Base Class
The Transactor class, defined in src/xrpld/app/tx/detail/Transactor.h, is the foundation of all transaction processing in rippled. Every transaction type inherits from this base class, which provides:
Common validation logic: Signature verification, fee checks, sequence number validation
Helper methods: Account balance queries, ledger state access, fee calculation
Virtual methods: Hooks for transaction-specific logic (preflight, preclaim, doApply)
Transaction context: Access to the ledger view, transaction data, and application state
Core Class Structure
class Transactor
{
protected:
ApplyContext& ctx_;
beast::WrappedSink sink_;
beast::Journal const j_;
AccountID const account_;
XRPAmount mPriorBalance; // Balance before fees.
XRPAmount mSourceBalance; // Balance after fees.
public:
enum ConsequencesFactoryType { Normal, Blocker, Custom };
// Main entry point for transaction application
ApplyResult operator()();
ApplyView& view();
ApplyView const& view() const;
// Static methods for validation phases
static NotTEC checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j);
static NotTEC checkPriorTxAndLastLedger(PreclaimContext const& ctx);
static TER checkFee(PreclaimContext const& ctx, XRPAmount baseFee);
static NotTEC checkSign(PreclaimContext const& ctx);
static XRPAmount calculateBaseFee(ReadView const& view, STTx const& tx);
// Default implementations that derived classes can override
static TER preclaim(PreclaimContext const& ctx) { return tesSUCCESS; }
protected:
TER apply();
explicit Transactor(ApplyContext& ctx);
virtual void preCompute();
virtual TER doApply() = 0; // Pure virtual - must be implemented
};Key observations:
doApply()is pure virtual: Every transaction type must implement this methodpreclaim()has a default implementation: ReturnstesSUCCESS, can be overriddenmPriorBalanceandmSourceBalance: Track balance before and after fee deductionctx_: TheApplyContextprovides access to the transaction, view, and application
Context Objects
The transactor framework uses three context objects that provide different levels of access at each processing phase:
PreflightContext
Used during the preflight phase for stateless validation:
What it provides:
Access to the raw transaction (
tx)Protocol rules that are currently enabled (
rules)Application flags (
flags)Logging journal (
j)
What it does NOT provide:
Any ledger state (no
view)
This limitation is intentional—preflight checks must be stateless and deterministic based solely on the transaction content.
PreclaimContext
Used during the preclaim phase for read-only ledger validation:
What it provides:
Read-only access to the ledger (
view)The result from preflight (
preflightResult)Transaction and application context
Key distinction: The view is ReadView const&—you can read ledger state but cannot modify it.
ApplyContext
Used during the doApply phase for ledger modification:
What it provides:
Full read/write access to the ledger via
view()Transaction data and application services
Methods for delivering amounts, tracking metadata
Transaction Type Registration
New transaction types must be registered with the transaction engine. This is done through a combination of:
Transaction format definition in
src/libxrpl/protocol/TxFormats.cppTransactor class implementation in
src/xrpld/app/tx/detail/Registration in
applySteps.cpp
Example: CheckCreate Registration
In TxFormats.cpp:
This defines:
The JSON name (
jss::CheckCreate)The transaction type enum (
ttCHECK_CREATE)Required and optional fields
Common fields inherited by all transactions
The Transactor Hierarchy
All specific transaction types inherit from Transactor and implement their own validation logic:
Each derived class typically implements:
preflight(): Static method for stateless validationpreclaim(): Static method for ledger-state validationdoApply(): Instance method for applying changes
The invokePreflight Template
The base class provides a template method that orchestrates the preflight phase:
This template ensures consistent ordering of validation steps across all transaction types.
Key Member Variables
account_
The AccountID of the transaction sender, extracted from the sfAccount field:
This is set during construction and used throughout transaction processing.
mPriorBalance and mSourceBalance
These track the sender's XRP balance:
mPriorBalance: Balance at the start of transaction processingmSourceBalance: Balance after the transaction fee is deducted
This is used for reserve calculations—the reserve is checked against mPriorBalance to allow accounts to dip into reserves to pay fees.
ctx_
The ApplyContext provides access to:
ctx_.tx: The transaction being processedctx_.view(): The ledger view for reading/writingctx_.app: The application instancectx_.journal: Logging
Helper Methods
The base class provides several helper methods used by derived transactors:
view()
Returns the ledger view for reading and modifying ledger state.
calculateBaseFee()
Calculates the base transaction fee based on the transaction type and current fee settings.
minimumFee()
Calculates the minimum fee required considering current load and fee escalation.
Codebase References
src/xrpld/app/tx/detail/Transactor.h
Base Transactor class definition
src/xrpld/app/tx/detail/Transactor.cpp
Base class implementation
src/xrpld/app/tx/detail/ApplyContext.h
ApplyContext definition
src/xrpld/app/tx/applySteps.cpp
Transaction type dispatch
src/libxrpl/protocol/TxFormats.cpp
Transaction format definitions
Key Takeaways
Inheritance Model: All transaction types inherit from
Transactor, ensuring consistent behaviorContext Objects: Three distinct contexts provide appropriate access at each phase
Pure Virtual doApply: Every transaction must implement its own state modification logic
Template-Based Preflight: The
invokePreflighttemplate ensures consistent validation orderingBalance Tracking:
mPriorBalanceandmSourceBalanceenable proper reserve handling
Last updated

