From f73d26b9fe3323871b409be770d460d6e4ee15dc Mon Sep 17 00:00:00 2001 From: Richard Holland Date: Mon, 9 Jan 2023 13:45:57 +0000 Subject: [PATCH] emit testcase work in progress --- src/ripple/app/hook/applyHook.h | 2 +- src/ripple/app/hook/impl/applyHook.cpp | 10 +- src/test/app/SetHook_test.cpp | 171 ++++++++++++++++++++----- 3 files changed, 145 insertions(+), 38 deletions(-) diff --git a/src/ripple/app/hook/applyHook.h b/src/ripple/app/hook/applyHook.h index 7405660db..dbf2d0316 100644 --- a/src/ripple/app/hook/applyHook.h +++ b/src/ripple/app/hook/applyHook.h @@ -433,7 +433,7 @@ namespace hook public: - HookContext hookCtx; + HookContext& hookCtx; WasmEdge_ModuleInstanceContext* importObj; /** diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index bc29c0b47..405fb6bcd 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -1143,7 +1143,6 @@ lookup_state_cache( ripple::uint256 const& ns, ripple::uint256 const& key) { - std::cout << "Lookup_state_cache: acc: " << acc << " ns: " << ns << " key: " << key << "\n"; auto& stateMap = hookCtx.result.stateMap; if (stateMap.find(acc) == stateMap.end()) return std::nullopt; @@ -1960,14 +1959,14 @@ DEFINE_HOOK_FUNCNARG( auto const& tx = applyCtx.tx; if (!tx.isFieldPresent(sfEmitDetails)) - return 1; // generation is always 1 if the tx wasn't a emit + return 0; // generation is always 0 if the tx wasn't a emit auto const& pd = const_cast(tx).getField(sfEmitDetails).downcast(); if (!pd.isFieldPresent(sfEmitGeneration)) { JLOG(j.warn()) << "HookError[" << HC_ACC() << "]: found sfEmitDetails but sfEmitGeneration was not present"; - return 1; + return 0; } hookCtx.generation = pd.getFieldU32(sfEmitGeneration); @@ -2811,11 +2810,6 @@ DEFINE_HOOK_FUNCTION( ripple::Blob blob{memory + read_ptr, memory + read_ptr + read_len}; - DBG_PRINTF("hook is emitting tx:-----\n"); - for (unsigned char c: blob) - DBG_PRINTF("%02X", c); - DBG_PRINTF("\n--------\n"); - std::shared_ptr stpTrans; try { diff --git a/src/test/app/SetHook_test.cpp b/src/test/app/SetHook_test.cpp index 79a1168dd..f8644b6c7 100644 --- a/src/test/app/SetHook_test.cpp +++ b/src/test/app/SetHook_test.cpp @@ -1875,7 +1875,7 @@ public: { testcase("Test float_compare"); using namespace jtx; - Env env{*this, supported_amendments()}; + Env env{*this, envconfig(), supported_amendments(), nullptr, beast::severities::kTrace}; auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -1891,6 +1891,11 @@ public: extern int64_t etxn_reserve(uint32_t); extern int64_t otxn_param(uint32_t, uint32_t, uint32_t, uint32_t); extern int64_t hook_account(uint32_t, uint32_t); + extern int64_t otxn_field ( + uint32_t write_ptr, + uint32_t write_len, + uint32_t field_id + ); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) #define OUT_OF_BOUNDS (-1) #define ttPAYMENT 0 @@ -2132,22 +2137,37 @@ public: if (!(x))\ rollback((uint32_t)#x,sizeof(#x),__LINE__) + #define sfDestination ((8U << 16U) + 3U) + + extern int64_t trace(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); int64_t cbak(uint32_t r) { + // on callback we emit 2 more txns + uint8_t bob[20]; + ASSERT(otxn_field(SBUF(bob), sfDestination) == 20); + + ASSERT(etxn_reserve(2) == 2); + uint8_t tx[PREPARE_PAYMENT_SIMPLE_SIZE]; + PREPARE_PAYMENT_SIMPLE(tx, 1000, bob, 0, 0); + + trace("cbaktx", 6, tx, sizeof(tx), 1); + uint8_t hash1[32]; + ASSERT(emit(SBUF(hash1), SBUF(tx)) == 32); + + ASSERT(etxn_details(tx + 132, 138) == 138); + uint8_t hash2[32]; + ASSERT(emit(SBUF(hash2), SBUF(tx)) == 32); + + ASSERT(!BUFFER_EQUAL_32(hash1, hash2)); + + return accept(0,0,0); } int64_t hook(uint32_t r) { _g(1,1); - - // emit txns based on otxn_params - uint8_t emitcount[2]; - ASSERT(otxn_param(SBUF(emitcount), "count", 5) == 2); - - uint16_t ec = UINT16_FROM_BUF(emitcount); - ASSERT(ec > 0); - etxn_reserve(ec); + etxn_reserve(1); // bounds checks ASSERT(emit(1000000, 32, 0, 32) == OUT_OF_BOUNDS); @@ -2162,23 +2182,8 @@ public: uint8_t tx[PREPARE_PAYMENT_SIMPLE_SIZE]; PREPARE_PAYMENT_SIMPLE(tx, 1000, bob, 0, 0); - uint8_t tmp[32]; - uint8_t incr = otxn_param(SBUF(tmp), "inc", 3) != DOESNT_EXIST; - - uint8_t hashes[64]; - - for (int i = 0; GUARD(64), i < ec; ++i) - { - if (incr) - { - tx[6] = (uint8_t)(i >> 8U); - tx[7] = (uint8_t)(i & 0xFFU); - } - ASSERT(emit(hashes + (i % 2) * 32, 32, SBUF(tx)) == 32); - - // assert the hash isn't the same as the previous txn - ASSERT(!BUFFER_EQUAL_32(hashes, hashes + 32)); - } + uint8_t hash[32]; + ASSERT(emit(SBUF(hash), SBUF(tx)) == 32); return accept(0,0,0); } @@ -2197,12 +2202,120 @@ public: params[0U][jss::HookParameter][jss::HookParameterName ] = strHex(std::string("bob")); params[0U][jss::HookParameter][jss::HookParameterValue] = strHex(bob.id()); - params[1U][jss::HookParameter][jss::HookParameterName ] = strHex(std::string("count")); - params[1U][jss::HookParameter][jss::HookParameterValue] = "0001"; - invoke[jss::HookParameters] = params; env(invoke, M("test otxn_param"), fee(XRP(1))); + + std::optional emithash; + { + auto meta = env.meta(); + + // ensure hook execution occured + BEAST_REQUIRE(meta); + BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions)); + + auto const hookExecutions = + meta->getFieldArray(sfHookExecutions); + BEAST_REQUIRE(hookExecutions.size() == 1); + + // ensure there was one emitted txn + BEAST_EXPECT(hookExecutions[0].getFieldU16(sfHookEmitCount) == 1); + + BEAST_REQUIRE(meta->isFieldPresent(sfAffectedNodes)); + + BEAST_REQUIRE(meta->getFieldArray(sfAffectedNodes).size() == 3); + + for (auto const& node : meta->getFieldArray(sfAffectedNodes)) + { + SField const& metaType = node.getFName(); + uint16_t nodeType = node.getFieldU16(sfLedgerEntryType); + if (metaType == sfCreatedNode && nodeType == ltEMITTED_TXN) + { + BEAST_REQUIRE(node.isFieldPresent(sfNewFields)); + + auto const& nf = + const_cast(node).getField(sfNewFields).downcast(); + + auto const& et = + const_cast(nf).getField(sfEmittedTxn).downcast(); + + auto const& em = + const_cast(et).getField(sfEmitDetails).downcast(); + + BEAST_EXPECT(em.getFieldU32(sfEmitGeneration) == 1); + BEAST_EXPECT(em.getFieldU64(sfEmitBurden) == 1); + + Blob txBlob = et.getSerializer().getData(); + auto const tx = std::make_unique(Slice { txBlob.data(), txBlob.size() }); + emithash = tx->getTransactionID(); + + break; + } + } + + BEAST_REQUIRE(emithash); + } + + { + auto balbefore = env.balance(bob).value().xrp().drops(); + + env.close(); + + auto const ledger = env.closed(); + + int txcount = 0; + for (auto& i : ledger->txs) + { + auto const& hash = i.first->getTransactionID(); + txcount++; + BEAST_EXPECT(hash == *emithash); + { + std::cout << "tx :: " << hash << ":\n"; + Serializer sTxn = i.first->getSerializer(); + Serializer sMeta = i.second->getSerializer(); + Slice txn { sTxn.data(), sTxn.getLength() }; + Slice meta { sMeta.data(), sMeta.getLength() }; + std::cout << strHex(txn) << "\n"; + std::cout << strHex(meta) << "\n"; + } + } + + BEAST_EXPECT(txcount == 1); + + env.close(); + + auto balafter = env.balance(bob).value().xrp().drops(); + + BEAST_EXPECT(balafter - balbefore == 1000); + } + + //for (int i = 0; i < 10; ++i) + // env.close(); +/* + { + auto const ledger = env.closed(); + for (auto& i : ledger->txs) + { + auto const& hash = i.first->getTransactionID(); + { + std::cout << "tx :: " << hash << ":\n"; + Serializer sTxn = i.first->getSerializer(); + Serializer sMeta = i.second->getSerializer(); + Slice txn { sTxn.data(), sTxn.getLength() }; + Slice meta { sMeta.data(), sMeta.getLength() }; + std::cout << strHex(txn) << "\n"; + std::cout << strHex(meta) << "\n"; + } + } + } +*/ + // at this point there should now be two emitted txns, spawned by the first emitted txn's callback + +/* + } +*/ + // find emitted txn + // RH UPTO: check emit count = 1, check that the emitted txn is correct // cbak