diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index 0b18e6a8d..bf5c9f160 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -1,12 +1,17 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include +#include +#include #include #include #include diff --git a/src/ripple/app/tx/impl/Batch.cpp b/src/ripple/app/tx/impl/Batch.cpp index de7cd5656..68458aec5 100644 --- a/src/ripple/app/tx/impl/Batch.cpp +++ b/src/ripple/app/tx/impl/Batch.cpp @@ -292,13 +292,13 @@ Batch::preflight(PreflightContext const& ctx) auto const& txns = tx.getFieldArray(sfRawTransactions); if (txns.empty()) { - JLOG(ctx.j.warn()) << "Batch: txns array empty."; + JLOG(ctx.j.error()) << "Batch: txns array empty."; return temMALFORMED; } - if (txns.size() > 400) + if (txns.size() > 12) { - JLOG(ctx.j.warn()) << "Batch: txns array exceeds 400 entries."; + JLOG(ctx.j.error()) << "Batch: txns array exceeds 12 entries."; return temMALFORMED; } @@ -306,7 +306,7 @@ Batch::preflight(PreflightContext const& ctx) { if (!txn.isFieldPresent(sfTransactionType)) { - JLOG(ctx.j.warn()) + JLOG(ctx.j.error()) << "Batch: TransactionType missing in array entry."; return temMALFORMED; } @@ -343,14 +343,15 @@ Batch::preclaim(PreclaimContext const& ctx) // Cannot continue on failed txns if (preflightResponses[i] != tesSUCCESS) { - JLOG(ctx.j.warn()) << "Batch: Failed Preflight Response: " << preflightResponses[i]; + JLOG(ctx.j.error()) << "Batch: Failed Preflight Response: " << preflightResponses[i]; + preclaimResponses.push_back(TER(preflightResponses[i])); continue; } auto const& txn = txns[i]; if (!txn.isFieldPresent(sfTransactionType)) { - JLOG(ctx.j.warn()) + JLOG(ctx.j.error()) << "Batch: TransactionType missing in array entry."; return temMALFORMED; } @@ -388,19 +389,22 @@ Batch::doApply() 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)) { - JLOG(ctx_.journal.warn()) + JLOG(ctx_.journal.error()) << "Batch: TransactionType missing in array entry."; return temMALFORMED; } auto const tt = txn.getFieldU16(sfTransactionType); auto const txtype = safe_cast(tt); - auto const stx = - STTx(txtype, [&txn](STObject& obj) { obj = std::move(txn); }); - + auto const stx = STTx(txtype, [&txn](STObject& obj) { obj = std::move(txn); }); + // OpenView(&ctx_.view()) ApplyContext actx( ctx_.app, @@ -410,32 +414,34 @@ Batch::doApply() ctx_.view().fees().base, view().flags(), ctx_.journal); + auto const result = invoke_apply(actx); - // if (result.first == tesSUCCESS) - // { - // ctx_.base_.apply(tesSUCCESS); - // } - // else - // { - // actx.discard(); - // } - - ApplyViewImpl& avi = dynamic_cast(ctx_.view()); - - STObject meta{sfBatchExecution}; meta.setFieldU8(sfTransactionResult, TERtoInt(result.first)); meta.setFieldU16(sfTransactionType, tt); - meta.setFieldH256(sfTransactionHash, stx.getTransactionID()); + if (result.first == tesSUCCESS) + meta.setFieldH256(sfTransactionHash, stx.getTransactionID()); + avi.addBatchExecutionMetaData(std::move(meta)); + if (result.first != tesSUCCESS) + { + actx.discard(); + } } auto const sle = ctx_.base_.read(keylet::account(account_)); if (!sle) return tefINTERNAL; - std::cout << "ACCOUNT SEQ: " << sle->getFieldU32(sfSequence) << "\n"; + auto const sleSrcAcc = sb.peek(keylet::account(account_)); + if (!sleSrcAcc) + return tefINTERNAL; + + 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)); + sb.update(sleSrcAcc); + sb.apply(ctx_.rawView()); return tesSUCCESS; } diff --git a/src/ripple/app/tx/impl/InvariantCheck.cpp b/src/ripple/app/tx/impl/InvariantCheck.cpp index add9fa98c..cecedc4eb 100644 --- a/src/ripple/app/tx/impl/InvariantCheck.cpp +++ b/src/ripple/app/tx/impl/InvariantCheck.cpp @@ -259,9 +259,9 @@ XRPNotCreated::finalize( // std::cout << "fee.drops: " << feeAdded << "\n"; // std::cout << "dropsAdded: " << dropsAdded.drops() << "\n"; // std::cout << "drops: " << drops << "\n"; - // std::cout << "drops=: " << drops_ << "\n"; + std::cout << "drops=: " << drops_ << "\n"; - // // catch any overflow or funny business + // catch any overflow or funny business // if (drops > dropsAdded.drops()) // return false; diff --git a/src/ripple/basics/impl/mulDiv.cpp b/src/ripple/basics/impl/mulDiv.cpp index 20e72e047..433c7dddd 100644 --- a/src/ripple/basics/impl/mulDiv.cpp +++ b/src/ripple/basics/impl/mulDiv.cpp @@ -30,7 +30,7 @@ mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div) { using namespace boost::multiprecision; - uint128_t result; + boost::multiprecision::uint128_t result; result = multiply(result, value, mul); result /= div; diff --git a/src/ripple/protocol/impl/InnerObjectFormats.cpp b/src/ripple/protocol/impl/InnerObjectFormats.cpp index 87cc54f94..2c6632798 100644 --- a/src/ripple/protocol/impl/InnerObjectFormats.cpp +++ b/src/ripple/protocol/impl/InnerObjectFormats.cpp @@ -66,7 +66,7 @@ InnerObjectFormats::InnerObjectFormats() sfBatchExecution.getCode(), {{sfTransactionType, soeREQUIRED}, {sfTransactionResult, soeREQUIRED}, - {sfTransactionHash, soeREQUIRED}}); + {sfTransactionHash, soeOPTIONAL}}); add(sfHookExecution.jsonName.c_str(), sfHookExecution.getCode(), diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index ab1ef8ccd..72ccddcca 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -44,6 +44,7 @@ TxFormats::TxFormats() {sfNetworkID, soeOPTIONAL}, {sfHookParameters, soeOPTIONAL}, {sfOperationLimit, soeOPTIONAL}, + {sfCloseResolution, soeOPTIONAL}, }; add(jss::AccountSet, diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index 8086edb5b..1914a5d06 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -48,17 +48,42 @@ class Batch_test : public beast::unit_test::suite ++index; } } - - void - testBatch(FeatureBitset features) + + Json::Value + addBatchTx( + Json::Value jv, + Json::Value const& tx, + jtx::Account const& account, + XRPAmount feeDrops, + std::uint32_t index, + std::uint32_t next) { - testcase("batch"); + 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] = 0; + jv[sfRawTransactions.jsonName][index][jss::RawTransaction][jss::Sequence] = 0; + jv[sfRawTransactions.jsonName][index][jss::RawTransaction][sfOperationLimit.jsonName] = index; + jv[sfRawTransactions.jsonName][index][jss::RawTransaction][sfCloseResolution.jsonName] = next; + return jv; + } + + // OnSucess -> (0) + // OnFailure -> Next Tx Index + // Ex. + // 0: MintURIToken: If Fail -> 2 + // 1: Payment: If Fail -> 2 + // 2: Payment: 0 + + void + testBadPubKey(FeatureBitset features) + { + testcase("bad pubkey"); using namespace test::jtx; using namespace std::literals; test::jtx::Env env{*this, envconfig()}; - auto const feeDrops = env.current()->fees().base; // Env env{ // *this, // envconfig(), @@ -67,14 +92,15 @@ 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"); auto const bob = Account("bob"); - auto const carol = Account("carol"); - env.fund(XRP(1000), alice, bob, carol); + env.fund(XRP(1000), alice, bob); env.close(); auto const seq = env.seq("alice"); - // ttBATCH Json::Value jv; jv[jss::TransactionType] = jss::Batch; @@ -82,39 +108,90 @@ class Batch_test : public beast::unit_test::suite // Batch Transactions jv[sfRawTransactions.jsonName] = Json::Value{Json::arrayValue}; - - // Tx 1 - jv[sfRawTransactions.jsonName][0U] = Json::Value{}; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] = Json::Value{}; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] - [jss::TransactionType] = jss::AccountSet; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] - [sfAccount.jsonName] = alice.human(); - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction][sfFee.jsonName] = - to_string(feeDrops); - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction][jss::Sequence] = - seq + 1; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] - [jss::SigningPubKey] = strHex(alice.pk()); - - // Tx 2 - jv[sfRawTransactions.jsonName][1U] = Json::Value{}; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] = Json::Value{}; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] - [jss::TransactionType] = jss::AccountSet; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] - [sfAccount.jsonName] = alice.human(); - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction][sfFee.jsonName] = - to_string(feeDrops); - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction][jss::Sequence] = - seq + 2; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] - [jss::SigningPubKey] = strHex(alice.pk()); - env(jv, fee(drops((2 * feeDrops) + (2 * feeDrops))), ter(tesSUCCESS)); + // Tx 1 + Json::Value const tx1 = pay(alice, bob, XRP(1)); + jv = addBatchTx(jv, tx1, alice, feeDrops, 0, 0); + + // Tx 2 + Json::Value const tx2 = pay(alice, bob, XRP(1)); + jv = addBatchTx(jv, tx2, bob, feeDrops, 1, 0); + + env(jv, fee(feeDrops * 2), ter(tesSUCCESS)); env.close(); + + std::array testCases = {{ + {"tesSUCCESS", "Payment"}, + {"tesSUCCESS", "Payment"}, + }}; + + 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); + + BEAST_EXPECT(env.seq(alice) == 2); + BEAST_EXPECT(env.balance(alice) == XRP(1000) - XRP(1) - (feeDrops * 2)); + BEAST_EXPECT(env.balance(bob) == XRP(1000) + XRP(1)); } + void + testUnfunded(FeatureBitset features) + { + testcase("unfunded"); + + using namespace test::jtx; + using namespace std::literals; + + test::jtx::Env env{*this, envconfig()}; + + auto const feeDrops = env.current()->fees().base; + + auto const alice = Account("alice"); + auto const bob = Account("bob"); + auto const carol = Account("carol"); + env.fund(XRP(1000), alice, bob, carol); + env.close(); + + auto const seq = env.seq("alice"); + // ttBATCH + Json::Value jv; + jv[jss::TransactionType] = jss::Batch; + jv[jss::Account] = alice.human(); + + // 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); + + // Tx 2 + Json::Value const tx2 = pay(alice, bob, XRP(999)); + jv = addBatchTx(jv, tx2, alice, feeDrops, 1, 0); + + env(jv, fee(feeDrops * 2), ter(tesSUCCESS)); + env.close(); + + std::array testCases = {{ + {"tesSUCCESS", "Payment"}, + {"tecUNFUNDED_PAYMENT", "Payment"}, + }}; + + 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); + + BEAST_EXPECT(env.seq(alice) == 2); + BEAST_EXPECT(env.balance(alice) == XRP(1000) - XRP(1) - (feeDrops * 1)); + BEAST_EXPECT(env.balance(bob) == XRP(1000) + XRP(1)); + } + void testSuccess(FeatureBitset features) { @@ -150,40 +227,12 @@ class Batch_test : public beast::unit_test::suite jv[sfRawTransactions.jsonName] = Json::Value{Json::arrayValue}; // Tx 1 - jv[sfRawTransactions.jsonName][0U] = Json::Value{}; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] = Json::Value{}; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] - [jss::TransactionType] = jss::Payment; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] - [sfAccount.jsonName] = alice.human(); - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] - [sfDestination.jsonName] = bob.human(); - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] - [sfAmount.jsonName] = "1000000"; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction][sfFee.jsonName] = - to_string(feeDrops); - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction][jss::Sequence] = - seq + 1; - jv[sfRawTransactions.jsonName][0U][jss::RawTransaction] - [jss::SigningPubKey] = strHex(alice.pk()); + Json::Value const tx1 = pay(alice, bob, XRP(1)); + jv = addBatchTx(jv, tx1, alice, feeDrops, 0, 0); // Tx 2 - jv[sfRawTransactions.jsonName][1U] = Json::Value{}; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] = Json::Value{}; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] - [jss::TransactionType] = jss::Payment; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] - [sfAccount.jsonName] = alice.human(); - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] - [sfDestination.jsonName] = bob.human(); - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] - [sfAmount.jsonName] = "1000000"; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction][sfFee.jsonName] = - to_string(feeDrops); - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction][jss::Sequence] = - seq + 2; - jv[sfRawTransactions.jsonName][1U][jss::RawTransaction] - [jss::SigningPubKey] = strHex(alice.pk()); + Json::Value const tx2 = pay(alice, bob, XRP(1)); + jv = addBatchTx(jv, tx2, alice, feeDrops, 1, 0); env(jv, fee(feeDrops * 2), ter(tesSUCCESS)); env.close(); @@ -200,7 +249,7 @@ class Batch_test : public beast::unit_test::suite auto const meta = jrr[jss::result][jss::meta]; validateBatchTxns(meta, testCases); - BEAST_EXPECT(env.seq(alice) == 4); + BEAST_EXPECT(env.seq(alice) == 2); BEAST_EXPECT(env.balance(alice) == XRP(1000) - XRP(2) - (feeDrops * 2)); BEAST_EXPECT(env.balance(bob) == XRP(1000) + XRP(2)); } @@ -325,8 +374,9 @@ class Batch_test : public beast::unit_test::suite void testWithFeats(FeatureBitset features) { - // testBatch(features); - testSuccess(features); + // testBadPubKey(features); + testUnfunded(features); + // testSuccess(features); // testSingle(features); // testInvalidBatch(features); }