xrpld
Loading...
Searching...
No Matches
PaymentChannelCreate.cpp
1#include <xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h>
2
3#include <xrpl/basics/chrono.h>
4#include <xrpl/beast/utility/Zero.h>
5#include <xrpl/core/ServiceRegistry.h>
6#include <xrpl/ledger/ApplyView.h>
7#include <xrpl/ledger/View.h>
8#include <xrpl/ledger/helpers/AccountRootHelpers.h>
9#include <xrpl/ledger/helpers/DirectoryHelpers.h>
10#include <xrpl/ledger/helpers/SponsorHelpers.h>
11#include <xrpl/protocol/Feature.h>
12#include <xrpl/protocol/Indexes.h>
13#include <xrpl/protocol/Keylet.h>
14#include <xrpl/protocol/LedgerFormats.h>
15#include <xrpl/protocol/PublicKey.h>
16#include <xrpl/protocol/SField.h>
17#include <xrpl/protocol/STAmount.h>
18#include <xrpl/protocol/STLedgerEntry.h>
19#include <xrpl/protocol/STTx.h>
20#include <xrpl/protocol/TER.h>
21#include <xrpl/protocol/XRPAmount.h>
22#include <xrpl/tx/Transactor.h>
23#include <xrpl/tx/applySteps.h>
24
25#include <memory>
26
27namespace xrpl {
28
29/*
30 PaymentChannel
31
32 Payment channels permit off-ledger checkpoints of XRP payments flowing
33 in a single direction. A channel sequesters the owner's XRP in its own
34 ledger entry. The owner can authorize the recipient to claim up to a
35 given balance by giving the receiver a signed message (off-ledger). The
36 recipient can use this signed message to claim any unpaid balance while
37 the channel remains open. The owner can top off the line as needed. If
38 the channel has not paid out all its funds, the owner must wait out a
39 delay to close the channel to give the recipient a chance to supply any
40 claims. The recipient can close the channel at any time. Any transaction
41 that touches the channel after the expiration time will close the
42 channel. The total amount paid increases monotonically as newer claims
43 are issued. When the channel is closed any remaining balance is returned
44 to the owner. Channels are intended to permit intermittent off-ledger
45 settlement of ILP trust lines as balances get substantial. For
46 bidirectional channels, a payment channel can be used in each direction.
47*/
48
49//------------------------------------------------------------------------------
50
53{
54 return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()};
55}
56
59{
60 if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::kZero))
61 return temBAD_AMOUNT;
62
63 if (ctx.tx[sfAccount] == ctx.tx[sfDestination])
64 return temDST_IS_SRC;
65
66 if (!publicKeyType(ctx.tx[sfPublicKey]))
67 return temMALFORMED;
68
69 return tesSUCCESS;
70}
71
72TER
74{
75 auto const account = ctx.tx[sfAccount];
76 auto const sle = ctx.view.read(keylet::account(account));
77 if (!sle)
78 return terNO_ACCOUNT;
79
80 // Check reserve and funds availability
81 if (!ctx.view.rules().enabled(featureSponsor))
82 {
83 auto const balance = (*sle)[sfBalance];
84 auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1, 1);
85
86 if (balance < reserve)
88
89 if (balance < reserve + ctx.tx[sfAmount])
90 return tecUNFUNDED;
91 }
92
93 auto const dst = ctx.tx[sfDestination];
94
95 {
96 // Check destination account
97 auto const sled = ctx.view.read(keylet::account(dst));
98 if (!sled)
99 return tecNO_DST;
100
101 // Check if they have disallowed incoming payment channels
102 if (sled->isFlag(lsfDisallowIncomingPayChan))
103 return tecNO_PERMISSION;
104
105 if (sled->isFlag(lsfRequireDestTag) && !ctx.tx[~sfDestinationTag])
106 return tecDST_TAG_NEEDED;
107
108 // Pseudo-accounts cannot receive payment channels, other than native
109 // to their underlying ledger object - implemented in their respective
110 // transaction types. Note, this is not amendment-gated because all
111 // writes to pseudo-account discriminator fields **are** amendment
112 // gated, hence the behaviour of this check will always match the
113 // currently active amendments.
114 if (isPseudoAccount(sled))
115 return tecNO_PERMISSION;
116 }
117
118 return tesSUCCESS;
119}
120
121TER
123{
124 auto const account = ctx_.tx[sfAccount];
125 auto const sle = ctx_.view().peek(keylet::account(account));
126 if (!sle)
127 return tefINTERNAL; // LCOV_EXCL_LINE
128
129 if (ctx_.view().rules().enabled(fixPayChanCancelAfter))
130 {
131 auto const closeTime = ctx_.view().header().parentCloseTime;
132 if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter]))
133 return tecEXPIRED;
134 }
135
136 if (ctx_.view().rules().enabled(featureSponsor))
137 {
138 // First check: whoever is on the hook for the new owner increment
139 // can cover it. When sponsored this hits the sponsor branch and
140 // validates the sponsor's reserve + remaining credit. When
141 // unsponsored this hits the source branch and validates the
142 // source's pre-lock balance against base + (currentOC+1)*increment.
143 if (auto const ret = checkReserve(
144 ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, j_);
145 !isTesSuccess(ret))
146 return ret;
147
148 // Second check: after locking sfAmount in the channel, the source
149 // must still meet its own reserve floor. This is always the
150 // source's own balance against the source's own reserve — the
151 // sponsor's reserve was already validated above, and a sponsor
152 // never covers the locked funds. We compare directly (rather than
153 // via checkReserve) because that helper diverts to the
154 // sponsor's balance when a sponsor is present and would ignore the
155 // source's post-lock balance entirely. ownerCountDelta differs by
156 // case:
157 // - sponsored: 0 — sponsor covers the new owner increment, so
158 // the source only owes reserve for its current owners.
159 // - unsponsored: 1 — source owes reserve including the new increment.
160 auto const sourceReserve = accountReserve(
161 ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
162 if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve)
163 return tecUNFUNDED;
164 }
165
166 auto const dst = ctx_.tx[sfDestination];
167
168 // Create PayChan in ledger.
169 //
170 // Note that we use the value from the sequence or ticket as the
171 // payChan sequence. For more explanation see comments in SeqProxy.h.
172 Keylet const payChanKeylet = keylet::payChannel(account, dst, ctx_.tx.getSeqProxy());
173 auto const slep = std::make_shared<SLE>(payChanKeylet);
174
175 // Funds held in this channel
176 (*slep)[sfAmount] = ctx_.tx[sfAmount];
177 // Amount channel has already paid
178 (*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed();
179 (*slep)[sfAccount] = account;
180 (*slep)[sfDestination] = dst;
181 (*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay];
182 (*slep)[sfPublicKey] = ctx_.tx[sfPublicKey];
183 (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
184 (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
185 (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
186 if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
187 {
188 (*slep)[sfSequence] = ctx_.tx.getSeqProxy().value();
189 }
190
191 ctx_.view().insert(slep);
192
193 // Add PayChan to owner directory
194 {
195 auto const page = ctx_.view().dirInsert(
196 keylet::ownerDir(account), payChanKeylet, describeOwnerDir(account));
197 if (!page)
198 return tecDIR_FULL; // LCOV_EXCL_LINE
199 (*slep)[sfOwnerNode] = *page;
200 }
201
202 // Add PayChan to the recipient's owner directory
203 {
204 auto const page =
205 ctx_.view().dirInsert(keylet::ownerDir(dst), payChanKeylet, describeOwnerDir(dst));
206 if (!page)
207 return tecDIR_FULL; // LCOV_EXCL_LINE
208 (*slep)[sfDestinationNode] = *page;
209 }
210
211 // Deduct owner's balance, increment owner count
212 (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
213 increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
214 addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
215 ctx_.view().update(sle);
216
217 return tesSUCCESS;
218}
219
220void
222{
223 // No transaction-specific invariants yet (future work).
224}
225
226bool
228 STTx const&,
229 TER,
230 XRPAmount,
231 ReadView const&,
232 beast::Journal const&)
233{
234 // No transaction-specific invariants yet (future work).
235 return true;
236}
237} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
A view into a ledger.
Definition ReadView.h:41
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:180
std::shared_ptr< STLedgerEntry const > const & const_ref
beast::Journal const j_
Definition Transactor.h:155
XRPAmount preFeeBalance_
Definition Transactor.h:158
ApplyContext & ctx_
Definition Transactor.h:153
Class describing the consequences to the account of applying a transaction if the transaction consume...
Definition applySteps.h:52
T make_shared(T... args)
constexpr Zero kZero
Definition Zero.h:30
Keylet payChannel(AccountID const &src, AccountID const &dst, SeqProxy const &seq) noexcept
A PaymentChannel.
Definition Indexes.cpp:394
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:373
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_ACCOUNT
Definition TER.h:213
bool isXRP(AccountID const &c)
Definition AccountID.h:84
void increaseOwnerCount(ApplyView &view, SLE::ref accountSle, SLE::ref sponsorSle, std::uint32_t count, beast::Journal j)
Increase owner-count fields when the caller supplies the sponsor.
@ tefINTERNAL
Definition TER.h:165
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
void addSponsorToLedgerEntry(SLE::ref sle, SLE::const_ref sponsorSle, SF_ACCOUNT const &field=sfSponsor)
Stamp a reserve sponsor onto a ledger entry using an explicit sponsor SLE.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:607
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:572
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
TER checkReserve(ApplyViewContext ctx, SLE::const_ref accSle, XRPAmount accBalance, SLE::const_ref sponsorSle, Adjustment adj, beast::Journal j, TER insufReserveCode=tecINSUFFICIENT_RESERVE)
Check if an account has sufficient reserve.
@ temDST_IS_SRC
Definition TER.h:96
@ temMALFORMED
Definition TER.h:75
@ temBAD_AMOUNT
Definition TER.h:77
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
@ tecDIR_FULL
Definition TER.h:290
@ tecEXPIRED
Definition TER.h:317
@ tecINSUFFICIENT_RESERVE
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:308
@ tecDST_TAG_NEEDED
Definition TER.h:312
@ tecNO_DST
Definition TER.h:293
@ tecUNFUNDED
Definition TER.h:298
bool isPseudoAccount(SLE::const_pointer sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
XRPAmount accountReserve(ReadView const &view, SLE::const_ref sle, beast::Journal j, Adjustment adj={})
Returns the account reserve, in drops.
@ tesSUCCESS
Definition TER.h:245
XRPAmount accountReserve(std::uint32_t ownerCount, std::uint32_t accountCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:83
ReadView const & view
Definition Transactor.h:86
State information when preflighting a tx.
Definition Transactor.h:38