From f96d9b6e5161f2b6b19b5c146f6d97ad9bdb83e3 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 20 Jan 2026 12:12:45 +0900 Subject: [PATCH 01/12] Add tests for Hooks fee --- src/test/app/SetHook_test.cpp | 567 +++++++++++++++++++++++----------- 1 file changed, 394 insertions(+), 173 deletions(-) diff --git a/src/test/app/SetHook_test.cpp b/src/test/app/SetHook_test.cpp index 4c2fc39d3..16c06d7be 100644 --- a/src/test/app/SetHook_test.cpp +++ b/src/test/app/SetHook_test.cpp @@ -75,6 +75,16 @@ using JSSMap = [[maybe_unused]] std::string const x##_hash_str = to_string(x##_hash); \ [[maybe_unused]] Keylet const x##_keylet = keylet::hookDefinition(x##_hash); +#define EXPECT_HOOK_FEE(x, fee) \ + do \ + { \ + auto const hookSLE = env.le(x##_keylet); \ + BEAST_EXPECTS( \ + hookSLE->getFieldAmount(sfFee) == XRPAmount{fee}, \ + "Hook fee mismatch: expected " #fee " got " + \ + to_string(hookSLE->getFieldAmount(sfFee))); \ + } while (false) + class SetHook0_test : public beast::unit_test::suite { private: @@ -2710,6 +2720,7 @@ public: M("Install Accept Hook"), HSFEE); env.close(); + EXPECT_HOOK_FEE(accept, 9); env(pay(bob, alice, XRP(1)), M("Test Accept Hook"), fee(XRP(1))); env.close(); @@ -2731,6 +2742,7 @@ public: M("Install Rollback Hook"), HSFEE); env.close(); + EXPECT_HOOK_FEE(rollback, 9); env(pay(bob, alice, XRP(1)), M("Test Rollback Hook"), @@ -2793,7 +2805,7 @@ public: // same loop again but with a guard call { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( (module (type (;0;) (func (param i32 i32) (result i64))) (type (;1;) (func (param i32 i32) (result i32))) @@ -2828,15 +2840,17 @@ public: (export "hook" (func 2))) )[test.hook]"]; - env(ripple::test::jtx::hook(alice, {{hso(hook)}}, 0), + HASH_WASM(hook); + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm)}}, 0), M("Loop 1 with guards"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 14); } // simple looping, c { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -2852,11 +2866,14 @@ public: return accept(0,0,2); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("Loop 2 in C"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 100); env(pay(bob, alice, XRP(1)), M("Test Loop 2"), fee(XRP(1))); env.close(); @@ -2864,7 +2881,7 @@ public: // complex looping, c { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -2896,11 +2913,14 @@ public: return accept(0,0,2); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("Loop 3 in C"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 1944); env(pay(bob, alice, XRP(1)), M("Test Loop 3"), fee(XRP(1))); env.close(); @@ -2908,7 +2928,7 @@ public: // complex looping missing a guard { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -2942,7 +2962,8 @@ public: } )[test.hook]"]; - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("Loop 4 in C"), HSFEE, ter(temMALFORMED)); @@ -2963,7 +2984,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g(uint32_t, uint32_t); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -3284,10 +3305,12 @@ public: } )[test.hook]"]; - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + HASH_WASM(hook); + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set emit"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 342); Json::Value invoke; invoke[jss::TransactionType] = "Invoke"; @@ -3858,7 +3881,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -3931,12 +3954,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set etxn_details"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 88); // invoke the hook env(pay(bob, alice, XRP(1)), M("test etxn_details"), fee(XRP(1))); @@ -3952,7 +3977,7 @@ public: auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4013,8 +4038,9 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); + HASH_WASM(hook); // install the hook on alice - auto hsobj = hso(hook, overrideFlag); + auto hsobj = hso(hook_wasm, overrideFlag); hsobj[jss::HookOn] = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFF" "FE"; // payment high @@ -4022,6 +4048,7 @@ public: M("set etxn_fee_base"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 77); // invoke the hook env(pay(bob, alice, XRP(1)), M("test etxn_fee_base"), fee(XRP(1))); @@ -4057,7 +4084,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4097,12 +4124,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set etxn_nonce"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 11644); // invoke the hook env(pay(bob, alice, XRP(1)), M("test etxn_nonce"), fee(XRP(1))); @@ -4122,7 +4151,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4147,12 +4176,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set etxn_reserve"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 69); // invoke the hook env(pay(bob, alice, XRP(1)), M("test etxn_reserve"), fee(XRP(1))); @@ -4171,7 +4202,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4188,12 +4219,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set fee_base"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 20); // invoke the hook env(pay(bob, alice, XRP(1)), M("test fee_base"), fee(XRP(1))); @@ -4212,7 +4245,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4321,11 +4354,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_compare"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 583); env(pay(bob, alice, XRP(1)), M("test float_compare"), fee(XRP(1))); env.close(); @@ -4345,7 +4381,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4520,11 +4556,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_divide"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 1799); env(pay(bob, alice, XRP(1)), M("test float_divide"), fee(XRP(1))); env.close(); @@ -4544,7 +4583,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4649,11 +4688,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_int"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 1178); env(pay(bob, alice, XRP(1)), M("test float_int"), fee(XRP(1))); env.close(); @@ -4673,7 +4715,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4737,11 +4779,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_invert"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 329); env(pay(bob, alice, XRP(1)), M("test float_invert"), fee(XRP(1))); env.close(); @@ -4761,7 +4806,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4820,11 +4865,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_log"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 388); env(pay(bob, alice, XRP(1)), M("test float_log"), fee(XRP(1))); env.close(); @@ -4844,7 +4892,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -4949,11 +4997,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_mantissa"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 309); env(pay(bob, alice, XRP(1)), M("test float_mantissa"), fee(XRP(1))); env.close(); @@ -4973,7 +5024,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -5104,11 +5155,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_mulratio"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 1683); env(pay(bob, alice, XRP(1)), M("test float_mulratio"), fee(XRP(1))); env.close(); @@ -5128,7 +5182,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -5403,12 +5457,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_multiply"), HSFEE); env.close(); - + EXPECT_HOOK_FEE(hook, 3180); env(pay(bob, alice, XRP(1)), M("test float_multiply"), fee(XRP(1))); env.close(); } @@ -5427,7 +5483,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -5474,11 +5530,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_negate"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 105); env(pay(bob, alice, XRP(1)), M("test float_negate"), fee(XRP(1))); env.close(); @@ -5498,7 +5557,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -5515,11 +5574,14 @@ public: : rollback(0,0,1); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_one"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 20); env(pay(bob, alice, XRP(1)), M("test float_one"), fee(XRP(1))); env.close(); @@ -5539,7 +5601,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -5593,11 +5655,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_root"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 257); env(pay(bob, alice, XRP(1)), M("test float_root"), fee(XRP(1))); env.close(); @@ -5617,7 +5682,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -5666,11 +5731,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_set"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 343); env(pay(bob, alice, XRP(1)), M("test float_set"), fee(XRP(1))); env.close(); @@ -5690,7 +5758,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -5779,11 +5847,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_sign"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 296); env(pay(bob, alice, XRP(1)), M("test float_sign"), fee(XRP(1))); env.close(); @@ -5803,7 +5874,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -5984,11 +6055,14 @@ public: } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_sto"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 920); env(pay(bob, alice, XRP(1)), M("test float_sto"), fee(XRP(1))); env.close(); @@ -6008,7 +6082,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -6126,11 +6200,14 @@ public: } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_sto_set"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 187); env(pay(bob, alice, XRP(1)), M("test float_sto_set"), fee(XRP(1))); env.close(); @@ -6150,7 +6227,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -6306,11 +6383,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set float_sum"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 1735); env(pay(bob, alice, XRP(1)), M("test float_sum"), fee(XRP(1))); env.close(); @@ -6330,7 +6410,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -6357,12 +6437,15 @@ public: accept((uint32_t)acc, 20, 0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set hook_account"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 72); // invoke the hook env(pay(bob, alice, XRP(1)), M("test hook_account"), fee(XRP(1))); @@ -6390,7 +6473,8 @@ public: } // install the same hook bob - env(ripple::test::jtx::hook(bob, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + bob, {{hso(hook_wasm, overrideFlag)}}, 0), M("set hook_account 2"), HSFEE); env.close(); @@ -6452,7 +6536,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -6481,12 +6565,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set hook_again"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 54); env(pay(bob, alice, XRP(1)), M("test hook_again"), fee(XRP(1))); env.close(); @@ -6529,7 +6615,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -6555,12 +6641,15 @@ public: accept((uint32_t)hash, 32, 0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set hook_hash"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 62); // invoke the hook env(pay(bob, alice, XRP(1)), M("test hook_hash"), fee(XRP(1))); @@ -6588,7 +6677,7 @@ public: BEAST_EXPECT(memcmp(hash.data(), retStr.data(), 32) == 0); } - TestHook hook2 = wasm[R"[test.hook]( + TestHook hook2_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -6614,12 +6703,15 @@ public: accept((uint32_t)hash, 32, 0); } )[test.hook]"]; + HASH_WASM(hook2); // install a slightly different hook on bob - env(ripple::test::jtx::hook(bob, {{hso(hook2, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + bob, {{hso(hook2_wasm, overrideFlag)}}, 0), M("set hook_hash 2"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook2, 62); // invoke the hook env(pay(bob, alice, XRP(1)), M("test hook_hash 2"), fee(XRP(1))); @@ -6662,11 +6754,8 @@ public: BEAST_EXPECT(memcmp(hash1.data(), hash2.data(), 32) != 0); // compute the hashes - auto computedHash2 = ripple::sha512Half_s( - ripple::Slice(hook.data(), hook.size())); - - auto computedHash1 = ripple::sha512Half_s( - ripple::Slice(hook2.data(), hook2.size())); + auto computedHash2 = hook_hash; + auto computedHash1 = hook2_hash; // ensure the computed hashes match BEAST_EXPECT(computedHash1 == hash1); @@ -6689,7 +6778,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -6771,6 +6860,7 @@ public: } )[test.hook]"]; + HASH_WASM(hook); Json::Value jv; jv[jss::Account] = alice.human(); @@ -6779,7 +6869,7 @@ public: jv[jss::Hooks] = Json::Value{Json::arrayValue}; Json::Value iv; - iv[jss::CreateCode] = strHex(hook); + iv[jss::CreateCode] = strHex(hook_wasm); iv[jss::HookOn] = "0000000000000000000000000000000000000000000000000000000000000000"; iv[jss::HookApiVersion] = 0U; @@ -6798,6 +6888,7 @@ public: jv[jss::Hooks][0U][jss::Hook] = iv; env(jv, M("set hook_param"), HSFEE, ter(tesSUCCESS)); env.close(); + EXPECT_HOOK_FEE(hook, 2412); // invoke env(pay(bob, alice, XRP(1)), M("test hook_param"), fee(XRP(1))); @@ -6958,6 +7049,7 @@ public: )[test.hook]"]; HASH_WASM(checker); + HASH_WASM(setter); Json::Value jv; jv[jss::Account] = alice.human(); @@ -7007,6 +7099,8 @@ public: env(jv, M("set hook_param_set"), HSFEE, ter(tesSUCCESS)); env.close(); + EXPECT_HOOK_FEE(checker, 475); + EXPECT_HOOK_FEE(setter, 759); // invoke env(pay(bob, alice, XRP(1)), M("test hook_param_set"), fee(XRP(1))); @@ -7025,7 +7119,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -7037,14 +7131,21 @@ public: accept(0,0,hook_pos()); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice in all four spots env(ripple::test::jtx::hook( - alice, {{hso(hook), hso(hook), hso(hook), hso(hook)}}, 0), + alice, + {{hso(hook_wasm), + hso(hook_wasm), + hso(hook_wasm), + hso(hook_wasm)}}, + 0), M("set hook_pos"), HSFEE, ter(tesSUCCESS)); env.close(); + EXPECT_HOOK_FEE(hook, 11); // invoke the hooks env(pay(bob, alice, XRP(1)), M("test hook_pos"), fee(XRP(1))); @@ -7151,6 +7252,7 @@ public: } )[test.hook]"]; + HASH_WASM(skip); HASH_WASM(pos); // install the hook on alice in one places @@ -7165,6 +7267,8 @@ public: HSFEE, ter(tesSUCCESS)); env.close(); + EXPECT_HOOK_FEE(skip, 263); + EXPECT_HOOK_FEE(pos, 11); // invoke the hooks { @@ -7201,7 +7305,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -7279,12 +7383,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set ledger_keylet"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 415); env(pay(bob, alice, XRP(1)), M("test ledger_keylet"), fee(XRP(1))); env.close(); @@ -7303,7 +7409,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -7328,12 +7434,14 @@ public: accept((uint32_t)hash, 32, 0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set ledger_last_hash"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 59); for (uint32_t i = 0; i < 3; ++i) { @@ -7377,7 +7485,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -7389,12 +7497,14 @@ public: accept(0,0,ledger_last_time()); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set ledger_last_time"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 11); // invoke the hook a few times for (uint32_t i = 0; i < 3; ++i) @@ -7445,7 +7555,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -7472,12 +7582,14 @@ public: accept((uint32_t)nonce, 64, 0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set ledger_nonce"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 74); // invoke the hook auto const seq = @@ -7543,7 +7655,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -7555,12 +7667,14 @@ public: accept(0,0,ledger_seq()); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set ledger_seq"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 11); // invoke the hook a few times for (uint32_t i = 0; i < 3; ++i) @@ -7602,7 +7716,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -7652,12 +7766,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set meta_slot"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 139); env(pay(bob, alice, XRP(1)), M("test meta_slot"), fee(XRP(1))); env.close(); @@ -7699,7 +7815,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -7765,6 +7881,7 @@ public: return accept(0,0,2); } )[test.hook]"]; + HASH_WASM(hook); // before featureHooksUpdate1 env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), @@ -7777,10 +7894,11 @@ public: env.close(); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set xpop_slot"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 3245); auto checkResult = [this](auto const& meta, uint64_t expectedCode) -> void { @@ -7820,7 +7938,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -7867,12 +7985,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set otxn_field"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 732); // invoke the hook env(pay(alice, bob, XRP(1)), M("test otxn_field"), fee(XRP(1))); @@ -7890,7 +8010,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -7950,12 +8070,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set otxn_id"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 1077); // invoke the hook env(pay(bob, alice, XRP(1)), M("test otxn_id"), fee(XRP(1))); @@ -7973,7 +8095,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8032,12 +8154,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set otxn_slot"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 6442); // invoke the hook env(pay(bob, alice, XRP(1)), M("test otxn_slot"), fee(XRP(1))); @@ -8055,7 +8179,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8092,12 +8216,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set otxn_type"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 51); // invoke the hook env(pay(bob, alice, XRP(1)), M("test otxn_type"), fee(XRP(1))); @@ -8126,7 +8252,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -8208,12 +8334,14 @@ public: } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set otxn_param"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 2412); // invoke Json::Value invoke; @@ -8249,7 +8377,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8339,12 +8467,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 232); // invoke the hook env(pay(bob, alice, XRP(1)), M("test slot"), fee(XRP(1))); @@ -8362,7 +8492,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8397,12 +8527,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot_clear"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 83); // invoke the hook env(pay(bob, alice, XRP(1)), M("test slot_clear"), fee(XRP(1))); @@ -8420,7 +8552,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8462,12 +8594,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot_count"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 97); // invoke the hook env(pay(bob, alice, XRP(1)), M("test slot_count"), fee(XRP(1))); @@ -8485,7 +8619,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8537,12 +8671,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot_float"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 112); // invoke the hook env(pay(bob, alice, XRP(1)), M("test slot_float"), fee(XRP(1))); @@ -8560,7 +8696,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8647,12 +8783,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot_set"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 11653); // invoke the hook env(pay(bob, alice, XRP(1)), M("test slot_set"), fee(XRP(1))); @@ -8670,7 +8808,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8726,12 +8864,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot_size"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 114); // invoke the hook env(pay(bob, alice, XRP(1)), M("test slot_size"), fee(XRP(1))); @@ -8750,7 +8890,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8847,12 +8987,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot_subarray"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 6212); // generate an array of memos to attach Json::Value jv; @@ -8890,7 +9032,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -8967,12 +9109,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot_subfield"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 6109); // invoke the hook env(pay(bob, alice, XRP(1)), M("test slot_subfield"), fee(XRP(1))); @@ -8993,7 +9137,7 @@ public: // set up a trustline which we can retrieve later env(trust(alice, bob["USD"](600))); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -9106,12 +9250,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set slot_subfield"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 284); // invoke the hook env(pay(bob, alice, XRP(1)), M("test slot_type"), fee(XRP(1))); @@ -9131,7 +9277,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -9190,12 +9336,15 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set state"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 2254); // invoke the hook env(pay(bob, alice, XRP(1)), M("test state"), fee(XRP(1))); @@ -9205,7 +9354,7 @@ public: // override hook with a second version that just reads those state // objects { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -9246,12 +9395,15 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set state 2"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 2134); // invoke the hook env(pay(bob, alice, XRP(1)), M("test state 2"), fee(XRP(1))); @@ -9272,7 +9424,7 @@ public: env.fund(XRP(10000), bob); { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -9312,12 +9464,15 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set state_foreign"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 72); // invoke the hook env(pay(bob, alice, XRP(1)), M("test state_foreign"), fee(XRP(1))); @@ -9325,7 +9480,7 @@ public: // set a second hook on bob that will read the state objects from alice { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -9402,12 +9557,15 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on bob - env(ripple::test::jtx::hook(bob, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + bob, {{hso(hook_wasm, overrideFlag)}}, 0), M("set state_foreign 2"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 2408); // invoke the hook @@ -9423,7 +9581,7 @@ public: testcase("Test state_foreign_set max"); using namespace jtx; - static const std::vector ns_maxHook = { + static const std::vector ns_maxHook_wasm = { 0x00U, 0x61U, 0x73U, 0x6dU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U, 0x36U, 0x07U, 0x60U, 0x02U, 0x7fU, 0x7fU, 0x01U, 0x7fU, 0x60U, 0x02U, 0x7fU, 0x7fU, 0x01U, 0x7eU, 0x60U, 0x03U, 0x7fU, 0x7fU, @@ -9483,6 +9641,7 @@ public: 0x52U, 0x65U, 0x61U, 0x63U, 0x68U, 0x65U, 0x64U, 0x00U, 0x6bU, 0x65U, 0x79U, 0x32U, 0x00U, 0x63U, 0x6fU, 0x6eU, 0x74U, 0x65U, 0x6eU, 0x74U, 0x32U}; + HASH_WASM(ns_maxHook); Env env{*this, features}; @@ -9493,10 +9652,11 @@ public: // install the hook on alice env(ripple::test::jtx::hook( - alice, {{hso(ns_maxHook, overrideFlag)}}, 0), + alice, {{hso(ns_maxHook_wasm, overrideFlag)}}, 0), M("set state_foreign_set_max"), HSFEE); env.close(); + EXPECT_HOOK_FEE(ns_maxHook, 103); // invoke the hook for (uint32_t i = 0; i < 255; ++i) @@ -9674,6 +9834,7 @@ public: env(json, M("set state_foreign_set"), HSFEE); env.close(); + EXPECT_HOOK_FEE(grantor, 103); } // install the grantee hook on bob @@ -9683,6 +9844,7 @@ public: bob, {{hso(grantee_wasm, overrideFlag)}}, 0); env(json, M("set state_foreign_set 2"), HSFEE); env.close(); + EXPECT_HOOK_FEE(grantee, 234); } auto const aliceid = Account("alice").id(); @@ -9944,6 +10106,7 @@ public: bob, {{hso(exhaustion_wasm, overrideFlag)}}, 0); env(json, M("set state_foreign_set 12"), HSFEE); env.close(); + EXPECT_HOOK_FEE(exhaustion, 10582); } // now invoke repeatedly until exhaustion is reached @@ -10044,7 +10207,7 @@ public: // bounds and buffer size checks { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -10087,12 +10250,15 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set state_set 1"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 143); BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1); @@ -10140,7 +10306,7 @@ public: // first hook will set two state objects with different keys and data on // alice { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -10211,12 +10377,15 @@ public: } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set state_set 1"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 85); BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1); @@ -10302,7 +10471,7 @@ public: // make amother hook to override an existing state and delete an // existing state { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -10357,8 +10526,9 @@ public: } )[test.hook]"]; + HASH_WASM(hook); - TestHook hook2 = wasm[R"[test.hook]( + TestHook hook2_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -10402,14 +10572,21 @@ public: } )[test.hook]"]; + HASH_WASM(hook2); + // install the hook on alice env(ripple::test::jtx::hook( alice, - {{{hso(hook, overrideFlag)}, {}, {}, {hso(hook2, 0)}}}, + {{{hso(hook_wasm, overrideFlag)}, + {}, + {}, + {hso(hook2_wasm, 0)}}}, 0), M("set state_set 2"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 82); + EXPECT_HOOK_FEE(hook2, 525); // two hooks + two state objects = 4 BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 4); @@ -10418,7 +10595,7 @@ public: // updated state is also available on his side. caution must be // taken because bob's hooks will execute first if bob's is the // otxn. therefore we will flip to a payment from alice to bob here - TestHook hook3 = wasm[R"[test.hook]( + TestHook hook3_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -10478,12 +10655,15 @@ public: } )[test.hook]"]; + HASH_WASM(hook3); // install the hook on bob - env(ripple::test::jtx::hook(bob, {{hso(hook3, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + bob, {{hso(hook3_wasm, overrideFlag)}}, 0), M("set state_set 3"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook3, 560); // invoke the hook with cho (rollback after alice's hooks have // executed) @@ -10536,7 +10716,7 @@ public: // create a hook state inside the weak side of an execution, while the // strong side is rolled back { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -10580,15 +10760,17 @@ public: } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice, deleting the other hook env(ripple::test::jtx::hook( alice, - {{{hso(hook, overrideFlag)}, {}, {}, {hso_delete()}}}, + {{{hso(hook_wasm, overrideFlag)}, {}, {}, {hso_delete()}}}, 0), M("set state_set 4"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 52); // invoke from alice to cho, this will cause a rollback, however the // hook state should still be updated because the hook specified @@ -10701,6 +10883,7 @@ public: env(json, M("set state_set 6"), HSFEE); env.close(); + EXPECT_HOOK_FEE(exhaustion, 54114); } // now invoke repeatedly until exhaustion is reached @@ -10853,6 +11036,7 @@ public: to_string(UINT256_BIT[ttACCOUNT_SET]); env(jv, M("Create scaled state hook"), HSFEE, ter(tesSUCCESS)); env.close(); + EXPECT_HOOK_FEE(scaled_state, 227); BEAST_EXPECT((*env.le(gary))[sfOwnerCount] == 1); BEAST_EXPECT(!env.le(gary)->isFieldPresent(sfHookStateCount)); @@ -10903,7 +11087,7 @@ public: // tests for set_state_cache if (extHookStateEnabled) { - TestHook extended_state_reserve_hook = wasm[R"[test.hook]( + TestHook extended_state_reserve_hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -10948,14 +11132,17 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(extended_state_reserve_hook); // install the hook on gary - Json::Value jv = hso(extended_state_reserve_hook, overrideFlag); + Json::Value jv = + hso(extended_state_reserve_hook_wasm, overrideFlag); jv[jss::HookOn] = "fffffffffffffffffffffffffffffffffffffff7ffffffffffffffffff" "bfffff"; // only invoke high env(ripple::test::jtx::hook(hank, {{jv}}, 0), HSFEE); env.close(); + EXPECT_HOOK_FEE(extended_state_reserve_hook, 95); Json::Value jv1 = noop(hank); jv1[sfHookStateScale.fieldName] = 8; @@ -10998,7 +11185,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11168,19 +11355,22 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set sto_emplace"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 15024); // invoke the hook env(pay(bob, alice, XRP(1)), M("test sto_emplace"), fee(XRP(1))); } { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11217,6 +11407,7 @@ public: accept(0,0,result); } )[test.hook]"]; + HASH_WASM(hook); for (auto f : {features, features - fixHookAPI20251128}) { @@ -11229,10 +11420,11 @@ public: // install the hook on alice env(ripple::test::jtx::hook( - alice, {{hso(hook, overrideFlag)}}, 0), + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set sto_emplace"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 36); // invoke the hook env(pay(bob, alice, XRP(1)), @@ -11270,7 +11462,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11383,12 +11575,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set sto_erase"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 10021); // invoke the hook env(pay(bob, alice, XRP(1)), M("test sto_erase"), fee(XRP(1))); @@ -11408,7 +11602,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11458,19 +11652,22 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set sto_subarray"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 95); // invoke the hook env(pay(bob, alice, XRP(1)), M("test sto_subarray"), fee(XRP(1))); } { - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11501,6 +11698,7 @@ public: accept(0,0,result1+result2); } )[test.hook]"]; + HASH_WASM(hook); for (auto isfixHookAPI20251128 : {true, false}) { @@ -11513,10 +11711,11 @@ public: // install the hook on alice env(ripple::test::jtx::hook( - alice, {{hso(hook, overrideFlag)}}, 0), + alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set sto_subarray"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 19); // invoke the hook env(pay(bob, alice, XRP(1)), @@ -11570,7 +11769,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11630,12 +11829,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set sto_subfield"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 123); // invoke the hook env(pay(bob, alice, XRP(1)), M("test sto_subfield"), fee(XRP(1))); @@ -11654,7 +11855,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11713,12 +11914,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set sto_validate"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 130); // invoke the hook env(pay(bob, alice, XRP(1)), M("test sto_validate"), fee(XRP(1))); @@ -11917,7 +12120,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11943,12 +12146,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set trace"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 103); // invoke the hook env(pay(bob, alice, XRP(1)), M("test trace"), fee(XRP(1))); @@ -11967,7 +12172,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -11987,12 +12192,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set trace_float"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 37); // invoke the hook env(pay(bob, alice, XRP(1)), M("test trace_float"), fee(XRP(1))); @@ -12011,7 +12218,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -12031,12 +12238,14 @@ public: return accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set trace_num"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 37); // invoke the hook env(pay(bob, alice, XRP(1)), M("test trace_num"), fee(XRP(1))); @@ -12053,7 +12262,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -12299,12 +12508,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set util_accid"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 3101); // invoke the hook env(pay(bob, alice, XRP(1)), M("test util_accid"), fee(XRP(1))); @@ -12323,7 +12534,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -12980,12 +13191,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set util_keylet"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 1546); // invoke the hook env(pay(bob, alice, XRP(1)), M("test util_keylet"), fee(XRP(1))); @@ -13003,7 +13216,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -13433,12 +13646,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set util_raddr"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 4279); // invoke the hook env(pay(bob, alice, XRP(1)), M("test util_raddr"), fee(XRP(1))); @@ -13456,7 +13671,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -13806,12 +14021,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set util_sha512h"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 2875); // invoke the hook env(pay(bob, alice, XRP(1)), M("test util_sha512h"), fee(XRP(1))); @@ -13829,7 +14046,7 @@ public: env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) @@ -13920,12 +14137,14 @@ public: accept(0,0,0); } )[test.hook]"]; + HASH_WASM(hook); // install the hook on alice - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set util_verify"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 230); // invoke the hook env(pay(bob, alice, XRP(1)), M("test util_verify"), fee(XRP(1))); @@ -13950,7 +14169,7 @@ public: env.fund(XRP(10000), hookacc); env.close(); - TestHook hook = wasm[R"[test.hook]( + TestHook hook_wasm = wasm[R"[test.hook]( #include extern int32_t _g(uint32_t, uint32_t); extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); @@ -14279,6 +14498,7 @@ public: } } )[test.hook]"]; + HASH_WASM(hook); bool const hasFeature = env.current()->rules().enabled(featureHookCanEmit); @@ -14314,15 +14534,16 @@ public: if (i == 1) { - Json::Value h = hso(hook, overrideFlag); + Json::Value h = hso(hook_wasm, overrideFlag); env(ripple::test::jtx::hook(hookacc, {{h}}, 0), M("set hookcanemit"), HSFEE); env.close(); + EXPECT_HOOK_FEE(hook, 755); } else if (i == 2) { - Json::Value h = hso(hook, overrideFlag); + Json::Value h = hso(hook_wasm, overrideFlag); env(ripple::test::jtx::hook(acc, {{h}}, 0), M("set hookcanemit"), HSFEE); @@ -14330,7 +14551,7 @@ public: } { - Json::Value h = hso(hook, overrideFlag); + Json::Value h = hso(hook_wasm, overrideFlag); env(ripple::test::jtx::hook(acc, {{h}}, 0), M("set hookcanemit"), HSFEE); @@ -14362,7 +14583,7 @@ public: { // same result with no-HookCanEmit - Json::Value h = hso(hook, overrideFlag); + Json::Value h = hso(hook_wasm, overrideFlag); h[jss::HookCanEmit] = "0000000000000000000000000000000000000000000000000000000000" "400000"; @@ -14401,7 +14622,7 @@ public: { // install the hook on acc - Json::Value hookCanEmitHook = hso(hook, overrideFlag); + Json::Value hookCanEmitHook = hso(hook_wasm, overrideFlag); hookCanEmitHook[jss::HookCanEmit] = "00000000000000000000000000000000000000000000000000" "00000000000000"; @@ -14440,7 +14661,7 @@ public: { // install the hook on acc - Json::Value hookCanEmitHook = hso(hook, overrideFlag); + Json::Value hookCanEmitHook = hso(hook_wasm, overrideFlag); hookCanEmitHook[jss::HookCanEmit] = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFF"; From 1ba444ae7f1d47d114e667aa6386a25161756b7f Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 17 Feb 2026 10:37:12 +0900 Subject: [PATCH 02/12] Updated tests to align with the changes merged into the dev branch. --- src/test/app/SetHook_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/app/SetHook_test.cpp b/src/test/app/SetHook_test.cpp index 16c06d7be..176cf41c2 100644 --- a/src/test/app/SetHook_test.cpp +++ b/src/test/app/SetHook_test.cpp @@ -3961,7 +3961,7 @@ public: M("set etxn_details"), HSFEE); env.close(); - EXPECT_HOOK_FEE(hook, 88); + EXPECT_HOOK_FEE(hook, 2436); // invoke the hook env(pay(bob, alice, XRP(1)), M("test etxn_details"), fee(XRP(1))); @@ -4131,7 +4131,7 @@ public: M("set etxn_nonce"), HSFEE); env.close(); - EXPECT_HOOK_FEE(hook, 11644); + EXPECT_HOOK_FEE(hook, 11657); // invoke the hook env(pay(bob, alice, XRP(1)), M("test etxn_nonce"), fee(XRP(1))); @@ -7884,7 +7884,7 @@ public: HASH_WASM(hook); // before featureHooksUpdate1 - env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + env(ripple::test::jtx::hook(alice, {{hso(hook_wasm, overrideFlag)}}, 0), M("set xpop_slot (disabled)"), HSFEE, ter(temMALFORMED)); From 9bfca635744f6eceb1e409456cefb60c1ffb3c28 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 24 Feb 2026 17:23:11 +0900 Subject: [PATCH 03/12] Update util_keylet fee test --- src/test/app/SetHook_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/app/SetHook_test.cpp b/src/test/app/SetHook_test.cpp index 176cf41c2..b36d18989 100644 --- a/src/test/app/SetHook_test.cpp +++ b/src/test/app/SetHook_test.cpp @@ -13198,7 +13198,7 @@ public: M("set util_keylet"), HSFEE); env.close(); - EXPECT_HOOK_FEE(hook, 1546); + EXPECT_HOOK_FEE(hook, 1786); // invoke the hook env(pay(bob, alice, XRP(1)), M("test util_keylet"), fee(XRP(1))); From 2d2951875d258287bb1d9036dcc4ec3047b8ecda Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 5 Mar 2026 19:19:42 +0900 Subject: [PATCH 04/12] fix: typo `SignersListSet` --- src/test/app/BaseFee_test.cpp | 6 ++--- src/test/app/Import_json.h | 18 ++++++------- src/test/app/Import_test.cpp | 46 ++++++++++++++++---------------- src/test/app/SetHookTSH_test.cpp | 12 ++++----- src/test/app/Touch_test.cpp | 8 +++--- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/test/app/BaseFee_test.cpp b/src/test/app/BaseFee_test.cpp index b6600e40f..97de93631 100644 --- a/src/test/app/BaseFee_test.cpp +++ b/src/test/app/BaseFee_test.cpp @@ -582,9 +582,9 @@ class BaseFee_test : public beast::unit_test::suite } void - testSignersListSet(FeatureBitset features) + testSignerListSet(FeatureBitset features) { - testcase("signers list set w/ hook params"); + testcase("signer list set w/ hook params"); using namespace test::jtx; using namespace std::literals; @@ -810,7 +810,7 @@ class BaseFee_test : public beast::unit_test::suite testPaymentChannelFund(features); testSetHook(features); testSetRegularKey(features); - testSignersListSet(features); + testSignerListSet(features); testTicketCreate(features); testTrustSet(features); testURITokenBurnFee(features); diff --git a/src/test/app/Import_json.h b/src/test/app/Import_json.h index 37cd6363f..22982ea01 100644 --- a/src/test/app/Import_json.h +++ b/src/test/app/Import_json.h @@ -879,7 +879,7 @@ inline std::string ImportTCSetRegularKey::w_signers = R"json({ } })json"; -class ImportTCSignersListSet +class ImportTCSignerListSet { public: static std::string w_seed_bad_fee; @@ -891,7 +891,7 @@ public: static std::string w_signers_empty; }; -inline std::string ImportTCSignersListSet::w_seed_bad_fee = R"json({ +inline std::string ImportTCSignerListSet::w_seed_bad_fee = R"json({ "ledger": { "acroot": "64F75A08037D9F8ED8A103893401EB2AD726E7D6AAC3EAA249005916A9354892", "close": 743008501, @@ -952,7 +952,7 @@ inline std::string ImportTCSignersListSet::w_seed_bad_fee = R"json({ } } })json"; -inline std::string ImportTCSignersListSet::w_seed = R"json({ +inline std::string ImportTCSignerListSet::w_seed = R"json({ "ledger": { "acroot": "8112FF5F3FEEA34894A16CCCD64A24D552521F2E699780A587A9E6F5F5117CE5", "close": 743008510, @@ -993,7 +993,7 @@ inline std::string ImportTCSignersListSet::w_seed = R"json({ } } })json"; -inline std::string ImportTCSignersListSet::w_regular_key = R"json({ +inline std::string ImportTCSignerListSet::w_regular_key = R"json({ "ledger": { "acroot": "2A25CA219781A3144C72FD5FB6EB62763214E050050DA6176624A046C51EECBD", "close": 743015350, @@ -1034,7 +1034,7 @@ inline std::string ImportTCSignersListSet::w_regular_key = R"json({ } } })json"; -inline std::string ImportTCSignersListSet::w_signers = R"json({ +inline std::string ImportTCSignerListSet::w_signers = R"json({ "ledger": { "acroot": "BC35E65B52724CF258BDAC8B8E0D3B9CA0F012F5B243F6AAD1B671EDABD5188E", "close": 745594953, @@ -1075,7 +1075,7 @@ inline std::string ImportTCSignersListSet::w_signers = R"json({ } } })json"; -inline std::string ImportTCSignersListSet::w_seed_empty = R"json({ +inline std::string ImportTCSignerListSet::w_seed_empty = R"json({ "ledger": { "acroot": "ECCAFDE52A6D5F1E36EB82EAA5247FF1D8ADE51FCF1ED0842850193018A510F7", "close": 743056482, @@ -1116,7 +1116,7 @@ inline std::string ImportTCSignersListSet::w_seed_empty = R"json({ } } })json"; -inline std::string ImportTCSignersListSet::w_regular_key_empty = R"json({ +inline std::string ImportTCSignerListSet::w_regular_key_empty = R"json({ "ledger": { "acroot": "E222F46D5F35C79FDA3BB98973E2024EF9F6FA7B26471CC9CEF2CE033FA0E6E7", "close": 743169800, @@ -1157,7 +1157,7 @@ inline std::string ImportTCSignersListSet::w_regular_key_empty = R"json({ } } })json"; -inline std::string ImportTCSignersListSet::w_signers_empty = R"json({ +inline std::string ImportTCSignerListSet::w_signers_empty = R"json({ "ledger": { "acroot": "987438A87AD998B7D7ED04A280FB5414C76E8475D621A55FB8463F15CEEEAD49", "close": 743172592, @@ -1261,4 +1261,4 @@ inline std::string ImportTCHalving::base_genesis = R"json({ } // namespace test } // namespace ripple -#endif \ No newline at end of file +#endif diff --git a/src/test/app/Import_test.cpp b/src/test/app/Import_test.cpp index f9fb090be..1a65de486 100644 --- a/src/test/app/Import_test.cpp +++ b/src/test/app/Import_test.cpp @@ -1898,7 +1898,7 @@ class Import_test : public beast::unit_test::suite // different keys. { auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_signers); + import::loadXpop(ImportTCSignerListSet::w_signers); env(import::import(alice, xpopJson), msig(bob, dave), fee((3 * feeDrops) * 10), @@ -1910,7 +1910,7 @@ class Import_test : public beast::unit_test::suite // different keys. - empty innerSigners { Json::Value xpopJson = - import::loadXpop(ImportTCSignersListSet::w_signers); + import::loadXpop(ImportTCSignerListSet::w_signers); xpopJson[jss::transaction][jss::blob] = "12000C22000000002400000014201B0000002B201D00005359202300000002" "6840000000001E84B073008114AE123A8556F3CF91154711376AFB0F894F83" @@ -1927,7 +1927,7 @@ class Import_test : public beast::unit_test::suite // different keys. { Json::Value xpopJson = - import::loadXpop(ImportTCSignersListSet::w_signers); + import::loadXpop(ImportTCSignerListSet::w_signers); xpopJson[jss::transaction][jss::blob] = "12000C22000000002400000014201B0000002B201D00005359202300000002" "6840000000001E84B073008114AE123A8556F3CF91154711376AFB0F894F83" @@ -1953,7 +1953,7 @@ class Import_test : public beast::unit_test::suite // temMALFORMED - Import: inner txn signature verify failed { Json::Value xpopJson = - import::loadXpop(ImportTCSignersListSet::w_signers); + import::loadXpop(ImportTCSignerListSet::w_signers); xpopJson[jss::transaction][jss::blob] = "12000C2200000008240000001A201B000003B9201D00005359202300000000" "6840000000001E84B073008114AE123A8556F3CF91154711376AFB0F894F83" @@ -2768,7 +2768,7 @@ class Import_test : public beast::unit_test::suite env.close(); } - // tefIMPORT_BLACKHOLED - SignersListSet (w/seed) + // tefIMPORT_BLACKHOLED - SignerListSet (w/seed) { test::jtx::Env env{ *this, network::makeNetworkVLConfig(21337, keys)}; @@ -2792,7 +2792,7 @@ class Import_test : public beast::unit_test::suite // Import with Master Key Json::Value tmpXpop = - import::loadXpop(ImportTCSignersListSet::w_seed); + import::loadXpop(ImportTCSignerListSet::w_seed); env(import::import(alice, tmpXpop), ter(tefIMPORT_BLACKHOLED), fee(feeDrops * 10), @@ -3244,7 +3244,7 @@ class Import_test : public beast::unit_test::suite env(noop(alice), sig(bob), fee(feeDrops), ter(tefBAD_AUTH)); } - // w/ signers list -> dne + // w/ signer list -> dne { test::jtx::Env env{ *this, network::makeNetworkVLConfig(21337, keys)}; @@ -3975,7 +3975,7 @@ class Import_test : public beast::unit_test::suite env(noop(alice), sig(carol), fee(feeDrops), ter(tesSUCCESS)); } - // w/ signers list -> funded (update regular key) + // w/ signer list -> funded (update regular key) { test::jtx::Env env{ *this, network::makeNetworkVLConfig(21337, keys)}; @@ -4049,7 +4049,7 @@ class Import_test : public beast::unit_test::suite BEAST_EXPECT(acctSle->getAccountID(sfRegularKey) == dave.id()); env(noop(alice), sig(dave), fee(feeDrops), ter(tesSUCCESS)); - // confirm signers list not set + // confirm signer list not set auto const k = keylet::signers(alice); BEAST_EXPECT(env.current()->read(k) == nullptr); } @@ -4351,9 +4351,9 @@ class Import_test : public beast::unit_test::suite } void - testSignersListSet(FeatureBitset features) + testSignerListSet(FeatureBitset features) { - testcase("signers list set tx"); + testcase("signer list set tx"); using namespace test::jtx; using namespace std::literals; @@ -4394,7 +4394,7 @@ class Import_test : public beast::unit_test::suite // import tx auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_seed_bad_fee); + import::loadXpop(ImportTCSignerListSet::w_seed_bad_fee); Json::Value tx = import::import(alice, xpopJson); tx[jss::Sequence] = 0; // tx[jss::Fee] = 0; @@ -4438,7 +4438,7 @@ class Import_test : public beast::unit_test::suite // import tx auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_seed); + import::loadXpop(ImportTCSignerListSet::w_seed); Json::Value tx = import::import(alice, xpopJson); tx[jss::Sequence] = 0; tx[jss::Fee] = 0; @@ -4523,7 +4523,7 @@ class Import_test : public beast::unit_test::suite // import tx auto const burnAmt = XRP(2); auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_regular_key); + import::loadXpop(ImportTCSignerListSet::w_regular_key); Json::Value tx = import::import(alice, xpopJson); tx[jss::Sequence] = 0; tx[jss::Fee] = 0; @@ -4614,7 +4614,7 @@ class Import_test : public beast::unit_test::suite // import tx auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_signers); + import::loadXpop(ImportTCSignerListSet::w_signers); Json::Value tx = import::import(alice, xpopJson); tx[jss::Sequence] = 0; tx[jss::Fee] = 0; @@ -4685,7 +4685,7 @@ class Import_test : public beast::unit_test::suite // import tx auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_seed); + import::loadXpop(ImportTCSignerListSet::w_seed); env(import::import(alice, xpopJson), fee(feeDrops * 10), ter(tesSUCCESS)); @@ -4771,7 +4771,7 @@ class Import_test : public beast::unit_test::suite auto const envAlice = env.balance(alice); BEAST_EXPECT(envAlice == XRP(1000)); - // set the signers list + // set the signer list env(signers(alice, 2, {{bob, 1}, {carol, 1}})); env(noop(alice), msig(bob, carol), @@ -4787,7 +4787,7 @@ class Import_test : public beast::unit_test::suite // import tx auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_seed_empty); + import::loadXpop(ImportTCSignerListSet::w_seed_empty); env(import::import(alice, xpopJson), fee(feeDrops * 10), ter(tesSUCCESS)); @@ -4852,7 +4852,7 @@ class Import_test : public beast::unit_test::suite env(noop(alice), sig(bob), fee(feeDrops), ter(tesSUCCESS)); env.close(); - // set the signers list + // set the signer list env(signers(alice, 2, {{bob, 1}, {carol, 1}})); env(noop(alice), msig(bob, carol), @@ -4868,7 +4868,7 @@ class Import_test : public beast::unit_test::suite // import tx auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_regular_key_empty); + import::loadXpop(ImportTCSignerListSet::w_regular_key_empty); env(import::import(alice, xpopJson), fee(feeDrops * 10), sig(bob), @@ -4935,7 +4935,7 @@ class Import_test : public beast::unit_test::suite auto const envAlice = env.balance(alice); BEAST_EXPECT(envAlice == XRP(1000)); - // set the signers list + // set the signer list env(signers(alice, 2, {{bob, 1}, {carol, 1}})); env(noop(alice), msig(bob, carol), @@ -4951,7 +4951,7 @@ class Import_test : public beast::unit_test::suite // import tx auto const xpopJson = - import::loadXpop(ImportTCSignersListSet::w_signers_empty); + import::loadXpop(ImportTCSignerListSet::w_signers_empty); env(import::import(alice, xpopJson), msig(bob, carol), fee((3 * feeDrops) * 10), @@ -6228,7 +6228,7 @@ public: testAccountSetFlags(features); testSetRegularKey(features); testSetRegularKeyFlags(features); - testSignersListSet(features); + testSignerListSet(features); testUsingTickets(features); testAccountIndex(features); testHookIssuer(features); diff --git a/src/test/app/SetHookTSH_test.cpp b/src/test/app/SetHookTSH_test.cpp index 425eedbaf..223a200b8 100644 --- a/src/test/app/SetHookTSH_test.cpp +++ b/src/test/app/SetHookTSH_test.cpp @@ -4492,14 +4492,14 @@ private: } } - // SignersListSet + // SignerListSet // | otxn | tsh | sls | // | A | A | S | // | A | S | S | void - testSignersListSetTSH(FeatureBitset features) + testSignerListSetTSH(FeatureBitset features) { - testcase("signers list set tsh"); + testcase("signer list set tsh"); using namespace test::jtx; using namespace std::literals; @@ -4527,7 +4527,7 @@ private: // set tsh hook setTSHHook(env, account, testStrong); - // signers list set + // signer list set env(signers(account, 2, {{signer1, 1}, {signer2, 1}}), fee(XRP(1)), ter(tesSUCCESS)); @@ -4566,7 +4566,7 @@ private: // set tsh hook setTSHHook(env, signer2, testStrong); - // signers list set + // signer list set env(signers(account, 2, {{signer1, 1}, {signer2, 1}}), fee(XRP(1)), ter(tesSUCCESS)); @@ -6914,7 +6914,7 @@ private: testPaymentChannelFundTSH(features); testSetHookTSH(features); testSetRegularKeyTSH(features); - testSignersListSetTSH(features); + testSignerListSetTSH(features); testTicketCreateTSH(features); testTrustSetTSH(features); testURITokenMintTSH(features); diff --git a/src/test/app/Touch_test.cpp b/src/test/app/Touch_test.cpp index 6943e5d37..f3a545f5d 100644 --- a/src/test/app/Touch_test.cpp +++ b/src/test/app/Touch_test.cpp @@ -880,9 +880,9 @@ private: } void - testSignersListSet(FeatureBitset features) + testSignerListSet(FeatureBitset features) { - testcase("signers list set"); + testcase("signer list set"); using namespace test::jtx; using namespace std::literals; @@ -895,7 +895,7 @@ private: env.fund(XRP(1000), alice, signer1, signer2); env.close(); - // signers list set + // signer list set env(signers(alice, 2, {{signer1, 1}, {signer2, 1}}), ter(tesSUCCESS)); env.close(); @@ -1384,7 +1384,7 @@ private: testPaymentChannelFund(features); testSetHook(features); testSetRegularKey(features); - testSignersListSet(features); + testSignerListSet(features); testTicketCreate(features); testTrustSet(features); testURITokenMint(features); From 8c4c158d3a2ba3139a6ba5eb86d918395030cad6 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 28 Nov 2025 01:10:49 +0900 Subject: [PATCH 05/12] output ccache configuration in release-builder --- build-core.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build-core.sh b/build-core.sh index 7054ce4ed..e6867d5e2 100755 --- a/build-core.sh +++ b/build-core.sh @@ -71,6 +71,7 @@ cmake .. -G Ninja \ -Dxrpld=TRUE \ -Dtests=TRUE && ccache -z && +ccache -p && ninja -j $3 && echo "=== Re-running final link with verbose output ===" && rm -f rippled && ninja -v rippled && ccache -s && strip -s rippled && From f90ed418027bca2dc7d1bd9162a46396d17e2bd9 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 28 Nov 2025 01:31:26 +0900 Subject: [PATCH 06/12] enable ccache direct_mode --- release-builder.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/release-builder.sh b/release-builder.sh index 436c8cd4a..ac9c46981 100755 --- a/release-builder.sh +++ b/release-builder.sh @@ -196,6 +196,7 @@ ENV PATH=/usr/local/bin:$PATH RUN /hbb_exe/activate-exec bash -c "ccache -M 100G && \ ccache -o cache_dir=/cache/ccache && \ ccache -o compiler_check=content && \ + ccache -o direct_mode=true && \ mkdir -p ~/.conan2 /cache/conan2 /cache/conan2_download /cache/conan2_sources && \ echo 'core.cache:storage_path=/cache/conan2' > ~/.conan2/global.conf && \ echo 'core.download:download_cache=/cache/conan2_download' >> ~/.conan2/global.conf && \ From 25123b370abd669e5e255997b449afcf37e4066d Mon Sep 17 00:00:00 2001 From: Nicholas Dudfield Date: Fri, 13 Mar 2026 12:08:27 +0700 Subject: [PATCH 07/12] chore: replace levelization shell script with python Backport of XRPLF/rippled#6325. The python version runs ~80x faster. --- .github/workflows/levelization.yml | 4 +- .gitignore | 3 + Builds/levelization/README.md | 6 +- Builds/levelization/levelization.py | 283 ++++++++++++++++++++++++++++ Builds/levelization/levelization.sh | 130 ------------- 5 files changed, 291 insertions(+), 135 deletions(-) create mode 100755 Builds/levelization/levelization.py delete mode 100755 Builds/levelization/levelization.sh diff --git a/.github/workflows/levelization.yml b/.github/workflows/levelization.yml index f99c1ca56..029672e18 100644 --- a/.github/workflows/levelization.yml +++ b/.github/workflows/levelization.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Check levelization - run: Builds/levelization/levelization.sh + run: python Builds/levelization/levelization.py - name: Check for differences id: assert run: | @@ -40,7 +40,7 @@ jobs: To fix it, you can do one of two things: 1. Download and apply the patch generated as an artifact of this job to your repo, commit, and push. - 2. Run './Builds/levelization/levelization.sh' in your repo, + 2. Run 'python Builds/levelization/levelization.py' in your repo, commit, and push. See Builds/levelization/README.md for more info. diff --git a/.gitignore b/.gitignore index 585a69efb..121cba965 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,9 @@ Builds/levelization/results/paths.txt Builds/levelization/results/includes/ Builds/levelization/results/includedby/ +# Python +__pycache__ + # Ignore tmp directory. tmp diff --git a/Builds/levelization/README.md b/Builds/levelization/README.md index 4ff3a5423..430247558 100644 --- a/Builds/levelization/README.md +++ b/Builds/levelization/README.md @@ -50,7 +50,7 @@ that `test` code should *never* be included in `ripple` code.) ## Validation -The [levelization.sh](levelization.sh) script takes no parameters, +The [levelization.py](levelization.py) script takes no parameters, reads no environment variables, and can be run from any directory, as long as it is in the expected location in the rippled repo. It can be run at any time from within a checked out repo, and will @@ -84,7 +84,7 @@ It generates many files of [results](results): Github Actions workflow to test that levelization loops haven't changed. Unfortunately, if changes are detected, it can't tell if they are improvements or not, so if you have resolved any issues or - done anything else to improve levelization, run `levelization.sh`, + done anything else to improve levelization, run `levelization.py`, and commit the updated results. The `loops.txt` and `ordering.txt` files relate the modules @@ -108,7 +108,7 @@ The committed files hide the detailed values intentionally, to prevent false alarms and merging issues, and because it's easy to get those details locally. -1. Run `levelization.sh` +1. Run `levelization.py` 2. Grep the modules in `paths.txt`. * For example, if a cycle is found `A ~= B`, simply `grep -w A Builds/levelization/results/paths.txt | grep -w B` diff --git a/Builds/levelization/levelization.py b/Builds/levelization/levelization.py new file mode 100755 index 000000000..043c9e00d --- /dev/null +++ b/Builds/levelization/levelization.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python3 + +""" +Usage: levelization.py +This script takes no parameters, and can be called from any directory in the file system. +""" + +import os +import re +import sys +from collections import defaultdict +from pathlib import Path + +# Compile regex patterns once at module level +INCLUDE_PATTERN = re.compile(r"^\s*#include.*/.*\.h") +INCLUDE_PATH_PATTERN = re.compile(r'[<"]([^>"]+)[>"]') + + +def dictionary_sort_key(s): + """ + Create a sort key that mimics 'sort -d' (dictionary order). + Dictionary order only considers blanks and alphanumeric characters. + """ + return "".join(c for c in s if c.isalnum() or c.isspace()) + + +def get_level(file_path): + """ + Extract the level from a file path (second and third directory components). + Equivalent to bash: cut -d/ -f 2,3 + + Examples: + src/ripple/app/main.cpp -> ripple.app + src/test/app/Import_test.cpp -> test.app + """ + parts = file_path.split("/") + + if len(parts) >= 3: + level = f"{parts[1]}/{parts[2]}" + elif len(parts) >= 2: + level = f"{parts[1]}/toplevel" + else: + level = file_path + + # If the "level" indicates a file, cut off the filename + if "." in level.split("/")[-1]: + # Use the "toplevel" label as a workaround for `sort` + # inconsistencies between different utility versions + level = level.rsplit("/", 1)[0] + "/toplevel" + + return level.replace("/", ".") + + +def extract_include_level(include_line): + """ + Extract the include path from an #include directive. + Gets the first two directory components from the include path. + Equivalent to bash: cut -d/ -f 1,2 + + Examples: + #include -> ripple.basics + #include "ripple/app/main/Application.h" -> ripple.app + """ + match = INCLUDE_PATH_PATTERN.search(include_line) + if not match: + return None + + include_path = match.group(1) + parts = include_path.split("/") + + if len(parts) >= 2: + include_level = f"{parts[0]}/{parts[1]}" + else: + include_level = include_path + + # If the "includelevel" indicates a file, cut off the filename + if "." in include_level.split("/")[-1]: + include_level = include_level.rsplit("/", 1)[0] + "/toplevel" + + return include_level.replace("/", ".") + + +def find_repository_directories(start_path, depth_limit=10): + """ + Find the repository root by looking for src or include folders. + Walks up the directory tree from the start path. + """ + current = start_path.resolve() + + for _ in range(depth_limit): + src_path = current / "src" + include_path = current / "include" + has_src = src_path.exists() + has_include = include_path.exists() + + if has_src or has_include: + dirs = [] + if has_src: + dirs.append(src_path) + if has_include: + dirs.append(include_path) + return current, dirs + + parent = current.parent + if parent == current: + break + current = parent + + raise RuntimeError( + "Could not find repository root. " + "Expected to find a directory containing 'src' and/or 'include' folders." + ) + + +def main(): + script_dir = Path(__file__).parent.resolve() + os.chdir(script_dir) + + # Clean up and create results directory. + results_dir = script_dir / "results" + if results_dir.exists(): + import shutil + + shutil.rmtree(results_dir) + results_dir.mkdir() + + # Find the repository root. + try: + repo_root, scan_dirs = find_repository_directories(script_dir) + print(f"Found repository root: {repo_root}") + for scan_dir in scan_dirs: + print(f" Scanning: {scan_dir.relative_to(repo_root)}") + except RuntimeError as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + # Find all #include directives. + print("\nScanning for raw includes...") + raw_includes = [] + rawincludes_file = results_dir / "rawincludes.txt" + + with open(rawincludes_file, "w", buffering=8192) as raw_f: + for dir_path in scan_dirs: + for file_path in dir_path.rglob("*"): + if not file_path.is_file(): + continue + try: + rel_path_str = str(file_path.relative_to(repo_root)) + with open( + file_path, "r", encoding="utf-8", errors="ignore", buffering=8192 + ) as f: + for line in f: + if "#include" not in line or "boost" in line: + continue + if INCLUDE_PATTERN.match(line): + line_stripped = line.strip() + entry = f"{rel_path_str}:{line_stripped}\n" + print(entry, end="") + raw_f.write(entry) + raw_includes.append((rel_path_str, line_stripped)) + except Exception as e: + print(f"Error reading {file_path}: {e}", file=sys.stderr) + + # Build levelization paths and count directly. + print("Build levelization paths") + path_counts = defaultdict(int) + + for file_path, include_line in raw_includes: + include_level = extract_include_level(include_line) + if not include_level: + continue + level = get_level(file_path) + if level != include_level: + path_counts[(level, include_level)] += 1 + + # Sort and deduplicate paths. + print("Sort and deduplicate paths") + sorted_items = sorted( + path_counts.items(), + key=lambda x: (dictionary_sort_key(x[0][0]), dictionary_sort_key(x[0][1])), + ) + + paths_file = results_dir / "paths.txt" + with open(paths_file, "w") as f: + for (level, include_level), count in sorted_items: + line = f"{count:7} {level} {include_level}\n" + print(line.rstrip()) + f.write(line) + + # Split into flat-file database. + print("Split into flat-file database") + includes_dir = results_dir / "includes" + includedby_dir = results_dir / "includedby" + includes_dir.mkdir() + includedby_dir.mkdir() + + includes_data = defaultdict(list) + includedby_data = defaultdict(list) + + for (level, include_level), count in sorted_items: + includes_data[level].append((include_level, count)) + includedby_data[include_level].append((level, count)) + + for level in sorted(includes_data.keys(), key=dictionary_sort_key): + with open(includes_dir / level, "w") as f: + for include_level, count in includes_data[level]: + line = f"{include_level} {count}\n" + print(line.rstrip()) + f.write(line) + + for include_level in sorted(includedby_data.keys(), key=dictionary_sort_key): + with open(includedby_dir / include_level, "w") as f: + for level, count in includedby_data[include_level]: + line = f"{level} {count}\n" + print(line.rstrip()) + f.write(line) + + # Search for loops. + print("Search for loops") + loops_file = results_dir / "loops.txt" + ordering_file = results_dir / "ordering.txt" + + # Pre-load all include files into memory for fast lookup. + includes_cache = {} + includes_lookup = {} + + for include_file in sorted(includes_dir.iterdir(), key=lambda p: p.name): + if not include_file.is_file(): + continue + includes_cache[include_file.name] = [] + includes_lookup[include_file.name] = {} + with open(include_file, "r") as f: + for line in f: + parts = line.strip().split() + if len(parts) >= 2: + name, count = parts[0], int(parts[1]) + includes_cache[include_file.name].append((name, count)) + includes_lookup[include_file.name][name] = count + + loops_found = set() + + with open(loops_file, "w", buffering=8192) as loops_f, open( + ordering_file, "w", buffering=8192 + ) as ordering_f: + for source in sorted(includes_cache.keys()): + for include, include_freq in includes_cache[source]: + if include not in includes_lookup: + continue + + source_freq = includes_lookup[include].get(source) + + if source_freq is not None: + loop_key = tuple(sorted([source, include])) + if loop_key in loops_found: + continue + loops_found.add(loop_key) + + loops_f.write(f"Loop: {source} {include}\n") + + diff = include_freq - source_freq + if diff > 3: + loops_f.write(f" {source} > {include}\n\n") + elif diff < -3: + loops_f.write(f" {include} > {source}\n\n") + elif source_freq == include_freq: + loops_f.write(f" {include} == {source}\n\n") + else: + loops_f.write(f" {include} ~= {source}\n\n") + else: + ordering_f.write(f"{source} > {include}\n") + + # Print results. + print("\nOrdering:") + with open(ordering_file, "r") as f: + print(f.read(), end="") + + print("\nLoops:") + with open(loops_file, "r") as f: + print(f.read(), end="") + + +if __name__ == "__main__": + main() diff --git a/Builds/levelization/levelization.sh b/Builds/levelization/levelization.sh deleted file mode 100755 index c18ca703f..000000000 --- a/Builds/levelization/levelization.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/bin/bash - -# Usage: levelization.sh -# This script takes no parameters, reads no environment variables, -# and can be run from any directory, as long as it is in the expected -# location in the repo. - -pushd $( dirname $0 ) - -if [ -v PS1 ] -then - # if the shell is interactive, clean up any flotsam before analyzing - git clean -ix -fi - -# Ensure all sorting is ASCII-order consistently across platforms. -export LANG=C - -rm -rfv results -mkdir results -includes="$( pwd )/results/rawincludes.txt" -pushd ../.. -echo Raw includes: -grep -r '^[ ]*#include.*/.*\.h' include src | \ - grep -v boost | tee ${includes} -popd -pushd results - -oldifs=${IFS} -IFS=: -mkdir includes -mkdir includedby -echo Build levelization paths -exec 3< ${includes} # open rawincludes.txt for input -while read -r -u 3 file include -do - level=$( echo ${file} | cut -d/ -f 2,3 ) - # If the "level" indicates a file, cut off the filename - if [[ "${level##*.}" != "${level}" ]] - then - # Use the "toplevel" label as a workaround for `sort` - # inconsistencies between different utility versions - level="$( dirname ${level} )/toplevel" - fi - level=$( echo ${level} | tr '/' '.' ) - - includelevel=$( echo ${include} | sed 's/.*["<]//; s/[">].*//' | \ - cut -d/ -f 1,2 ) - if [[ "${includelevel##*.}" != "${includelevel}" ]] - then - # Use the "toplevel" label as a workaround for `sort` - # inconsistencies between different utility versions - includelevel="$( dirname ${includelevel} )/toplevel" - fi - includelevel=$( echo ${includelevel} | tr '/' '.' ) - - if [[ "$level" != "$includelevel" ]] - then - echo $level $includelevel | tee -a paths.txt - fi -done -echo Sort and dedup paths -sort -ds paths.txt | uniq -c | tee sortedpaths.txt -mv sortedpaths.txt paths.txt -exec 3>&- #close fd 3 -IFS=${oldifs} -unset oldifs - -echo Split into flat-file database -exec 4&- #close fd 4 - -loops="$( pwd )/loops.txt" -ordering="$( pwd )/ordering.txt" -pushd includes -echo Search for loops -# Redirect stdout to a file -exec 4>&1 -exec 1>"${loops}" -for source in * -do - if [[ -f "$source" ]] - then - exec 5<"${source}" # open for input - while read -r -u 5 include includefreq - do - if [[ -f $include ]] - then - if grep -q -w $source $include - then - if grep -q -w "Loop: $include $source" "${loops}" - then - continue - fi - sourcefreq=$( grep -w $source $include | cut -d\ -f2 ) - echo "Loop: $source $include" - # If the counts are close, indicate that the two modules are - # on the same level, though they shouldn't be - if [[ $(( $includefreq - $sourcefreq )) -gt 3 ]] - then - echo -e " $source > $include\n" - elif [[ $(( $sourcefreq - $includefreq )) -gt 3 ]] - then - echo -e " $include > $source\n" - elif [[ $sourcefreq -eq $includefreq ]] - then - echo -e " $include == $source\n" - else - echo -e " $include ~= $source\n" - fi - else - echo "$source > $include" >> "${ordering}" - fi - fi - done - exec 5>&- #close fd 5 - fi -done -exec 1>&4 #close fd 1 -exec 4>&- #close fd 4 -cat "${ordering}" -cat "${loops}" -popd -popd -popd From 4150f0383c484b4eff8437d706fe0b52519c8322 Mon Sep 17 00:00:00 2001 From: Nicholas Dudfield Date: Fri, 13 Mar 2026 12:13:39 +0700 Subject: [PATCH 08/12] chore: use improved levelization script with threading and argparse --- Builds/levelization/levelization.py | 456 ++++++++++++++-------------- 1 file changed, 223 insertions(+), 233 deletions(-) diff --git a/Builds/levelization/levelization.py b/Builds/levelization/levelization.py index 043c9e00d..964acdcd4 100755 --- a/Builds/levelization/levelization.py +++ b/Builds/levelization/levelization.py @@ -1,283 +1,273 @@ #!/usr/bin/env python3 - """ -Usage: levelization.py -This script takes no parameters, and can be called from any directory in the file system. +Levelization generator. + +Produces the same result artifacts as levelization.sh, but much faster by +doing parsing/counting in-process instead of spawning external tools in +tight loops. """ +from __future__ import annotations + +import argparse +import concurrent.futures import os +import posixpath import re -import sys -from collections import defaultdict +import shutil +import time +from collections import Counter, defaultdict from pathlib import Path -# Compile regex patterns once at module level INCLUDE_PATTERN = re.compile(r"^\s*#include.*/.*\.h") -INCLUDE_PATH_PATTERN = re.compile(r'[<"]([^>"]+)[>"]') +INCLUDE_TARGET_PATTERN = re.compile(r'.*["<]([^">]+)[">].*') +PATHS_LINE_PATTERN = re.compile(r"^\s*(\d+)\s+(\S+)\s+(\S+)\s*$") -def dictionary_sort_key(s): - """ - Create a sort key that mimics 'sort -d' (dictionary order). - Dictionary order only considers blanks and alphanumeric characters. - """ - return "".join(c for c in s if c.isalnum() or c.isspace()) +def dictionary_sort_key(value: str) -> str: + """Approximate `sort -d` behavior used by the shell script.""" + return "".join(ch for ch in value if ch.isalnum() or ch.isspace()) -def get_level(file_path): - """ - Extract the level from a file path (second and third directory components). - Equivalent to bash: cut -d/ -f 2,3 - - Examples: - src/ripple/app/main.cpp -> ripple.app - src/test/app/Import_test.cpp -> test.app - """ - parts = file_path.split("/") - - if len(parts) >= 3: - level = f"{parts[1]}/{parts[2]}" - elif len(parts) >= 2: - level = f"{parts[1]}/toplevel" - else: - level = file_path - - # If the "level" indicates a file, cut off the filename - if "." in level.split("/")[-1]: - # Use the "toplevel" label as a workaround for `sort` - # inconsistencies between different utility versions - level = level.rsplit("/", 1)[0] + "/toplevel" - - return level.replace("/", ".") +def normalize_level(value: str) -> str: + # Match shell behavior: if level includes a file component (contains "."), + # replace with dirname + "/toplevel". + if "." in value: + parent = posixpath.dirname(value) or "." + value = f"{parent}/toplevel" + return value.replace("/", ".") -def extract_include_level(include_line): - """ - Extract the include path from an #include directive. - Gets the first two directory components from the include path. - Equivalent to bash: cut -d/ -f 1,2 +def source_level(rel_path: str) -> str: + parts = rel_path.split("/") + return normalize_level("/".join(parts[1:3])) - Examples: - #include -> ripple.basics - #include "ripple/app/main/Application.h" -> ripple.app - """ - match = INCLUDE_PATH_PATTERN.search(include_line) + +def include_level(include_line: str) -> str | None: + match = INCLUDE_TARGET_PATTERN.match(include_line) if not match: return None - include_path = match.group(1) parts = include_path.split("/") - - if len(parts) >= 2: - include_level = f"{parts[0]}/{parts[1]}" - else: - include_level = include_path - - # If the "includelevel" indicates a file, cut off the filename - if "." in include_level.split("/")[-1]: - include_level = include_level.rsplit("/", 1)[0] + "/toplevel" - - return include_level.replace("/", ".") + return normalize_level("/".join(parts[:2])) -def find_repository_directories(start_path, depth_limit=10): - """ - Find the repository root by looking for src or include folders. - Walks up the directory tree from the start path. - """ - current = start_path.resolve() +def scan_file(path: Path, repo_root: Path) -> tuple[list[str], list[tuple[str, str]]]: + rel = path.relative_to(repo_root).as_posix() + src_level = source_level(rel) - for _ in range(depth_limit): - src_path = current / "src" - include_path = current / "include" - has_src = src_path.exists() - has_include = include_path.exists() + raw_lines: list[str] = [] + paths: list[tuple[str, str]] = [] - if has_src or has_include: - dirs = [] - if has_src: - dirs.append(src_path) - if has_include: - dirs.append(include_path) - return current, dirs + with path.open("r", encoding="utf-8", errors="ignore") as handle: + for line in handle: + if "boost" in line: + continue + if not INCLUDE_PATTERN.match(line): + continue - parent = current.parent - if parent == current: - break - current = parent + line = line.rstrip("\n") + raw_lines.append(f"{rel}:{line}") - raise RuntimeError( - "Could not find repository root. " - "Expected to find a directory containing 'src' and/or 'include' folders." - ) + dst_level = include_level(line) + if dst_level is None: + continue + if src_level != dst_level: + paths.append((src_level, dst_level)) + + return raw_lines, paths -def main(): - script_dir = Path(__file__).parent.resolve() - os.chdir(script_dir) +def iter_source_files(repo_root: Path) -> list[Path]: + files: list[Path] = [] + for top in ("include", "src"): + root = repo_root / top + if root.exists(): + files.extend(path for path in root.rglob("*") if path.is_file()) + files.sort(key=lambda p: p.relative_to(repo_root).as_posix()) + return files - # Clean up and create results directory. - results_dir = script_dir / "results" - if results_dir.exists(): - import shutil - shutil.rmtree(results_dir) - results_dir.mkdir() - - # Find the repository root. - try: - repo_root, scan_dirs = find_repository_directories(script_dir) - print(f"Found repository root: {repo_root}") - for scan_dir in scan_dirs: - print(f" Scanning: {scan_dir.relative_to(repo_root)}") - except RuntimeError as e: - print(f"Error: {e}", file=sys.stderr) - sys.exit(1) - - # Find all #include directives. - print("\nScanning for raw includes...") - raw_includes = [] - rawincludes_file = results_dir / "rawincludes.txt" - - with open(rawincludes_file, "w", buffering=8192) as raw_f: - for dir_path in scan_dirs: - for file_path in dir_path.rglob("*"): - if not file_path.is_file(): - continue - try: - rel_path_str = str(file_path.relative_to(repo_root)) - with open( - file_path, "r", encoding="utf-8", errors="ignore", buffering=8192 - ) as f: - for line in f: - if "#include" not in line or "boost" in line: - continue - if INCLUDE_PATTERN.match(line): - line_stripped = line.strip() - entry = f"{rel_path_str}:{line_stripped}\n" - print(entry, end="") - raw_f.write(entry) - raw_includes.append((rel_path_str, line_stripped)) - except Exception as e: - print(f"Error reading {file_path}: {e}", file=sys.stderr) - - # Build levelization paths and count directly. - print("Build levelization paths") - path_counts = defaultdict(int) - - for file_path, include_line in raw_includes: - include_level = extract_include_level(include_line) - if not include_level: - continue - level = get_level(file_path) - if level != include_level: - path_counts[(level, include_level)] += 1 - - # Sort and deduplicate paths. - print("Sort and deduplicate paths") - sorted_items = sorted( - path_counts.items(), - key=lambda x: (dictionary_sort_key(x[0][0]), dictionary_sort_key(x[0][1])), - ) - - paths_file = results_dir / "paths.txt" - with open(paths_file, "w") as f: - for (level, include_level), count in sorted_items: - line = f"{count:7} {level} {include_level}\n" - print(line.rstrip()) - f.write(line) - - # Split into flat-file database. - print("Split into flat-file database") +def write_relation_db( + results_dir: Path, + edge_counts: list[tuple[tuple[str, str], int]], +) -> tuple[dict[str, list[tuple[str, int]]], dict[str, list[tuple[str, int]]]]: includes_dir = results_dir / "includes" includedby_dir = results_dir / "includedby" - includes_dir.mkdir() - includedby_dir.mkdir() + includes_dir.mkdir(parents=True, exist_ok=True) + includedby_dir.mkdir(parents=True, exist_ok=True) - includes_data = defaultdict(list) - includedby_data = defaultdict(list) + includes: dict[str, list[tuple[str, int]]] = defaultdict(list) + includedby: dict[str, list[tuple[str, int]]] = defaultdict(list) - for (level, include_level), count in sorted_items: - includes_data[level].append((include_level, count)) - includedby_data[include_level].append((level, count)) + with (results_dir / "paths.txt").open("w", encoding="utf-8") as out: + for (src, dst), count in edge_counts: + out.write(f"{count:7d} {src} {dst}\n") + includes[src].append((dst, count)) + includedby[dst].append((src, count)) - for level in sorted(includes_data.keys(), key=dictionary_sort_key): - with open(includes_dir / level, "w") as f: - for include_level, count in includes_data[level]: - line = f"{include_level} {count}\n" - print(line.rstrip()) - f.write(line) + for src, entries in includes.items(): + with (includes_dir / src).open("w", encoding="utf-8") as out: + for dst, count in entries: + out.write(f"{dst} {count}\n") - for include_level in sorted(includedby_data.keys(), key=dictionary_sort_key): - with open(includedby_dir / include_level, "w") as f: - for level, count in includedby_data[include_level]: - line = f"{level} {count}\n" - print(line.rstrip()) - f.write(line) + for dst, entries in includedby.items(): + with (includedby_dir / dst).open("w", encoding="utf-8") as out: + for src, count in entries: + out.write(f"{src} {count}\n") - # Search for loops. - print("Search for loops") - loops_file = results_dir / "loops.txt" - ordering_file = results_dir / "ordering.txt" + return includes, includedby - # Pre-load all include files into memory for fast lookup. - includes_cache = {} - includes_lookup = {} - for include_file in sorted(includes_dir.iterdir(), key=lambda p: p.name): - if not include_file.is_file(): - continue - includes_cache[include_file.name] = [] - includes_lookup[include_file.name] = {} - with open(include_file, "r") as f: - for line in f: - parts = line.strip().split() - if len(parts) >= 2: - name, count = parts[0], int(parts[1]) - includes_cache[include_file.name].append((name, count)) - includes_lookup[include_file.name][name] = count +def build_loops_and_ordering( + includes: dict[str, list[tuple[str, int]]], +) -> tuple[list[str], list[str]]: + include_map = { + src: {dst: count for dst, count in entries} + for src, entries in includes.items() + } - loops_found = set() + ordering_lines: list[str] = [] + loops_lines: list[str] = [] - with open(loops_file, "w", buffering=8192) as loops_f, open( - ordering_file, "w", buffering=8192 - ) as ordering_f: - for source in sorted(includes_cache.keys()): - for include, include_freq in includes_cache[source]: - if include not in includes_lookup: - continue + seen_pairs: set[tuple[str, str]] = set() - source_freq = includes_lookup[include].get(source) + for source in sorted(includes.keys()): + for include, includefreq in includes[source]: + if include not in include_map: + continue - if source_freq is not None: - loop_key = tuple(sorted([source, include])) - if loop_key in loops_found: - continue - loops_found.add(loop_key) + sourcefreq = include_map[include].get(source) + if sourcefreq is None: + ordering_lines.append(f"{source} > {include}\n") + continue - loops_f.write(f"Loop: {source} {include}\n") + if (include, source) in seen_pairs: + continue + seen_pairs.add((source, include)) - diff = include_freq - source_freq - if diff > 3: - loops_f.write(f" {source} > {include}\n\n") - elif diff < -3: - loops_f.write(f" {include} > {source}\n\n") - elif source_freq == include_freq: - loops_f.write(f" {include} == {source}\n\n") - else: - loops_f.write(f" {include} ~= {source}\n\n") - else: - ordering_f.write(f"{source} > {include}\n") + loops_lines.append(f"Loop: {source} {include}\n") + if includefreq - sourcefreq > 3: + loops_lines.append(f" {source} > {include}\n\n") + elif sourcefreq - includefreq > 3: + loops_lines.append(f" {include} > {source}\n\n") + elif sourcefreq == includefreq: + loops_lines.append(f" {include} == {source}\n\n") + else: + loops_lines.append(f" {include} ~= {source}\n\n") - # Print results. - print("\nOrdering:") - with open(ordering_file, "r") as f: - print(f.read(), end="") + return ordering_lines, loops_lines - print("\nLoops:") - with open(loops_file, "r") as f: - print(f.read(), end="") + +def generate(results_dir: Path, repo_root: Path, workers: int) -> None: + if results_dir.exists(): + shutil.rmtree(results_dir) + results_dir.mkdir(parents=True) + + files = iter_source_files(repo_root) + + raw_by_file: dict[str, list[str]] = {} + paths_by_file: dict[str, list[tuple[str, str]]] = {} + + start = time.perf_counter() + if workers <= 1: + for file in files: + rel = file.relative_to(repo_root).as_posix() + raw, paths = scan_file(file, repo_root) + raw_by_file[rel] = raw + paths_by_file[rel] = paths + else: + with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool: + futures = { + file.relative_to(repo_root).as_posix(): pool.submit( + scan_file, file, repo_root + ) + for file in files + } + for rel in sorted(futures.keys()): + raw, paths = futures[rel].result() + raw_by_file[rel] = raw + paths_by_file[rel] = paths + + raw_lines: list[str] = [] + raw_lines.extend( + line + for rel in sorted(raw_by_file.keys()) + for line in raw_by_file[rel] + ) + with (results_dir / "rawincludes.txt").open("w", encoding="utf-8") as out: + out.write("\n".join(raw_lines)) + if raw_lines: + out.write("\n") + + path_pairs: list[tuple[str, str]] = [] + path_pairs.extend( + pair + for rel in sorted(paths_by_file.keys()) + for pair in paths_by_file[rel] + ) + counts = Counter(path_pairs) + + edge_counts = sorted( + counts.items(), + key=lambda item: ( + dictionary_sort_key(item[0][0]), + dictionary_sort_key(item[0][1]), + ), + ) + + includes, _ = write_relation_db(results_dir, edge_counts) + ordering, loops = build_loops_and_ordering(includes) + + with (results_dir / "ordering.txt").open("w", encoding="utf-8") as out: + out.writelines(ordering) + with (results_dir / "loops.txt").open("w", encoding="utf-8") as out: + out.writelines(loops) + + elapsed = time.perf_counter() - start + print( + f"levelization.py: scanned {len(files)} files, " + f"{len(raw_lines)} includes, {len(edge_counts)} unique paths in " + f"{elapsed:.2f}s" + ) + print((results_dir / "ordering.txt").read_text(encoding="utf-8"), end="") + print((results_dir / "loops.txt").read_text(encoding="utf-8"), end="") + + +def main() -> int: + script_dir = Path(__file__).resolve().parent + repo_root = script_dir.parents[1] + + parser = argparse.ArgumentParser() + parser.add_argument( + "--repo-root", + type=Path, + default=repo_root, + help="Repository root (defaults based on script location).", + ) + parser.add_argument( + "--results-dir", + type=Path, + default=script_dir / "results", + help="Output results directory.", + ) + parser.add_argument( + "--workers", + type=int, + default=min(32, (os.cpu_count() or 1)), + help="Thread count for source scanning (default: CPU count, max 32).", + ) + args = parser.parse_args() + + generated_dir = args.results_dir.resolve() + generate( + results_dir=generated_dir, + repo_root=args.repo_root.resolve(), + workers=max(1, args.workers), + ) + + return 0 if __name__ == "__main__": - main() + raise SystemExit(main()) From 7f6ac75617503f1af5ad90ad376d29407c094e30 Mon Sep 17 00:00:00 2001 From: Nicholas Dudfield Date: Fri, 13 Mar 2026 12:33:19 +0700 Subject: [PATCH 09/12] Revert "chore: use improved levelization script with threading and argparse" This reverts commit 5c1d7d9ae907e7ad13d58a41ccbc6ee19408b456. --- Builds/levelization/levelization.py | 456 ++++++++++++++-------------- 1 file changed, 233 insertions(+), 223 deletions(-) diff --git a/Builds/levelization/levelization.py b/Builds/levelization/levelization.py index 964acdcd4..043c9e00d 100755 --- a/Builds/levelization/levelization.py +++ b/Builds/levelization/levelization.py @@ -1,273 +1,283 @@ #!/usr/bin/env python3 -""" -Levelization generator. -Produces the same result artifacts as levelization.sh, but much faster by -doing parsing/counting in-process instead of spawning external tools in -tight loops. +""" +Usage: levelization.py +This script takes no parameters, and can be called from any directory in the file system. """ -from __future__ import annotations - -import argparse -import concurrent.futures import os -import posixpath import re -import shutil -import time -from collections import Counter, defaultdict +import sys +from collections import defaultdict from pathlib import Path +# Compile regex patterns once at module level INCLUDE_PATTERN = re.compile(r"^\s*#include.*/.*\.h") -INCLUDE_TARGET_PATTERN = re.compile(r'.*["<]([^">]+)[">].*') -PATHS_LINE_PATTERN = re.compile(r"^\s*(\d+)\s+(\S+)\s+(\S+)\s*$") +INCLUDE_PATH_PATTERN = re.compile(r'[<"]([^>"]+)[>"]') -def dictionary_sort_key(value: str) -> str: - """Approximate `sort -d` behavior used by the shell script.""" - return "".join(ch for ch in value if ch.isalnum() or ch.isspace()) +def dictionary_sort_key(s): + """ + Create a sort key that mimics 'sort -d' (dictionary order). + Dictionary order only considers blanks and alphanumeric characters. + """ + return "".join(c for c in s if c.isalnum() or c.isspace()) -def normalize_level(value: str) -> str: - # Match shell behavior: if level includes a file component (contains "."), - # replace with dirname + "/toplevel". - if "." in value: - parent = posixpath.dirname(value) or "." - value = f"{parent}/toplevel" - return value.replace("/", ".") +def get_level(file_path): + """ + Extract the level from a file path (second and third directory components). + Equivalent to bash: cut -d/ -f 2,3 + + Examples: + src/ripple/app/main.cpp -> ripple.app + src/test/app/Import_test.cpp -> test.app + """ + parts = file_path.split("/") + + if len(parts) >= 3: + level = f"{parts[1]}/{parts[2]}" + elif len(parts) >= 2: + level = f"{parts[1]}/toplevel" + else: + level = file_path + + # If the "level" indicates a file, cut off the filename + if "." in level.split("/")[-1]: + # Use the "toplevel" label as a workaround for `sort` + # inconsistencies between different utility versions + level = level.rsplit("/", 1)[0] + "/toplevel" + + return level.replace("/", ".") -def source_level(rel_path: str) -> str: - parts = rel_path.split("/") - return normalize_level("/".join(parts[1:3])) +def extract_include_level(include_line): + """ + Extract the include path from an #include directive. + Gets the first two directory components from the include path. + Equivalent to bash: cut -d/ -f 1,2 - -def include_level(include_line: str) -> str | None: - match = INCLUDE_TARGET_PATTERN.match(include_line) + Examples: + #include -> ripple.basics + #include "ripple/app/main/Application.h" -> ripple.app + """ + match = INCLUDE_PATH_PATTERN.search(include_line) if not match: return None + include_path = match.group(1) parts = include_path.split("/") - return normalize_level("/".join(parts[:2])) + + if len(parts) >= 2: + include_level = f"{parts[0]}/{parts[1]}" + else: + include_level = include_path + + # If the "includelevel" indicates a file, cut off the filename + if "." in include_level.split("/")[-1]: + include_level = include_level.rsplit("/", 1)[0] + "/toplevel" + + return include_level.replace("/", ".") -def scan_file(path: Path, repo_root: Path) -> tuple[list[str], list[tuple[str, str]]]: - rel = path.relative_to(repo_root).as_posix() - src_level = source_level(rel) +def find_repository_directories(start_path, depth_limit=10): + """ + Find the repository root by looking for src or include folders. + Walks up the directory tree from the start path. + """ + current = start_path.resolve() - raw_lines: list[str] = [] - paths: list[tuple[str, str]] = [] + for _ in range(depth_limit): + src_path = current / "src" + include_path = current / "include" + has_src = src_path.exists() + has_include = include_path.exists() - with path.open("r", encoding="utf-8", errors="ignore") as handle: - for line in handle: - if "boost" in line: - continue - if not INCLUDE_PATTERN.match(line): - continue + if has_src or has_include: + dirs = [] + if has_src: + dirs.append(src_path) + if has_include: + dirs.append(include_path) + return current, dirs - line = line.rstrip("\n") - raw_lines.append(f"{rel}:{line}") + parent = current.parent + if parent == current: + break + current = parent - dst_level = include_level(line) - if dst_level is None: - continue - if src_level != dst_level: - paths.append((src_level, dst_level)) - - return raw_lines, paths + raise RuntimeError( + "Could not find repository root. " + "Expected to find a directory containing 'src' and/or 'include' folders." + ) -def iter_source_files(repo_root: Path) -> list[Path]: - files: list[Path] = [] - for top in ("include", "src"): - root = repo_root / top - if root.exists(): - files.extend(path for path in root.rglob("*") if path.is_file()) - files.sort(key=lambda p: p.relative_to(repo_root).as_posix()) - return files +def main(): + script_dir = Path(__file__).parent.resolve() + os.chdir(script_dir) + # Clean up and create results directory. + results_dir = script_dir / "results" + if results_dir.exists(): + import shutil -def write_relation_db( - results_dir: Path, - edge_counts: list[tuple[tuple[str, str], int]], -) -> tuple[dict[str, list[tuple[str, int]]], dict[str, list[tuple[str, int]]]]: + shutil.rmtree(results_dir) + results_dir.mkdir() + + # Find the repository root. + try: + repo_root, scan_dirs = find_repository_directories(script_dir) + print(f"Found repository root: {repo_root}") + for scan_dir in scan_dirs: + print(f" Scanning: {scan_dir.relative_to(repo_root)}") + except RuntimeError as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + # Find all #include directives. + print("\nScanning for raw includes...") + raw_includes = [] + rawincludes_file = results_dir / "rawincludes.txt" + + with open(rawincludes_file, "w", buffering=8192) as raw_f: + for dir_path in scan_dirs: + for file_path in dir_path.rglob("*"): + if not file_path.is_file(): + continue + try: + rel_path_str = str(file_path.relative_to(repo_root)) + with open( + file_path, "r", encoding="utf-8", errors="ignore", buffering=8192 + ) as f: + for line in f: + if "#include" not in line or "boost" in line: + continue + if INCLUDE_PATTERN.match(line): + line_stripped = line.strip() + entry = f"{rel_path_str}:{line_stripped}\n" + print(entry, end="") + raw_f.write(entry) + raw_includes.append((rel_path_str, line_stripped)) + except Exception as e: + print(f"Error reading {file_path}: {e}", file=sys.stderr) + + # Build levelization paths and count directly. + print("Build levelization paths") + path_counts = defaultdict(int) + + for file_path, include_line in raw_includes: + include_level = extract_include_level(include_line) + if not include_level: + continue + level = get_level(file_path) + if level != include_level: + path_counts[(level, include_level)] += 1 + + # Sort and deduplicate paths. + print("Sort and deduplicate paths") + sorted_items = sorted( + path_counts.items(), + key=lambda x: (dictionary_sort_key(x[0][0]), dictionary_sort_key(x[0][1])), + ) + + paths_file = results_dir / "paths.txt" + with open(paths_file, "w") as f: + for (level, include_level), count in sorted_items: + line = f"{count:7} {level} {include_level}\n" + print(line.rstrip()) + f.write(line) + + # Split into flat-file database. + print("Split into flat-file database") includes_dir = results_dir / "includes" includedby_dir = results_dir / "includedby" - includes_dir.mkdir(parents=True, exist_ok=True) - includedby_dir.mkdir(parents=True, exist_ok=True) + includes_dir.mkdir() + includedby_dir.mkdir() - includes: dict[str, list[tuple[str, int]]] = defaultdict(list) - includedby: dict[str, list[tuple[str, int]]] = defaultdict(list) + includes_data = defaultdict(list) + includedby_data = defaultdict(list) - with (results_dir / "paths.txt").open("w", encoding="utf-8") as out: - for (src, dst), count in edge_counts: - out.write(f"{count:7d} {src} {dst}\n") - includes[src].append((dst, count)) - includedby[dst].append((src, count)) + for (level, include_level), count in sorted_items: + includes_data[level].append((include_level, count)) + includedby_data[include_level].append((level, count)) - for src, entries in includes.items(): - with (includes_dir / src).open("w", encoding="utf-8") as out: - for dst, count in entries: - out.write(f"{dst} {count}\n") + for level in sorted(includes_data.keys(), key=dictionary_sort_key): + with open(includes_dir / level, "w") as f: + for include_level, count in includes_data[level]: + line = f"{include_level} {count}\n" + print(line.rstrip()) + f.write(line) - for dst, entries in includedby.items(): - with (includedby_dir / dst).open("w", encoding="utf-8") as out: - for src, count in entries: - out.write(f"{src} {count}\n") + for include_level in sorted(includedby_data.keys(), key=dictionary_sort_key): + with open(includedby_dir / include_level, "w") as f: + for level, count in includedby_data[include_level]: + line = f"{level} {count}\n" + print(line.rstrip()) + f.write(line) - return includes, includedby + # Search for loops. + print("Search for loops") + loops_file = results_dir / "loops.txt" + ordering_file = results_dir / "ordering.txt" + # Pre-load all include files into memory for fast lookup. + includes_cache = {} + includes_lookup = {} -def build_loops_and_ordering( - includes: dict[str, list[tuple[str, int]]], -) -> tuple[list[str], list[str]]: - include_map = { - src: {dst: count for dst, count in entries} - for src, entries in includes.items() - } + for include_file in sorted(includes_dir.iterdir(), key=lambda p: p.name): + if not include_file.is_file(): + continue + includes_cache[include_file.name] = [] + includes_lookup[include_file.name] = {} + with open(include_file, "r") as f: + for line in f: + parts = line.strip().split() + if len(parts) >= 2: + name, count = parts[0], int(parts[1]) + includes_cache[include_file.name].append((name, count)) + includes_lookup[include_file.name][name] = count - ordering_lines: list[str] = [] - loops_lines: list[str] = [] + loops_found = set() - seen_pairs: set[tuple[str, str]] = set() + with open(loops_file, "w", buffering=8192) as loops_f, open( + ordering_file, "w", buffering=8192 + ) as ordering_f: + for source in sorted(includes_cache.keys()): + for include, include_freq in includes_cache[source]: + if include not in includes_lookup: + continue - for source in sorted(includes.keys()): - for include, includefreq in includes[source]: - if include not in include_map: - continue + source_freq = includes_lookup[include].get(source) - sourcefreq = include_map[include].get(source) - if sourcefreq is None: - ordering_lines.append(f"{source} > {include}\n") - continue + if source_freq is not None: + loop_key = tuple(sorted([source, include])) + if loop_key in loops_found: + continue + loops_found.add(loop_key) - if (include, source) in seen_pairs: - continue - seen_pairs.add((source, include)) + loops_f.write(f"Loop: {source} {include}\n") - loops_lines.append(f"Loop: {source} {include}\n") - if includefreq - sourcefreq > 3: - loops_lines.append(f" {source} > {include}\n\n") - elif sourcefreq - includefreq > 3: - loops_lines.append(f" {include} > {source}\n\n") - elif sourcefreq == includefreq: - loops_lines.append(f" {include} == {source}\n\n") - else: - loops_lines.append(f" {include} ~= {source}\n\n") + diff = include_freq - source_freq + if diff > 3: + loops_f.write(f" {source} > {include}\n\n") + elif diff < -3: + loops_f.write(f" {include} > {source}\n\n") + elif source_freq == include_freq: + loops_f.write(f" {include} == {source}\n\n") + else: + loops_f.write(f" {include} ~= {source}\n\n") + else: + ordering_f.write(f"{source} > {include}\n") - return ordering_lines, loops_lines + # Print results. + print("\nOrdering:") + with open(ordering_file, "r") as f: + print(f.read(), end="") - -def generate(results_dir: Path, repo_root: Path, workers: int) -> None: - if results_dir.exists(): - shutil.rmtree(results_dir) - results_dir.mkdir(parents=True) - - files = iter_source_files(repo_root) - - raw_by_file: dict[str, list[str]] = {} - paths_by_file: dict[str, list[tuple[str, str]]] = {} - - start = time.perf_counter() - if workers <= 1: - for file in files: - rel = file.relative_to(repo_root).as_posix() - raw, paths = scan_file(file, repo_root) - raw_by_file[rel] = raw - paths_by_file[rel] = paths - else: - with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool: - futures = { - file.relative_to(repo_root).as_posix(): pool.submit( - scan_file, file, repo_root - ) - for file in files - } - for rel in sorted(futures.keys()): - raw, paths = futures[rel].result() - raw_by_file[rel] = raw - paths_by_file[rel] = paths - - raw_lines: list[str] = [] - raw_lines.extend( - line - for rel in sorted(raw_by_file.keys()) - for line in raw_by_file[rel] - ) - with (results_dir / "rawincludes.txt").open("w", encoding="utf-8") as out: - out.write("\n".join(raw_lines)) - if raw_lines: - out.write("\n") - - path_pairs: list[tuple[str, str]] = [] - path_pairs.extend( - pair - for rel in sorted(paths_by_file.keys()) - for pair in paths_by_file[rel] - ) - counts = Counter(path_pairs) - - edge_counts = sorted( - counts.items(), - key=lambda item: ( - dictionary_sort_key(item[0][0]), - dictionary_sort_key(item[0][1]), - ), - ) - - includes, _ = write_relation_db(results_dir, edge_counts) - ordering, loops = build_loops_and_ordering(includes) - - with (results_dir / "ordering.txt").open("w", encoding="utf-8") as out: - out.writelines(ordering) - with (results_dir / "loops.txt").open("w", encoding="utf-8") as out: - out.writelines(loops) - - elapsed = time.perf_counter() - start - print( - f"levelization.py: scanned {len(files)} files, " - f"{len(raw_lines)} includes, {len(edge_counts)} unique paths in " - f"{elapsed:.2f}s" - ) - print((results_dir / "ordering.txt").read_text(encoding="utf-8"), end="") - print((results_dir / "loops.txt").read_text(encoding="utf-8"), end="") - - -def main() -> int: - script_dir = Path(__file__).resolve().parent - repo_root = script_dir.parents[1] - - parser = argparse.ArgumentParser() - parser.add_argument( - "--repo-root", - type=Path, - default=repo_root, - help="Repository root (defaults based on script location).", - ) - parser.add_argument( - "--results-dir", - type=Path, - default=script_dir / "results", - help="Output results directory.", - ) - parser.add_argument( - "--workers", - type=int, - default=min(32, (os.cpu_count() or 1)), - help="Thread count for source scanning (default: CPU count, max 32).", - ) - args = parser.parse_args() - - generated_dir = args.results_dir.resolve() - generate( - results_dir=generated_dir, - repo_root=args.repo_root.resolve(), - workers=max(1, args.workers), - ) - - return 0 + print("\nLoops:") + with open(loops_file, "r") as f: + print(f.read(), end="") if __name__ == "__main__": - raise SystemExit(main()) + main() From 66f7294120dfb095542c8f4bb45e7751a15e7eac Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 1 Apr 2026 14:12:33 +0900 Subject: [PATCH 10/12] Test: hint build_test_hooks.sh when hook wasm is empty in hso() --- src/test/jtx/impl/hook.cpp | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/test/jtx/impl/hook.cpp b/src/test/jtx/impl/hook.cpp index 8602e0053..07b72ad00 100644 --- a/src/test/jtx/impl/hook.cpp +++ b/src/test/jtx/impl/hook.cpp @@ -65,29 +65,16 @@ hso_delete(void (*f)(Json::Value& jv)) Json::Value hso(std::vector const& wasmBytes, void (*f)(Json::Value& jv)) { - if (wasmBytes.size() == 0) - throw std::runtime_error("empty hook wasm passed to hso()"); - - Json::Value jv; - jv[jss::CreateCode] = strHex(wasmBytes); - { - jv[jss::HookOn] = - "0000000000000000000000000000000000000000000000000000000000000000"; - jv[jss::HookNamespace] = to_string(uint256{beast::zero}); - jv[jss::HookApiVersion] = Json::Value{0}; - } - - if (f) - f(jv); - - return jv; + return hso(strHex(wasmBytes), f); } Json::Value hso(std::string const& wasmHex, void (*f)(Json::Value& jv)) { if (wasmHex.size() == 0) - throw std::runtime_error("empty hook wasm passed to hso()"); + throw std::runtime_error( + "empty hook wasm passed to hso(): run " + "src/test/app/build_test_hooks.sh to generate the hook wasm"); Json::Value jv; jv[jss::CreateCode] = wasmHex; From 05a3e04f2d3be77384f81b4a4d0758f96d009b14 Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 6 Mar 2026 20:58:07 +0900 Subject: [PATCH 11/12] Fix BEAST_ENHANCED_LOGGING not working and restore original behavior --- cmake/RippledCore.cmake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmake/RippledCore.cmake b/cmake/RippledCore.cmake index c96c42b6c..f4b070e0f 100644 --- a/cmake/RippledCore.cmake +++ b/cmake/RippledCore.cmake @@ -68,6 +68,17 @@ target_link_libraries(xrpl.imports.main $<$:antithesis-sdk-cpp> ) +# date-tz for enhanced logging (always linked, code is #ifdef guarded) +if(TARGET date::date-tz) + target_link_libraries(xrpl.imports.main INTERFACE date::date-tz) +endif() + +# BEAST_ENHANCED_LOGGING: enable for Debug builds OR when explicitly requested +# Uses generator expression so it works with multi-config generators (Xcode, VS, Ninja Multi-Config) +target_compile_definitions(xrpl.imports.main INTERFACE + $<$,$>:BEAST_ENHANCED_LOGGING=1> +) + include(add_module) include(target_link_modules) From cd00ed72d8461f8ae44c0d60c118c357e66799ed Mon Sep 17 00:00:00 2001 From: Alloy Networks <45832257+alloynetworks@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:38:34 +0530 Subject: [PATCH 12/12] change build instructions url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 30efdb776..b34fa8394 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The server software that powers Xahau is called `xahaud` and is available in thi ### Build from Source -* [Read the build instructions in our documentation](https://xahau.network/infrastructure/building-xahau) +* [Read the build instructions in our documentation](https://xahau.network/docs/infrastructure/build-xahaud/) * If you encounter any issues, please [open an issue](https://github.com/xahau/xahaud/issues) ## Highlights of Xahau