This commit is contained in:
Denis Angell
2024-04-13 09:32:24 +02:00
parent 8a8822cb57
commit 175f436974
18 changed files with 262 additions and 94 deletions

View File

@@ -59,6 +59,7 @@ buildLedgerImpl(
OpenView accum(&*built);
assert(!accum.open());
applyTxs(accum, built);
std::cout << "BuildLedger::buildLedgerImpl: " << "\n";
accum.apply(*built);
}

View File

@@ -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))
{

View File

@@ -1397,7 +1397,7 @@ NetworkOPsImp::apply(std::unique_lock<std::mutex>& 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;

View File

@@ -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);
}

View File

@@ -17,7 +17,7 @@
*/
//==============================================================================
#include <ripple/protocol/TER.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/tx/applySteps.h>
#include <ripple/app/tx/impl/ApplyContext.h>
#include <ripple/app/tx/impl/Batch.h>
@@ -52,6 +52,7 @@
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/TER.h>
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<TER> 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<STTx> 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<ApplyViewImpl&>(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<TxType>(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<std::pair<std::uint16_t, TER>> 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<ApplyViewImpl&>(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<STObject> executions;
// std::vector<STObject> emissions;
// std::vector<STObject> 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

View File

@@ -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<ApplyViewImpl&>(ctx_.view());
std::vector<STObject> executions;
std::vector<STObject> emissions;
avi.copyHookMetaData(executions, emissions);
std::vector<STObject> batch;
avi.copyHookMetaData(executions, emissions, batch);
ctx_.discard();
ApplyViewImpl& avi2 = dynamic_cast<ApplyViewImpl&>(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);
}

View File

@@ -105,16 +105,19 @@ public:
void
setHookMetaData(
std::vector<STObject>&& executions,
std::vector<STObject>&& emissions)
std::vector<STObject>&& emissions,
std::vector<STObject>&& batch)
{
hookExecution_ = std::move(executions);
hookEmission_ = std::move(emissions);
batchExecution_ = std::move(batch);
}
void
copyHookMetaData(
std::vector<STObject>& execution /* in */,
std::vector<STObject>& emission /* in */)
std::vector<STObject>& emission /* in */,
std::vector<STObject>& 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

View File

@@ -54,6 +54,7 @@ public:
void
apply(RawView& to)
{
std::cout << "Sandbox::apply: " << "\n";
items_.apply(to);
}
};

View File

@@ -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);
}

View File

@@ -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_)
{

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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 =

View File

@@ -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);

View File

@@ -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<std::uint32_t> const ticketSeq{operator[](~sfTicketSequence)};
if (!ticketSeq)
// No TicketSequence specified. Return the Sequence, whatever it is.

View File

@@ -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."),

View File

@@ -45,6 +45,7 @@ TxFormats::TxFormats()
{sfHookParameters, soeOPTIONAL},
{sfOperationLimit, soeOPTIONAL},
{sfCloseResolution, soeOPTIONAL},
{sfBatchIndex, soeOPTIONAL},
};
add(jss::AccountSet,

View File

@@ -22,6 +22,33 @@
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
// 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