From 175f436974c8a1faf30c2c69f3ecd9a4821a92cf Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Sat, 13 Apr 2024 09:32:24 +0200 Subject: [PATCH] dump --- src/ripple/app/ledger/impl/BuildLedger.cpp | 1 + src/ripple/app/ledger/impl/OpenLedger.cpp | 6 +- src/ripple/app/misc/NetworkOPs.cpp | 2 +- src/ripple/app/tx/impl/ApplyContext.cpp | 12 +- src/ripple/app/tx/impl/Batch.cpp | 170 ++++++++++++++------- src/ripple/app/tx/impl/Transactor.cpp | 30 ++-- src/ripple/ledger/ApplyViewImpl.h | 11 +- src/ripple/ledger/Sandbox.h | 1 + src/ripple/ledger/impl/ApplyViewImpl.cpp | 2 +- src/ripple/ledger/impl/OpenView.cpp | 2 + src/ripple/protocol/SField.h | 1 + src/ripple/protocol/TER.h | 1 + src/ripple/protocol/TxFlags.h | 1 + src/ripple/protocol/impl/SField.cpp | 1 + src/ripple/protocol/impl/STTx.cpp | 4 + src/ripple/protocol/impl/TER.cpp | 1 + src/ripple/protocol/impl/TxFormats.cpp | 1 + src/test/app/Batch_test.cpp | 109 ++++++++++--- 18 files changed, 262 insertions(+), 94 deletions(-) diff --git a/src/ripple/app/ledger/impl/BuildLedger.cpp b/src/ripple/app/ledger/impl/BuildLedger.cpp index fbdf9945f..c08cd4df3 100644 --- a/src/ripple/app/ledger/impl/BuildLedger.cpp +++ b/src/ripple/app/ledger/impl/BuildLedger.cpp @@ -59,6 +59,7 @@ buildLedgerImpl( OpenView accum(&*built); assert(!accum.open()); applyTxs(accum, built); + std::cout << "BuildLedger::buildLedgerImpl: " << "\n"; accum.apply(*built); } diff --git a/src/ripple/app/ledger/impl/OpenLedger.cpp b/src/ripple/app/ledger/impl/OpenLedger.cpp index f81def31e..e13d80438 100644 --- a/src/ripple/app/ledger/impl/OpenLedger.cpp +++ b/src/ripple/app/ledger/impl/OpenLedger.cpp @@ -121,7 +121,7 @@ OpenLedger::accept( // Apply local tx for (auto const& item : locals) { - // std::cout << "OpenLedger::accept: TXQu" << "\n"; + JLOG(j_.trace()) << "OpenLedger::accept: getTxQ" << "\n"; app.getTxQ().apply(app, *next, item.second, flags, j_); } @@ -134,6 +134,10 @@ OpenLedger::accept( // skip emitted txns if (tx->isFieldPresent(sfEmitDetails)) continue; + + // skip emitted txns + if (tx->isFieldPresent(sfBatchIndex)) + continue; if (auto const toSkip = app.getHashRouter().shouldRelay(txId)) { diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index e679c0190..c04ca05bc 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -1397,7 +1397,7 @@ NetworkOPsImp::apply(std::unique_lock& batchLock) if (e.failType == FailHard::yes) flags |= tapFAIL_HARD; - // std::cout << "NetworkOPsImp::apply: TXQu" << "\n"; + std::cout << "NetworkOPsImp::apply: getTxQ" << "\n"; auto const result = app_.getTxQ().apply( app_, view, e.transaction->getSTransaction(), flags, j); e.result = result.first; diff --git a/src/ripple/app/tx/impl/ApplyContext.cpp b/src/ripple/app/tx/impl/ApplyContext.cpp index d6aff4912..917b46fde 100644 --- a/src/ripple/app/tx/impl/ApplyContext.cpp +++ b/src/ripple/app/tx/impl/ApplyContext.cpp @@ -56,10 +56,20 @@ ApplyContext::discard() void ApplyContext::apply(TER ter) { - // std::cout << "ApplyContext::apply" << "\n"; + std::cout << "ApplyContext::apply: " << ter << "\n"; + std::cout << "ApplyContext::apply: " << (flags_ & tapPREFLIGHT_BATCH) << "\n"; // std::cout << "tx: " << tx.getTransactionID() << "\n"; // if (flags_ == tapPREFLIGHT_BATCH) // return; + // if (ter == tecBATCH_FAILURE || flags_ == tapPREFLIGHT_BATCH) + // { + // std::cout << "ApplyContext::apply: " << "FAILURE" << "\n"; + // return; + // } + // else + // { + // view_->apply(base_, tx, ter, journal); + // } view_->apply(base_, tx, ter, journal); } diff --git a/src/ripple/app/tx/impl/Batch.cpp b/src/ripple/app/tx/impl/Batch.cpp index a96149565..ad8410227 100644 --- a/src/ripple/app/tx/impl/Batch.cpp +++ b/src/ripple/app/tx/impl/Batch.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include +#include #include #include #include @@ -52,6 +52,7 @@ #include #include #include +#include namespace ripple { @@ -229,10 +230,10 @@ invoke_preclaim(PreclaimContext const& ctx) if (result != tesSUCCESS) return result; - result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); + // result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); - if (result != tesSUCCESS) - return result; + // if (result != tesSUCCESS) + // return result; result = T::checkSign(ctx); @@ -366,38 +367,41 @@ Batch::preclaim(PreclaimContext const& ctx) preclaimResponses.push_back(response); } - // Handle Atomicity - // for (const auto& response : preclaimResponses) - // { - // if (response != tesSUCCESS) - // { - // return response; - // } - // } - return tesSUCCESS; } -std::vector doApplyResponses; +// void +// Batch::updateAccount(Sandbox& sb) +// { +// auto const sle = ctx_.base_.read(keylet::account(account_)); +// if (!sle) +// return tefINTERNAL; + +// auto const sleSrcAcc = sb.peek(keylet::account(account_)); +// if (!sleSrcAcc) +// return tefINTERNAL; + +// auto const feePaid = ctx_.tx[sfFee].xrp(); +// sleSrcAcc->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance).xrp() - feePaid); +// sb.update(sleSrcAcc); +// sb.apply(ctx_.rawView()); +// } TER Batch::doApply() { - // std::cout << "Batch::doApply()" << "\n"; + std::cout << "Batch::doApply()" << "\n"; Sandbox sb(&ctx_.view()); - // Sandbox sb(ctx_.base_.base_, view().flags()); uint32_t flags = ctx_.tx.getFlags(); if (flags & tfBatchMask) return temINVALID_FLAG; + // SANITIZE + std::vector stxTxns; auto const& txns = ctx_.tx.getFieldArray(sfRawTransactions); for (std::size_t i = 0; i < txns.size(); ++i) { - // std::cout << "START" << "\n"; - ApplyViewImpl& avi = dynamic_cast(ctx_.view()); - STObject meta{sfBatchExecution}; - auto const& txn = txns[i]; if (!txn.isFieldPresent(sfTransactionType)) { @@ -409,9 +413,61 @@ Batch::doApply() auto const tt = txn.getFieldU16(sfTransactionType); auto const txtype = safe_cast(tt); auto const stx = STTx(txtype, [&txn](STObject& obj) { obj = std::move(txn); }); + stxTxns.push_back(stx); + } - // OpenView(&ctx_.view()) - JLOG(ctx_.journal.error()) << "Batch: SUBMITTING: " << preclaimResponses[i]; + // DRY RUN + // std::cout << "DRYRUN::size(): " << stxTxns.size() << "\n"; + // std::vector> dryVector; + // for (std::size_t i = 0; i < stxTxns.size(); ++i) + // { + // auto const& stx = stxTxns[i]; + // ApplyContext actx( + // ctx_.app, + // ctx_.base_, + // stx, + // preclaimResponses[i], + // ctx_.view().fees().base, + // tapPREFLIGHT_BATCH, + // ctx_.journal); + // auto const result = invoke_apply(actx); + // dryVector.emplace_back(stx.getTxnType(), result.first); + // std::cout << "ApplyContext::size(): " << actx.size() << "\n"; + // actx.discard(); + // } + + ApplyViewImpl& avi = dynamic_cast(ctx_.view()); + // for (auto const& dryRun : dryVector) + // { + // STObject meta{sfBatchExecution}; + // meta.setFieldU8(sfTransactionResult, TERtoInt(dryRun.second)); + // meta.setFieldU16(sfTransactionType, dryRun.first); + // avi.addBatchExecutionMetaData(std::move(meta)); + + // // tfBatchAtomic + // if (dryRun.second != tesSUCCESS && flags & tfBatchAtomic) + // { + // std::cout << "tfBatchAtomic::Failed" << "\n"; + // sb.apply(ctx_.rawView()); + // return tecBATCH_FAILURE; + // } + // } + + // ctx_.discard(); + + // // reset avi + // std::vector executions; + // std::vector emissions; + // std::vector batch; + // avi.setHookMetaData(std::move(executions), std::move(emissions), std::move(batch)); + + // WET RUN + TER result = tesSUCCESS; + for (std::size_t i = 0; i < stxTxns.size(); ++i) + { + STObject meta{sfBatchExecution}; + + auto const& stx = stxTxns[i]; ApplyContext actx( ctx_.app, ctx_.base_, @@ -420,54 +476,60 @@ Batch::doApply() ctx_.view().fees().base, view().flags(), ctx_.journal); - auto const result = invoke_apply(actx); - meta.setFieldU8(sfTransactionResult, TERtoInt(result.first)); - meta.setFieldU16(sfTransactionType, tt); - if (result.first == tesSUCCESS) - meta.setFieldH256(sfTransactionHash, stx.getTransactionID()); - - avi.addBatchExecutionMetaData(std::move(meta)); - - if (result.first != tesSUCCESS) - { - actx.discard(); + auto const _result = invoke_apply(actx); + meta.setFieldU8(sfTransactionResult, TERtoInt(_result.first)); + meta.setFieldU16(sfTransactionType, stx.getTxnType()); + if(_result.first != tesSUCCESS) + meta.setFieldH256(sfTransactionHash, stx.getTransactionID()); + + avi.addBatchExecutionMetaData(std::move(meta)); + + std::cout << "tfBatchFirst: " << (flags & tfBatchFirst) << "\n"; + std::cout << "tfBatchOne: " << (flags & tfBatchOne) << "\n"; + std::cout << "tfBatchAtomic: " << _result.first << "\n"; + + if (_result.first != tesSUCCESS) + { if (flags & tfBatchFirst) { - return tecFAILED_PROCESSING; + actx.discard(); + result = tecBATCH_FAILURE; + break; + } + if (flags & tfBatchOne) + { + actx.discard(); + continue; } } - doApplyResponses.push_back(result.first); + + if (_result.first == tesSUCCESS && flags & tfBatchOne) + { + result = tecBATCH_FAILURE; + break; + } } - auto const sle = ctx_.base_.read(keylet::account(account_)); - if (!sle) + auto const sleBase = ctx_.base_.read(keylet::account(account_)); + if (!sleBase) return tefINTERNAL; auto const sleSrcAcc = sb.peek(keylet::account(account_)); if (!sleSrcAcc) return tefINTERNAL; + // std::cout << "ACCOUNT BASE SEQ: " << sleBase->getFieldU32(sfSequence) << "\n"; + // std::cout << "ACCOUNT BASE BALANCE: " << sleBase->getFieldAmount(sfBalance) << "\n"; // std::cout << "ACCOUNT SEQ: " << sleSrcAcc->getFieldU32(sfSequence) << "\n"; - // std::cout << "ACCOUNT BALANCE: " << mSourceBalance << "\n"; - // std::cout << "ACCOUNT BALANCE=: " << sle->getFieldAmount(sfBalance) << "\n"; - sleSrcAcc->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance)); + // std::cout << "ACCOUNT BALANCE: " << sleSrcAcc->getFieldAmount(sfBalance) << "\n"; + + auto const feePaid = ctx_.tx[sfFee].xrp(); + sleSrcAcc->setFieldU32(sfSequence, sleBase->getFieldU32(sfSequence)); + sleSrcAcc->setFieldAmount(sfBalance, sleBase->getFieldAmount(sfBalance).xrp() - feePaid); sb.update(sleSrcAcc); - - for (auto applyResp : doApplyResponses) - { - if (applyResp != tesSUCCESS) - { - if (flags & tfBatchAtomic) - { - ctx_.discard(); - return tecFAILED_PROCESSING; - } - } - } - sb.apply(ctx_.rawView()); - return tesSUCCESS; + return result; } XRPAmount diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 15f5c876c..a66284dc4 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -531,6 +531,7 @@ TER Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); + std::cout << "feePaid: " << feePaid << "\n"; auto const sle = view().peek(keylet::account(account_)); // RH NOTE: we don't need to check for ttIMPORT here because this function @@ -541,7 +542,9 @@ Transactor::payFee() // Deduct the fee, so it's not available during the transaction. // Will only write the account back if the transaction succeeds. + std::cout << "mSourceBalance1.1: " << mSourceBalance << "\n"; mSourceBalance -= feePaid; + std::cout << "mSourceBalance1.2: " << mSourceBalance << "\n"; sle->setFieldAmount(sfBalance, mSourceBalance); // VFALCO Should we call view().rawDestroyXRP() here as well? @@ -557,9 +560,12 @@ Transactor::checkSeqProxy( { auto const id = tx.getAccountID(sfAccount); + auto const tt = tx.getTxnType(); + auto const sle = view.read(keylet::account(id)); SeqProxy const t_seqProx = tx.getSeqProxy(); + std::cout << "Transactor::checkSeqProxy: " << t_seqProx.value() << "\n"; if (!sle) { if (view.rules().enabled(featureImport) && @@ -613,6 +619,7 @@ Transactor::checkSeqProxy( return terPRE_SEQ; } // It's an already-used sequence number. + JLOG(j.trace()) << "applyTransaction: " << tt; JLOG(j.trace()) << "applyTransaction: has past sequence number " << "a_seq=" << a_seq << " t_seq=" << t_seqProx; return tefPAST_SEQ; @@ -808,10 +815,8 @@ Transactor::apply() preCompute(); auto const tt = ctx_.tx.getTxnType(); - // std::cout << "tt: " << tt << "\n"; - // std::cout << "id: " << ctx_.tx.getTransactionID() << "\n"; - // if (tt == ttBATCH) - // return doApply(); + std::cout << "tt: " << tt << "\n"; + std::cout << "id: " << ctx_.tx.getTransactionID() << "\n"; // If the transactor requires a valid account and the transaction doesn't // list one, preflight will have already a flagged a failure. @@ -827,11 +832,7 @@ Transactor::apply() if (sle) { - // std::cout << "mSourceBalance: " << mSourceBalance << "\n"; - // std::cout << "mPriorBalance: " << mPriorBalance << "\n"; - // std::cout << "mSourceBalance=: " << (mSourceBalance - mPriorBalance) << "\n"; mPriorBalance = STAmount{(*sle)[sfBalance]}.xrp(); - // std::cout << "mPriorBalance: " << mPriorBalance << "\n"; mSourceBalance = mPriorBalance; TER result = consumeSeqProxy(sle); @@ -847,9 +848,6 @@ Transactor::apply() view().update(sle); } - - // std::cout << "Transactor::apply" << "\n"; - return doApply(); } @@ -1168,10 +1166,11 @@ Transactor::reset(XRPAmount fee) ApplyViewImpl& avi = dynamic_cast(ctx_.view()); std::vector executions; std::vector emissions; - avi.copyHookMetaData(executions, emissions); + std::vector batch; + avi.copyHookMetaData(executions, emissions, batch); ctx_.discard(); ApplyViewImpl& avi2 = dynamic_cast(ctx_.view()); - avi2.setHookMetaData(std::move(executions), std::move(emissions)); + avi2.setHookMetaData(std::move(executions), std::move(emissions), std::move(batch)); auto const txnAcct = view().peek(keylet::account(ctx_.tx.getAccountID(sfAccount))); @@ -1849,7 +1848,8 @@ Transactor::operator()() if (ctx_.size() > oversizeMetaDataCap) result = tecOVERSIZE; - if (isTecClaim(result) && (view().flags() & tapFAIL_HARD)) + if ((isTecClaim(result) && (view().flags() & tapFAIL_HARD)) || + view().flags() & tapPREFLIGHT_BATCH) { // If the tapFAIL_HARD flag is set, a tec result // must not do anything @@ -2083,6 +2083,8 @@ Transactor::operator()() ctx_.destroyXRP(fee); // Once we call apply, we will no longer be able to look at view() + std::cout << "Transaction::apply" << "\n"; + std::cout << "Transaction::apply: " << (view().flags() & tapPREFLIGHT_BATCH) << "\n"; ctx_.apply(result); } diff --git a/src/ripple/ledger/ApplyViewImpl.h b/src/ripple/ledger/ApplyViewImpl.h index 0f1655eed..03a40d824 100644 --- a/src/ripple/ledger/ApplyViewImpl.h +++ b/src/ripple/ledger/ApplyViewImpl.h @@ -105,16 +105,19 @@ public: void setHookMetaData( std::vector&& executions, - std::vector&& emissions) + std::vector&& emissions, + std::vector&& batch) { hookExecution_ = std::move(executions); hookEmission_ = std::move(emissions); + batchExecution_ = std::move(batch); } void copyHookMetaData( std::vector& execution /* in */, - std::vector& emission /* in */) + std::vector& emission /* in */, + std::vector& batch /* in */) { std::copy( hookExecution_.begin(), @@ -124,6 +127,10 @@ public: hookEmission_.begin(), hookEmission_.end(), std::back_inserter(emission)); + std::copy( + batchExecution_.begin(), + batchExecution_.end(), + std::back_inserter(batch)); } uint16_t diff --git a/src/ripple/ledger/Sandbox.h b/src/ripple/ledger/Sandbox.h index d8610a69e..bc829762b 100644 --- a/src/ripple/ledger/Sandbox.h +++ b/src/ripple/ledger/Sandbox.h @@ -54,6 +54,7 @@ public: void apply(RawView& to) { + std::cout << "Sandbox::apply: " << "\n"; items_.apply(to); } }; diff --git a/src/ripple/ledger/impl/ApplyViewImpl.cpp b/src/ripple/ledger/impl/ApplyViewImpl.cpp index 209e3d5e5..8811b003b 100644 --- a/src/ripple/ledger/impl/ApplyViewImpl.cpp +++ b/src/ripple/ledger/impl/ApplyViewImpl.cpp @@ -31,7 +31,7 @@ ApplyViewImpl::ApplyViewImpl(ReadView const* base, ApplyFlags flags) void ApplyViewImpl::apply(OpenView& to, STTx const& tx, TER ter, beast::Journal j) { - // std::cout << "ApplyViewImpl::apply" << "\n"; + std::cout << "ApplyViewImpl::apply" << "\n"; items_.apply(to, tx, ter, deliver_, batchExecution_, hookExecution_, hookEmission_, j); } diff --git a/src/ripple/ledger/impl/OpenView.cpp b/src/ripple/ledger/impl/OpenView.cpp index c34b7b3d4..e434f4fc0 100644 --- a/src/ripple/ledger/impl/OpenView.cpp +++ b/src/ripple/ledger/impl/OpenView.cpp @@ -130,6 +130,8 @@ void OpenView::apply(TxsRawView& to) const { // std::cout << "OpenView::apply" << "\n"; + // std::cout << "OpenView::apply: " << items_.size() << "\n"; + std::cout << "OpenView::apply: " << txs_.size() << "\n"; items_.apply(to); for (auto const& item : txs_) { diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index 69efad6a7..c21b405c2 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -410,6 +410,7 @@ extern SF_UINT32 const sfRewardLgrLast; extern SF_UINT32 const sfFirstNFTokenSequence; extern SF_UINT32 const sfImportSequence; extern SF_UINT32 const sfXahauActivationLgrSeq; +extern SF_UINT32 const sfBatchIndex; // 64-bit integers (common) extern SF_UINT64 const sfIndexNext; diff --git a/src/ripple/protocol/TER.h b/src/ripple/protocol/TER.h index 42d3cabd3..f2ed35221 100644 --- a/src/ripple/protocol/TER.h +++ b/src/ripple/protocol/TER.h @@ -340,6 +340,7 @@ enum TECcodes : TERUnderlyingType { tecXCHAIN_SELF_COMMIT = 185, // RESERVED - XCHAIN tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 186, // RESERVED - XCHAIN tecINSUF_RESERVE_SELLER = 187, + tecBATCH_FAILURE = 188, tecLAST_POSSIBLE_ENTRY = 255, }; diff --git a/src/ripple/protocol/TxFlags.h b/src/ripple/protocol/TxFlags.h index 4d24a6ace..ea9437623 100644 --- a/src/ripple/protocol/TxFlags.h +++ b/src/ripple/protocol/TxFlags.h @@ -171,6 +171,7 @@ constexpr std::uint32_t const tfOptOut = 0x00000001; enum BatchFlags : std::uint32_t { tfBatchAtomic = 0x00000001, tfBatchFirst = 0x00000002, + tfBatchOne = 0x00000004, }; constexpr std::uint32_t const tfBatchMask = diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index 31c9b539d..6723206a7 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -157,6 +157,7 @@ CONSTRUCT_TYPED_SFIELD(sfLockCount, "LockCount", UINT32, CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50); +CONSTRUCT_TYPED_SFIELD(sfBatchIndex, "BatchIndex", UINT32, 95); CONSTRUCT_TYPED_SFIELD(sfXahauActivationLgrSeq, "XahauActivationLgrSeq",UINT32, 96); CONSTRUCT_TYPED_SFIELD(sfImportSequence, "ImportSequence", UINT32, 97); CONSTRUCT_TYPED_SFIELD(sfRewardTime, "RewardTime", UINT32, 98); diff --git a/src/ripple/protocol/impl/STTx.cpp b/src/ripple/protocol/impl/STTx.cpp index cfa350381..8429278e4 100644 --- a/src/ripple/protocol/impl/STTx.cpp +++ b/src/ripple/protocol/impl/STTx.cpp @@ -188,6 +188,10 @@ STTx::getSeqProxy() const if (seq != 0) return SeqProxy::sequence(seq); + // std::uint32_t const batchIndex{getFieldU32(sfBatchIndex)}; + // if (batchIndex != 0) + // return SeqProxy::sequence(batchIndex); + std::optional const ticketSeq{operator[](~sfTicketSequence)}; if (!ticketSeq) // No TicketSequence specified. Return the Sequence, whatever it is. diff --git a/src/ripple/protocol/impl/TER.cpp b/src/ripple/protocol/impl/TER.cpp index bc8a41fa9..6e008aa88 100644 --- a/src/ripple/protocol/impl/TER.cpp +++ b/src/ripple/protocol/impl/TER.cpp @@ -92,6 +92,7 @@ transResults() MAKE_ERROR(tecREQUIRES_FLAG, "The transaction or part-thereof requires a flag that wasn't set."), MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."), MAKE_ERROR(tecINSUF_RESERVE_SELLER, "The seller of an object has insufficient reserves, and thus cannot complete the sale."), + MAKE_ERROR(tecBATCH_FAILURE, "Tx Batch Failure."), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), MAKE_ERROR(tefBAD_AUTH, "Transaction's public key is not authorized."), diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index 72ccddcca..09e3cc00c 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -45,6 +45,7 @@ TxFormats::TxFormats() {sfHookParameters, soeOPTIONAL}, {sfOperationLimit, soeOPTIONAL}, {sfCloseResolution, soeOPTIONAL}, + {sfBatchIndex, soeOPTIONAL}, }; add(jss::AccountSet, diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index 863d925fe..0153170fd 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -22,6 +22,33 @@ #include #include +// tfBatchOne +// Tx1: Payment = tecUNFUNDED => Leave +// Tx2: Payment = tesSUCCESS => Leave +// TER(tesSUCCESS) + +// tfBatchFirst +// Tx1: Payment = tesSUCCESS => Leave +// Tx2: Payment = tecUNFUNDED => Leave +// TER(tesSUCCESS) + +// tfBatchAtomic +// Tx1: Payment = tesSUCCESS => Revert +// Tx2: Payment = tecUNFUNDED => Leave +// TER(tecBATCH_FAILURE) + +// Broadcast - Manually broadcast the inner transactions +// Stacking Views +// TicketCreate as first of batch +// Sequence optional except when needed specifically +// Bypass the queue for inner transactions +// Fee only on outer - if 2 payments and fee escellation make sure that the outer tx includes the fee escallation for all inner +// Look at TicketCreate for creating virtual tickets (Transactor.cpp) (TransactionConsequences.cpp) +// If we have the batch index, the virtual ticket number is seq of batch + batch index. +// Think about multiple different angles of this. AccountA & AccountB. + +// OptionB: Use OfferID + namespace ripple { namespace test { @@ -61,9 +88,9 @@ class Batch_test : public beast::unit_test::suite jv[sfRawTransactions.jsonName][index] = Json::Value{}; jv[sfRawTransactions.jsonName][index][jss::RawTransaction] = tx; jv[sfRawTransactions.jsonName][index][jss::RawTransaction][jss::SigningPubKey] = strHex(account.pk()); - jv[sfRawTransactions.jsonName][index][jss::RawTransaction][sfFee.jsonName] = to_string(feeDrops); + jv[sfRawTransactions.jsonName][index][jss::RawTransaction][sfFee.jsonName] = 0; jv[sfRawTransactions.jsonName][index][jss::RawTransaction][jss::Sequence] = next; - // jv[sfRawTransactions.jsonName][index][jss::RawTransaction][sfCloseResolution.jsonName] = next; + jv[sfRawTransactions.jsonName][index][jss::RawTransaction][sfBatchIndex.jsonName] = next; return jv; } @@ -91,7 +118,6 @@ class Batch_test : public beast::unit_test::suite // // beast::severities::kWarning // beast::severities::kTrace}; - auto const feeDrops = env.current()->fees().base; auto const alice = Account("alice"); @@ -221,18 +247,18 @@ class Batch_test : public beast::unit_test::suite Json::Value jv; jv[jss::TransactionType] = jss::Batch; jv[jss::Account] = alice.human(); - jv[jss::Sequence] = seq + 2; + jv[jss::Sequence] = seq; // Batch Transactions jv[sfRawTransactions.jsonName] = Json::Value{Json::arrayValue}; // Tx 1 Json::Value const tx1 = pay(alice, bob, XRP(1)); - jv = addBatchTx(jv, tx1, alice, feeDrops, 0, seq); + jv = addBatchTx(jv, tx1, alice, feeDrops, 0, seq + 1); // Tx 2 Json::Value const tx2 = pay(alice, bob, XRP(1)); - jv = addBatchTx(jv, tx2, alice, feeDrops, 1, seq + 1); + jv = addBatchTx(jv, tx2, alice, feeDrops, 1, seq + 2); env(jv, fee(feeDrops * 2), ter(tesSUCCESS)); env.close(); @@ -243,11 +269,22 @@ class Batch_test : public beast::unit_test::suite }}; Json::Value params; - params[jss::transaction] = env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); + params[jss::ledger_index] = env.current()->seq() - 1; + params[jss::transactions] = true; + params[jss::expand] = true; + auto const jrr = env.rpc("json", "ledger", to_string(params)); std::cout << "jrr: " << jrr << "\n"; - auto const meta = jrr[jss::result][jss::meta]; - validateBatchTxns(meta, testCases); + + // Json::Value params; + // params[jss::transaction] = env.tx()->getJson(JsonOptions::none)[jss::hash]; + // auto const jrr = env.rpc("json", "tx", to_string(params)); + // std::cout << "jrr: " << jrr << "\n"; + // auto const meta = jrr[jss::result][jss::meta]; + // validateBatchTxns(meta, testCases); + + std::cout << "seq: " << env.seq(alice) << "\n"; + std::cout << "alice: " << env.balance(alice) << "\n"; + std::cout << "bob: " << env.balance(bob) << "\n"; BEAST_EXPECT(env.seq(alice) == 2); BEAST_EXPECT(env.balance(alice) == XRP(1000) - XRP(2) - (feeDrops * 2)); @@ -275,21 +312,33 @@ class Batch_test : public beast::unit_test::suite Json::Value jv; jv[jss::TransactionType] = jss::Batch; jv[jss::Account] = alice.human(); + jv[jss::Sequence] = seq; // Batch Transactions jv[sfRawTransactions.jsonName] = Json::Value{Json::arrayValue}; // Tx 1 Json::Value const tx1 = pay(alice, bob, XRP(1)); - jv = addBatchTx(jv, tx1, alice, feeDrops, 0, 0); + jv = addBatchTx(jv, tx1, alice, feeDrops, 0, seq + 1); // Tx 2 Json::Value const tx2 = pay(alice, bob, XRP(999)); - jv = addBatchTx(jv, tx2, alice, feeDrops, 1, 0); + jv = addBatchTx(jv, tx2, alice, feeDrops, 1, seq + 2); - env(jv, fee(feeDrops * 2), txflags(tfBatchAtomic), ter(tecFAILED_PROCESSING)); + env(jv, fee(feeDrops * 2), txflags(tfBatchAtomic), ter(tecBATCH_FAILURE)); env.close(); + Json::Value params; + params[jss::ledger_index] = env.current()->seq() - 1; + params[jss::transactions] = true; + params[jss::expand] = true; + auto const jrr = env.rpc("json", "ledger", to_string(params)); + std::cout << "jrr: " << jrr << "\n"; + + std::cout << "seq: " << env.seq(alice) << "\n"; + std::cout << "alice: " << env.balance(alice) << "\n"; + std::cout << "bob: " << env.balance(bob) << "\n"; + BEAST_EXPECT(env.seq(alice) == 2); BEAST_EXPECT(env.balance(alice) == XRP(1000) - (feeDrops * 2)); BEAST_EXPECT(env.balance(bob) == XRP(1000)); @@ -303,7 +352,14 @@ class Batch_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - test::jtx::Env env{*this, envconfig()}; + // test::jtx::Env env{*this, envconfig()}; + Env env{ + *this, + envconfig(), + features, + nullptr, + // beast::severities::kWarning + beast::severities::kTrace}; auto const feeDrops = env.current()->fees().base; auto const alice = Account("alice"); @@ -312,9 +368,11 @@ class Batch_test : public beast::unit_test::suite env.fund(XRP(1000), alice, bob, carol); env.close(); + auto const seq = env.seq("alice"); Json::Value jv; jv[jss::TransactionType] = jss::Batch; jv[jss::Account] = alice.human(); + jv[jss::Sequence] = seq; // Batch Transactions jv[sfRawTransactions.jsonName] = Json::Value{Json::arrayValue}; @@ -325,18 +383,29 @@ class Batch_test : public beast::unit_test::suite // Tx 2 Json::Value const tx2 = pay(alice, bob, XRP(999)); - jv = addBatchTx(jv, tx2, alice, feeDrops, 1, 0); + jv = addBatchTx(jv, tx2, alice, feeDrops, 1, 1); // Tx 3 Json::Value const tx3 = pay(alice, bob, XRP(1)); - jv = addBatchTx(jv, tx3, alice, feeDrops, 2, 0); + jv = addBatchTx(jv, tx3, alice, feeDrops, 2, 2); - env(jv, fee(feeDrops * 3), txflags(tfBatchFirst), ter(tecFAILED_PROCESSING)); + env(jv, fee(feeDrops * 3), txflags(tfBatchFirst), ter(tecBATCH_FAILURE)); env.close(); - BEAST_EXPECT(env.seq(alice) == 2); - BEAST_EXPECT(env.balance(alice) == XRP(1000) - (feeDrops * 3)); - BEAST_EXPECT(env.balance(bob) == XRP(1000)); + Json::Value params; + params[jss::ledger_index] = env.current()->seq() - 1; + params[jss::transactions] = true; + params[jss::expand] = true; + auto const jrr = env.rpc("json", "ledger", to_string(params)); + std::cout << "jrr: " << jrr << "\n"; + + std::cout << "seq: " << env.seq(alice) << "\n"; + std::cout << "alice: " << env.balance(alice) << "\n"; + std::cout << "bob: " << env.balance(bob) << "\n"; + + BEAST_EXPECT(env.seq(alice) == 4); + BEAST_EXPECT(env.balance(alice) == XRP(1000) - XRP(1)); + BEAST_EXPECT(env.balance(bob) == XRP(1000) + XRP(1)); } void