Skip to content

Commit 4ad6970

Browse files
authored
Fix xahau v1audit (XRPLF#250)
1 parent 97acfe9 commit 4ad6970

File tree

4 files changed

+126
-66
lines changed

4 files changed

+126
-66
lines changed

src/ripple/app/tx/impl/Escrow.cpp

+24-29
Original file line numberDiff line numberDiff line change
@@ -437,11 +437,19 @@ EscrowFinish::preflight(PreflightContext const& ctx)
437437
{
438438
if (!ctx.tx.isFieldPresent(sfOfferSequence))
439439
return temMALFORMED;
440-
}
441440

442-
if (!ctx.tx.isFieldPresent(sfEscrowID) &&
443-
!ctx.tx.isFieldPresent(sfOfferSequence))
444-
return temMALFORMED;
441+
if (ctx.tx.isFieldPresent(sfEscrowID) &&
442+
ctx.tx.getFieldU32(sfOfferSequence) != 0)
443+
return temMALFORMED;
444+
}
445+
else
446+
{
447+
if ((!ctx.tx.isFieldPresent(sfEscrowID) &&
448+
!ctx.tx.isFieldPresent(sfOfferSequence)) ||
449+
ctx.tx.isFieldPresent(sfEscrowID) &&
450+
ctx.tx.isFieldPresent(sfOfferSequence))
451+
return temMALFORMED;
452+
}
445453

446454
return tesSUCCESS;
447455
}
@@ -472,17 +480,6 @@ EscrowFinish::doApply()
472480

473481
bool const fixV1 = view().rules().enabled(fixXahauV1);
474482

475-
if (!fixV1)
476-
{
477-
if (escrowID && ctx_.tx[sfOfferSequence] != 0)
478-
return temMALFORMED;
479-
}
480-
else
481-
{
482-
if (escrowID && offerSequence)
483-
return temMALFORMED;
484-
}
485-
486483
Keylet k = escrowID ? Keylet(ltESCROW, *escrowID)
487484
: keylet::escrow(ctx_.tx[sfOwner], *offerSequence);
488485

@@ -723,11 +720,19 @@ EscrowCancel::preflight(PreflightContext const& ctx)
723720
{
724721
if (!ctx.tx.isFieldPresent(sfOfferSequence))
725722
return temMALFORMED;
726-
}
727723

728-
if (!ctx.tx.isFieldPresent(sfEscrowID) &&
729-
!ctx.tx.isFieldPresent(sfOfferSequence))
730-
return temMALFORMED;
724+
if (ctx.tx.isFieldPresent(sfEscrowID) &&
725+
ctx.tx.getFieldU32(sfOfferSequence) != 0)
726+
return temMALFORMED;
727+
}
728+
else
729+
{
730+
if ((!ctx.tx.isFieldPresent(sfEscrowID) &&
731+
!ctx.tx.isFieldPresent(sfOfferSequence)) ||
732+
ctx.tx.isFieldPresent(sfEscrowID) &&
733+
ctx.tx.isFieldPresent(sfOfferSequence))
734+
return temMALFORMED;
735+
}
731736

732737
return preflight2(ctx);
733738
}
@@ -744,16 +749,6 @@ EscrowCancel::doApply()
744749
std::optional<std::uint32_t> offerSequence = ctx_.tx[~sfOfferSequence];
745750

746751
bool const fixV1 = view().rules().enabled(fixXahauV1);
747-
if (!fixV1)
748-
{
749-
if (escrowID && ctx_.tx[sfOfferSequence] != 0)
750-
return temMALFORMED;
751-
}
752-
else
753-
{
754-
if (escrowID && offerSequence)
755-
return temMALFORMED;
756-
}
757752

758753
Keylet k = escrowID ? Keylet(ltESCROW, *escrowID)
759754
: keylet::escrow(ctx_.tx[sfOwner], *offerSequence);

src/ripple/app/tx/impl/Import.cpp

+11-10
Original file line numberDiff line numberDiff line change
@@ -817,16 +817,17 @@ Import::preflight(PreflightContext const& ctx)
817817
<< " validation count: " << validationCount;
818818

819819
// check if the validation count is adequate
820-
auto hasInsufficientQuorum = [&ctx](int quorum, int validationCount) {
821-
if (ctx.rules.enabled(fixXahauV1))
822-
{
823-
return quorum > validationCount;
824-
}
825-
else
826-
{
827-
return quorum >= validationCount;
828-
}
829-
};
820+
auto hasInsufficientQuorum =
821+
[&ctx](uint64_t quorum, uint64_t validationCount) {
822+
if (ctx.rules.enabled(fixXahauV1))
823+
{
824+
return quorum > validationCount;
825+
}
826+
else
827+
{
828+
return quorum >= validationCount;
829+
}
830+
};
830831
if (hasInsufficientQuorum(quorum, validationCount))
831832
{
832833
JLOG(ctx.j.warn()) << "Import: xpop did not contain an 80% quorum for "

src/ripple/app/tx/impl/URIToken.cpp

+87-26
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ URIToken::preflight(PreflightContext const& ctx)
144144
TER
145145
URIToken::preclaim(PreclaimContext const& ctx)
146146
{
147+
bool const fixV1 = ctx.view.rules().enabled(fixXahauV1);
148+
147149
std::shared_ptr<SLE const> sleU;
148150
uint32_t leFlags;
149151
std::optional<AccountID> issuer;
@@ -180,6 +182,11 @@ URIToken::preclaim(PreclaimContext const& ctx)
180182
AccountID const acc = ctx.tx.getAccountID(sfAccount);
181183
uint16_t tt = ctx.tx.getFieldU16(sfTransactionType);
182184

185+
auto const sle =
186+
ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount)));
187+
if (!sle)
188+
return tefINTERNAL;
189+
183190
switch (tt)
184191
{
185192
case ttURITOKEN_MINT: {
@@ -228,24 +235,75 @@ URIToken::preclaim(PreclaimContext const& ctx)
228235
if (purchaseAmount < saleAmount)
229236
return tecINSUFFICIENT_PAYMENT;
230237

231-
if (purchaseAmount.native() && saleAmount->native())
238+
if (fixV1)
232239
{
233-
// if it's an xrp sale/purchase then no trustline needed
234-
if (purchaseAmount >
235-
(sleOwner->getFieldAmount(sfBalance) - ctx.tx[sfFee]))
236-
return tecINSUFFICIENT_FUNDS;
237-
}
240+
if (purchaseAmount.native() && saleAmount->native())
241+
{
242+
// native transfer
238243

239-
// execution to here means it's an IOU sale
240-
// check if the buyer has the right trustline with an adequate
241-
// balance
244+
STAmount needed{ctx.view.fees().accountReserve(
245+
sle->getFieldU32(sfOwnerCount) + 1)};
242246

243-
STAmount availableFunds{accountFunds(
244-
ctx.view, acc, purchaseAmount, fhZERO_IF_FROZEN, ctx.j)};
247+
STAmount const fee = ctx.tx.getFieldAmount(sfFee).xrp();
245248

246-
if (purchaseAmount > availableFunds)
247-
return tecINSUFFICIENT_FUNDS;
249+
if (needed + fee < needed)
250+
return tecINTERNAL;
248251

252+
needed += fee;
253+
254+
if (needed + purchaseAmount < needed)
255+
return tecINTERNAL;
256+
257+
needed += purchaseAmount;
258+
259+
if (needed > sle->getFieldAmount(sfBalance))
260+
return tecINSUFFICIENT_FUNDS;
261+
}
262+
else if (purchaseAmount.native() || saleAmount->native())
263+
{
264+
// should not be able to happen
265+
return tecINTERNAL;
266+
}
267+
else
268+
{
269+
// iou transfer
270+
271+
STAmount availableFunds{accountFunds(
272+
ctx.view,
273+
acc,
274+
purchaseAmount,
275+
fhZERO_IF_FROZEN,
276+
ctx.j)};
277+
278+
if (purchaseAmount > availableFunds)
279+
return tecINSUFFICIENT_FUNDS;
280+
}
281+
}
282+
else
283+
{
284+
// old logic
285+
286+
if (purchaseAmount.native() && saleAmount->native())
287+
{
288+
// if it's an xrp sale/purchase then no trustline needed
289+
if (purchaseAmount >
290+
(sleOwner->getFieldAmount(sfBalance) - ctx.tx[sfFee]))
291+
return tecINSUFFICIENT_FUNDS;
292+
}
293+
else
294+
{
295+
// iou
296+
STAmount availableFunds{accountFunds(
297+
ctx.view,
298+
acc,
299+
purchaseAmount,
300+
fhZERO_IF_FROZEN,
301+
ctx.j)};
302+
303+
if (purchaseAmount > availableFunds)
304+
return tecINSUFFICIENT_FUNDS;
305+
}
306+
}
249307
return tesSUCCESS;
250308
}
251309

@@ -412,17 +470,6 @@ URIToken::doApply()
412470
}
413471

414472
case ttURITOKEN_BUY: {
415-
if (account_ == *owner)
416-
{
417-
// this is a clear operation
418-
sleU->makeFieldAbsent(sfAmount);
419-
if (sleU->isFieldPresent(sfDestination))
420-
sleU->makeFieldAbsent(sfDestination);
421-
sb.update(sleU);
422-
sb.apply(ctx_.rawView());
423-
return tesSUCCESS;
424-
}
425-
426473
STAmount const purchaseAmount = ctx_.tx.getFieldAmount(sfAmount);
427474

428475
// check if the seller has listed it at all
@@ -446,8 +493,22 @@ URIToken::doApply()
446493
// if it's an xrp sale/purchase then no trustline needed
447494
if (purchaseAmount.native())
448495
{
449-
if (purchaseAmount >
450-
((*sleOwner)[sfBalance] - ctx_.tx[sfFee]))
496+
STAmount needed{sb.fees().accountReserve(
497+
sle->getFieldU32(sfOwnerCount) + 1)};
498+
499+
STAmount const fee = ctx_.tx.getFieldAmount(sfFee).xrp();
500+
501+
if (needed + fee < needed)
502+
return tecINTERNAL;
503+
504+
needed += fee;
505+
506+
if (needed + purchaseAmount < needed)
507+
return tecINTERNAL;
508+
509+
needed += purchaseAmount;
510+
511+
if (needed > mPriorBalance)
451512
return tecINSUFFICIENT_FUNDS;
452513
}
453514
else

src/test/app/URIToken_test.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,10 @@ struct URIToken_test : public beast::unit_test::suite
454454
using namespace std::literals::chrono_literals;
455455

456456
// setup env
457-
Env env{*this, features};
457+
Env env{
458+
*this, envconfig(), features, nullptr, beast::severities::kWarning
459+
// beast::severities::kTrace
460+
};
458461
auto const alice = Account("alice");
459462
auto const bob = Account("bob");
460463
auto const carol = Account("carol");

0 commit comments

Comments
 (0)