30
30
#include < ripple/ledger/View.h>
31
31
#include < ripple/protocol/Feature.h>
32
32
#include < ripple/protocol/Indexes.h>
33
+ #include < ripple/protocol/Rate.h>
33
34
#include < ripple/protocol/TxFlags.h>
34
35
#include < ripple/protocol/digest.h>
35
36
#include < ripple/protocol/st.h>
@@ -93,7 +94,8 @@ after(NetClock::time_point now, std::uint32_t mark)
93
94
TxConsequences
94
95
EscrowCreate::makeTxConsequences (PreflightContext const & ctx)
95
96
{
96
- return TxConsequences{ctx.tx , ctx.tx [sfAmount].xrp ()};
97
+ return TxConsequences{
98
+ ctx.tx , isXRP (ctx.tx [sfAmount]) ? ctx.tx [sfAmount].xrp () : beast::zero};
97
99
}
98
100
99
101
NotTEC
@@ -105,8 +107,18 @@ EscrowCreate::preflight(PreflightContext const& ctx)
105
107
if (auto const ret = preflight1 (ctx); !isTesSuccess (ret))
106
108
return ret;
107
109
108
- if (!isXRP (ctx.tx [sfAmount]))
109
- return temBAD_AMOUNT;
110
+ STAmount const amount{ctx.tx [sfAmount]};
111
+ if (!isXRP (amount))
112
+ {
113
+ if (!ctx.rules .enabled (featurePaychanAndEscrowForTokens))
114
+ return temBAD_AMOUNT;
115
+
116
+ if (!isLegalNet (amount))
117
+ return temBAD_AMOUNT;
118
+
119
+ if (isFakeXRP (amount))
120
+ return temBAD_CURRENCY;
121
+ }
110
122
111
123
if (ctx.tx [sfAmount] <= beast::zero)
112
124
return temBAD_AMOUNT;
@@ -198,17 +210,70 @@ EscrowCreate::doApply()
198
210
if (!sle)
199
211
return tefINTERNAL;
200
212
201
- // Check reserve and funds availability
202
- {
203
- auto const balance = STAmount ((*sle)[sfBalance]).xrp ();
204
- auto const reserve =
205
- ctx_.view ().fees ().accountReserve ((*sle)[sfOwnerCount] + 1 );
213
+ STAmount const amount{ctx_.tx [sfAmount]};
214
+
215
+ std::shared_ptr<SLE> sleLine;
216
+
217
+ auto const balance = STAmount ((*sle)[sfBalance]).xrp ();
218
+ auto const reserve =
219
+ ctx_.view ().fees ().accountReserve ((*sle)[sfOwnerCount] + 1 );
220
+ bool isIssuer = amount.getIssuer () == account;
206
221
207
- if (balance < reserve)
208
- return tecINSUFFICIENT_RESERVE;
222
+ if (balance < reserve)
223
+ return tecINSUFFICIENT_RESERVE;
209
224
225
+ // Check reserve and funds availability
226
+ if (isXRP (amount))
227
+ {
210
228
if (balance < reserve + STAmount (ctx_.tx [sfAmount]).xrp ())
211
229
return tecUNFUNDED;
230
+ // pass
231
+ }
232
+ else
233
+ {
234
+ // preflight will prevent this ever firing, included
235
+ // defensively for completeness
236
+ if (!ctx_.view ().rules ().enabled (featurePaychanAndEscrowForTokens))
237
+ return temDISABLED;
238
+
239
+ TER result = trustTransferAllowed (
240
+ ctx_.view (),
241
+ {account, ctx_.tx [sfDestination]},
242
+ amount.issue (),
243
+ ctx_.journal );
244
+
245
+ JLOG (ctx_.journal .trace ())
246
+ << " EscrowCreate::doApply trustTransferAllowed result=" << result;
247
+
248
+ if (!isTesSuccess (result))
249
+ return result;
250
+
251
+ // issuer does not need to lock anything
252
+ if (!isIssuer)
253
+ {
254
+ // perform the lock as a dry run before
255
+ // we modify anything on-ledger
256
+ sleLine = ctx_.view ().peek (keylet::line (
257
+ account, amount.getIssuer (), amount.getCurrency ()));
258
+
259
+ // check if the escrow is capable of being
260
+ // finished before we allow it to be created
261
+ if (!sleLine)
262
+ return tecNO_LINE;
263
+
264
+ {
265
+ TER result = trustAdjustLockedBalance (
266
+ ctx_.view (), sleLine, amount, 1 , ctx_.journal , DryRun);
267
+
268
+ JLOG (ctx_.journal .trace ())
269
+ << " EscrowCreate::doApply trustAdjustLockedBalance (dry) "
270
+ " result="
271
+ << result;
272
+
273
+ if (!isTesSuccess (result))
274
+ return result;
275
+ }
276
+ }
212
277
}
213
278
214
279
// Check destination account
@@ -223,7 +288,7 @@ EscrowCreate::doApply()
223
288
224
289
// Obeying the lsfDissalowXRP flag was a bug. Piggyback on
225
290
// featureDepositAuth to remove the bug.
226
- if (!ctx_.view ().rules ().enabled (featureDepositAuth) &&
291
+ if (!ctx_.view ().rules ().enabled (featureDepositAuth) && isXRP (amount) &&
227
292
((*sled)[sfFlags] & lsfDisallowXRP))
228
293
return tecNO_TARGET;
229
294
}
@@ -264,7 +329,33 @@ EscrowCreate::doApply()
264
329
}
265
330
266
331
// Deduct owner's balance, increment owner count
267
- (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx [sfAmount];
332
+ if (isXRP (amount))
333
+ (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx [sfAmount];
334
+ else
335
+ {
336
+ if (!ctx_.view ().rules ().enabled (featurePaychanAndEscrowForTokens))
337
+ return temDISABLED;
338
+
339
+ // issuer does not need to lock anything
340
+ if (!isIssuer)
341
+ {
342
+ if (!sleLine)
343
+ return tecNO_LINE;
344
+
345
+ // do the lock-up for real now
346
+ TER result = trustAdjustLockedBalance (
347
+ ctx_.view (), sleLine, amount, 1 , ctx_.journal , WetRun);
348
+
349
+ JLOG (ctx_.journal .trace ())
350
+ << " EscrowCreate::doApply trustAdjustLockedBalance (wet) "
351
+ " result="
352
+ << result;
353
+
354
+ if (!isTesSuccess (result))
355
+ return result;
356
+ }
357
+ }
358
+
268
359
adjustOwnerCount (ctx_.view (), sle, 1 , ctx_.journal );
269
360
ctx_.view ().update (sle);
270
361
@@ -360,6 +451,10 @@ EscrowFinish::doApply()
360
451
if (!slep)
361
452
return tecNO_TARGET;
362
453
454
+ AccountID const account = (*slep)[sfAccount];
455
+ auto const sle = ctx_.view ().peek (keylet::account (account));
456
+ auto amount = slep->getFieldAmount (sfAmount);
457
+
363
458
// If a cancel time is present, a finish operation should only succeed prior
364
459
// to that time. fix1571 corrects a logic error in the check that would make
365
460
// a finish only succeed strictly after the cancel time.
@@ -460,7 +555,31 @@ EscrowFinish::doApply()
460
555
}
461
556
}
462
557
463
- AccountID const account = (*slep)[sfAccount];
558
+ if (!isXRP (amount))
559
+ {
560
+ if (!ctx_.view ().rules ().enabled (featurePaychanAndEscrowForTokens))
561
+ return temDISABLED;
562
+
563
+ // perform a dry run of the transfer before we
564
+ // change anything on-ledger
565
+ TER result = trustTransferLockedBalance (
566
+ ctx_.view (),
567
+ account_, // txn signing account
568
+ sle, // src account
569
+ sled, // dst account
570
+ amount, // xfer amount
571
+ -1 ,
572
+ j_,
573
+ DryRun // dry run
574
+ );
575
+
576
+ JLOG (j_.trace ())
577
+ << " EscrowFinish::doApply trustTransferLockedBalance (dry) result="
578
+ << result;
579
+
580
+ if (!isTesSuccess (result))
581
+ return result;
582
+ }
464
583
465
584
// Remove escrow from owner directory
466
585
{
@@ -484,12 +603,35 @@ EscrowFinish::doApply()
484
603
}
485
604
}
486
605
487
- // Transfer amount to destination
488
- (*sled)[sfBalance] = (*sled)[sfBalance] + (*slep)[sfAmount];
606
+ if (isXRP (amount))
607
+ (*sled)[sfBalance] = (*sled)[sfBalance] + (*slep)[sfAmount];
608
+ else
609
+ {
610
+ // all the significant complexity of checking the validity of this
611
+ // transfer and ensuring the lines exist etc is hidden away in this
612
+ // function, all we need to do is call it and return if unsuccessful.
613
+ TER result = trustTransferLockedBalance (
614
+ ctx_.view (),
615
+ account_, // txn signing account
616
+ sle, // src account
617
+ sled, // dst account
618
+ amount, // xfer amount
619
+ -1 ,
620
+ j_,
621
+ WetRun // wet run;
622
+ );
623
+
624
+ JLOG (j_.trace ())
625
+ << " EscrowFinish::doApply trustTransferLockedBalance (wet) result="
626
+ << result;
627
+
628
+ if (!isTesSuccess (result))
629
+ return result;
630
+ }
631
+
489
632
ctx_.view ().update (sled);
490
633
491
634
// Adjust source owner count
492
- auto const sle = ctx_.view ().peek (keylet::account (account));
493
635
adjustOwnerCount (ctx_.view (), sle, -1 , ctx_.journal );
494
636
ctx_.view ().update (sle);
495
637
@@ -543,6 +685,30 @@ EscrowCancel::doApply()
543
685
}
544
686
545
687
AccountID const account = (*slep)[sfAccount];
688
+ auto const sle = ctx_.view ().peek (keylet::account (account));
689
+ auto amount = slep->getFieldAmount (sfAmount);
690
+ bool isIssuer = amount.getIssuer () == account;
691
+
692
+ std::shared_ptr<SLE> sleLine;
693
+
694
+ if (!isXRP (amount))
695
+ {
696
+ if (!ctx_.view ().rules ().enabled (featurePaychanAndEscrowForTokens))
697
+ return temDISABLED;
698
+
699
+ // issuer does not need to lock anything
700
+ if (!isIssuer)
701
+ {
702
+ sleLine = ctx_.view ().peek (keylet::line (
703
+ account, amount.getIssuer (), amount.getCurrency ()));
704
+
705
+ // dry run before we make any changes to ledger
706
+ if (TER result = trustAdjustLockedBalance (
707
+ ctx_.view (), sleLine, -amount, -1 , ctx_.journal , DryRun);
708
+ result != tesSUCCESS)
709
+ return result;
710
+ }
711
+ }
546
712
547
713
// Remove escrow from owner directory
548
714
{
@@ -569,9 +735,32 @@ EscrowCancel::doApply()
569
735
}
570
736
}
571
737
572
- // Transfer amount back to owner, decrement owner count
573
- auto const sle = ctx_.view ().peek (keylet::account (account));
574
- (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount];
738
+ // Transfer amount back to the owner (or unlock it in TL case)
739
+ if (isXRP (amount))
740
+ (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount];
741
+ else
742
+ {
743
+ if (!ctx_.view ().rules ().enabled (featurePaychanAndEscrowForTokens))
744
+ return temDISABLED;
745
+
746
+ // issuer does not need to lock anything
747
+ if (!isIssuer)
748
+ {
749
+ // unlock previously locked tokens from source line
750
+ TER result = trustAdjustLockedBalance (
751
+ ctx_.view (), sleLine, -amount, -1 , ctx_.journal , WetRun);
752
+
753
+ JLOG (ctx_.journal .trace ())
754
+ << " EscrowCancel::doApply trustAdjustLockedBalance (wet) "
755
+ " result="
756
+ << result;
757
+
758
+ if (!isTesSuccess (result))
759
+ return result;
760
+ }
761
+ }
762
+
763
+ // Decrement owner count
575
764
adjustOwnerCount (ctx_.view (), sle, -1 , ctx_.journal );
576
765
ctx_.view ().update (sle);
577
766
0 commit comments