From f20977914b876782e6dca011930e61ef19b71a79 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 15 Apr 2025 12:32:35 +0900 Subject: [PATCH] HookCanEmit (#392) --- src/ripple/app/hook/applyHook.h | 8 + src/ripple/app/hook/impl/applyHook.cpp | 35 + src/ripple/app/tx/impl/SetHook.cpp | 71 +- src/ripple/app/tx/impl/Transactor.cpp | 9 + src/ripple/protocol/Feature.h | 3 +- src/ripple/protocol/SField.h | 1 + src/ripple/protocol/impl/Feature.cpp | 1 + .../protocol/impl/InnerObjectFormats.cpp | 2 + src/ripple/protocol/impl/LedgerFormats.cpp | 3 +- src/ripple/protocol/impl/SField.cpp | 1 + src/ripple/protocol/jss.h | 1 + src/test/app/SetHook_test.cpp | 635 +++++++++++++++++- src/test/app/SetHook_wasm.h | 599 ++++++++++++++++- 13 files changed, 1346 insertions(+), 23 deletions(-) diff --git a/src/ripple/app/hook/applyHook.h b/src/ripple/app/hook/applyHook.h index c81514fe4..d6855969a 100644 --- a/src/ripple/app/hook/applyHook.h +++ b/src/ripple/app/hook/applyHook.h @@ -428,6 +428,12 @@ namespace hook { bool canHook(ripple::TxType txType, ripple::uint256 hookOn); +bool +canEmit(ripple::TxType txType, ripple::uint256 hookCanEmit); + +ripple::uint256 +getHookCanEmit(ripple::STObject const& hookObj, SLE::pointer const& hookDef); + struct HookResult; HookResult @@ -436,6 +442,7 @@ apply( used for caching (one day) */ ripple::uint256 const& hookHash, /* hash of the actual hook byte code, used for metadata */ + ripple::uint256 const& hookCanEmit, ripple::uint256 const& hookNamespace, ripple::Blob const& wasm, std::map< @@ -472,6 +479,7 @@ struct HookResult { ripple::uint256 const hookSetTxnID; ripple::uint256 const hookHash; + ripple::uint256 const hookCanEmit; ripple::Keylet const accountKeylet; ripple::Keylet const ownerDirKeylet; ripple::Keylet const hookKeylet; diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index aeb9f3f50..a0c5ff434 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -1028,6 +1028,29 @@ hook::canHook(ripple::TxType txType, ripple::uint256 hookOn) return (hookOn & UINT256_BIT[txType]) != beast::zero; } +bool +hook::canEmit(ripple::TxType txType, ripple::uint256 hookCanEmit) +{ + return hook::canHook(txType, hookCanEmit); +} + +ripple::uint256 +hook::getHookCanEmit( + ripple::STObject const& hookObj, + SLE::pointer const& hookDef) +{ + // default allows all transaction types + uint256 defaultHookCanEmit = UINT256_BIT[ttHOOK_SET]; + + uint256 hookCanEmit = + (hookObj.isFieldPresent(sfHookCanEmit) + ? hookObj.getFieldH256(sfHookCanEmit) + : hookDef->isFieldPresent(sfHookCanEmit) + ? hookDef->getFieldH256(sfHookCanEmit) + : defaultHookCanEmit); + return hookCanEmit; +} + // Update HookState ledger objects for the hook... only called after accept() // assumes the specified acc has already been checked for authoriation (hook // grants) @@ -1179,6 +1202,7 @@ hook::apply( used for caching (one day) */ ripple::uint256 const& hookHash, /* hash of the actual hook byte code, used for metadata */ + ripple::uint256 const& hookCanEmit, ripple::uint256 const& hookNamespace, ripple::Blob const& wasm, std::map< @@ -1206,6 +1230,7 @@ hook::apply( .result = {.hookSetTxnID = hookSetTxnID, .hookHash = hookHash, + .hookCanEmit = hookCanEmit, .accountKeylet = keylet::account(account), .ownerDirKeylet = keylet::ownerDir(account), .hookKeylet = keylet::hook(account), @@ -3270,6 +3295,16 @@ DEFINE_HOOK_FUNCTION( return EMISSION_FAILURE; } + ripple::TxType txType = stpTrans->getTxnType(); + + ripple::uint256 const& hookCanEmit = hookCtx.result.hookCanEmit; + if (!hook::canEmit(txType, hookCanEmit)) + { + JLOG(j.trace()) << "HookEmit[" << HC_ACC() + << "]: Hook cannot emit this txn."; + return EMISSION_FAILURE; + } + // check the emitted txn is valid /* Emitted TXN rules * 0. Account must match the hook account diff --git a/src/ripple/app/tx/impl/SetHook.cpp b/src/ripple/app/tx/impl/SetHook.cpp index 67e89d993..d19a3337a 100644 --- a/src/ripple/app/tx/impl/SetHook.cpp +++ b/src/ripple/app/tx/impl/SetHook.cpp @@ -225,6 +225,7 @@ SetHook::inferOperation(STObject const& hookSetObj) !hookSetObj.isFieldPresent(sfHookNamespace) && !hookSetObj.isFieldPresent(sfHookParameters) && !hookSetObj.isFieldPresent(sfHookOn) && + !hookSetObj.isFieldPresent(sfHookCanEmit) && !hookSetObj.isFieldPresent(sfHookApiVersion) && !hookSetObj.isFieldPresent(sfFlags)) return hsoNOOP; @@ -259,6 +260,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj) if (hookSetObj.isFieldPresent(sfHookGrants) || hookSetObj.isFieldPresent(sfHookParameters) || hookSetObj.isFieldPresent(sfHookOn) || + hookSetObj.isFieldPresent(sfHookCanEmit) || hookSetObj.isFieldPresent(sfHookApiVersion) || !hookSetObj.isFieldPresent(sfFlags) || !hookSetObj.isFieldPresent(sfHookNamespace)) @@ -288,6 +290,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj) if (hookSetObj.isFieldPresent(sfHookGrants) || hookSetObj.isFieldPresent(sfHookParameters) || hookSetObj.isFieldPresent(sfHookOn) || + hookSetObj.isFieldPresent(sfHookCanEmit) || hookSetObj.isFieldPresent(sfHookApiVersion) || hookSetObj.isFieldPresent(sfHookNamespace) || !hookSetObj.isFieldPresent(sfFlags)) @@ -452,6 +455,13 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj) return false; } + // validate sfHookCanEmit + // HookCanEmit field is an optional field for backward compatibility + if (!hookSetObj.isFieldPresent(sfHookCanEmit)) + { + // pass + } + // finally validate web assembly byte code { if (!hookSetObj.isFieldPresent(sfCreateCode)) @@ -714,6 +724,10 @@ SetHook::preflight(PreflightContext const& ctx) allBlank = false; + if (!ctx.rules.enabled(featureHookCanEmit) && + hookSetObj.isFieldPresent(sfHookCanEmit)) + return temDISABLED; + for (auto const& hookSetElement : hookSetObj) { auto const& name = hookSetElement.getFName(); @@ -721,7 +735,8 @@ SetHook::preflight(PreflightContext const& ctx) if (name != sfCreateCode && name != sfHookHash && name != sfHookNamespace && name != sfHookParameters && name != sfHookOn && name != sfHookGrants && - name != sfHookApiVersion && name != sfFlags) + name != sfHookApiVersion && name != sfFlags && + name != sfHookCanEmit) { JLOG(ctx.j.trace()) << "HookSet(" << hook::log::HOOK_INVALID_FIELD << ")[" @@ -1222,6 +1237,10 @@ SetHook::setHook() std::optional newHookOn; std::optional defHookOn; + std::optional oldHookCanEmit; + std::optional newHookCanEmit; + std::optional defHookCanEmit; + // when hsoCREATE is invoked it populates this variable in case the hook // definition already exists and the operation falls through into a // hsoINSTALL operation instead @@ -1282,6 +1301,14 @@ SetHook::setHook() oldHookOn = oldHook->get().getFieldH256(sfHookOn); else if (defHookOn) oldHookOn = *defHookOn; + + if (oldDefSLE && oldDefSLE->isFieldPresent(sfHookCanEmit)) + defHookCanEmit = oldDefSLE->getFieldH256(sfHookCanEmit); + + if (oldHook && oldHook->get().isFieldPresent(sfHookCanEmit)) + oldHookCanEmit = oldHook->get().getFieldH256(sfHookCanEmit); + else if (defHookCanEmit) + oldHookCanEmit = *defHookCanEmit; } // in preparation for three way merge populate fields if they are @@ -1298,6 +1325,9 @@ SetHook::setHook() if (hookSetObj->get().isFieldPresent(sfHookOn)) newHookOn = hookSetObj->get().getFieldH256(sfHookOn); + if (hookSetObj->get().isFieldPresent(sfHookCanEmit)) + newHookCanEmit = hookSetObj->get().getFieldH256(sfHookCanEmit); + if (hookSetObj->get().isFieldPresent(sfHookNamespace)) { newNamespace = hookSetObj->get().getFieldH256(sfHookNamespace); @@ -1315,13 +1345,14 @@ SetHook::setHook() } else if (op == hsoNSDELETE && newDirKeylet) { - printf("Marking a namespace for destruction.... NSDELETE\n"); + JLOG(ctx.j.trace()) + << "Marking a namespace for destruction.... NSDELETE"; namespacesToDestroy.emplace(*newNamespace); } else if (oldDirKeylet) { - printf( - "Marking a namespace for destruction.... non-NSDELETE\n"); + JLOG(ctx.j.trace()) + << "Marking a namespace for destruction.... non-NSDELETE"; namespacesToDestroy.emplace(*oldNamespace); } else @@ -1407,6 +1438,10 @@ SetHook::setHook() if (oldHook->get().isFieldPresent(sfHookOn)) newHook.setFieldH256( sfHookOn, oldHook->get().getFieldH256(sfHookOn)); + if (oldHook->get().isFieldPresent(sfHookCanEmit)) + newHook.setFieldH256( + sfHookCanEmit, + oldHook->get().getFieldH256(sfHookCanEmit)); if (oldHook->get().isFieldPresent(sfHookNamespace)) newHook.setFieldH256( sfHookNamespace, @@ -1436,6 +1471,19 @@ SetHook::setHook() newHook.setFieldH256(sfHookOn, *newHookOn); } + // set the hookcanemit field if it differs from definition + if (newHookCanEmit) + { + if (defHookCanEmit.has_value() && + *defHookCanEmit == *newHookCanEmit) + { + if (newHook.isFieldPresent(sfHookCanEmit)) + newHook.makeFieldAbsent(sfHookCanEmit); + } + else + newHook.setFieldH256(sfHookCanEmit, *newHookCanEmit); + } + // parameters if (hookSetObj->get().isFieldPresent(sfHookParameters) && hookSetObj->get().getFieldArray(sfHookParameters).empty()) @@ -1585,6 +1633,9 @@ SetHook::setHook() auto newHookDef = std::make_shared(keylet); newHookDef->setFieldH256(sfHookHash, *createHookHash); newHookDef->setFieldH256(sfHookOn, *newHookOn); + if (newHookCanEmit) + newHookDef->setFieldH256( + sfHookCanEmit, *newHookCanEmit); newHookDef->setFieldH256(sfHookNamespace, *newNamespace); newHookDef->setFieldArray( sfHookParameters, @@ -1678,6 +1729,8 @@ SetHook::setHook() // change which definition we're using to the new target defNamespace = newDefSLE->getFieldH256(sfHookNamespace); defHookOn = newDefSLE->getFieldH256(sfHookOn); + if (newDefSLE->isFieldPresent(sfHookCanEmit)) + defHookCanEmit = newDefSLE->getFieldH256(sfHookCanEmit); // set the namespace if it differs from the definition namespace if (newNamespace && *defNamespace != *newNamespace) @@ -1687,6 +1740,12 @@ SetHook::setHook() if (newHookOn && *defHookOn != *newHookOn) newHook.setFieldH256(sfHookOn, *newHookOn); + // set the hookcanemit field if it differs from definition + if (newHookCanEmit && + !(defHookCanEmit.has_value() && + *defHookCanEmit == *newHookCanEmit)) + newHook.setFieldH256(sfHookCanEmit, *newHookCanEmit); + // parameters TER result = updateHookParameters( ctx, @@ -1735,8 +1794,8 @@ SetHook::setHook() // sfHook: 1 reserve PER non-blank entry // sfParameters: 1 reserve PER entry // sfGrants are: 1 reserve PER entry - // sfHookHash, sfHookNamespace, sfHookOn, sfHookApiVersion, sfFlags: - // free + // sfHookHash, sfHookNamespace, sfHookOn, sfHookCanEmit, + // sfHookApiVersion, sfFlags: free // sfHookDefinition is not reserved because it is an unowned object, // rather the uploader is billed via fee according to the following: diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 621667896..6d06e89ad 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -1230,6 +1230,8 @@ Transactor::executeHookChain( if (!hook::canHook(ctx_.tx.getTxnType(), hookOn)) continue; // skip if it can't + uint256 hookCanEmit = hook::getHookCanEmit(hookObj, hookDef); + uint32_t flags = (hookObj.isFieldPresent(sfFlags) ? hookObj.getFieldU32(sfFlags) : hookDef->getFieldU32(sfFlags)); @@ -1265,6 +1267,7 @@ Transactor::executeHookChain( results.push_back(hook::apply( hookDef->getFieldH256(sfHookSetTxnID), hookHash, + hookCanEmit, ns, hookDef->getFieldVL(sfCreateCode), parameters, @@ -1395,6 +1398,8 @@ Transactor::doHookCallback( if (hookObj.getFieldH256(sfHookHash) != callbackHookHash) continue; + uint256 hookCanEmit = hook::getHookCanEmit(hookObj, hookDef); + // fetch the namespace either from the hook object of, if absent, the // hook def uint256 const& ns = @@ -1420,6 +1425,7 @@ Transactor::doHookCallback( hook::HookResult callbackResult = hook::apply( hookDef->getFieldH256(sfHookSetTxnID), callbackHookHash, + hookCanEmit, ns, hookDef->getFieldVL(sfCreateCode), parameters, @@ -1667,6 +1673,8 @@ Transactor::doAgainAsWeak( continue; } + uint256 hookCanEmit = hook::getHookCanEmit(hookObj, hookDef); + // fetch the namespace either from the hook object of, if absent, the // hook def uint256 const& ns = @@ -1687,6 +1695,7 @@ Transactor::doAgainAsWeak( hook::HookResult aawResult = hook::apply( hookDef->getFieldH256(sfHookSetTxnID), hookHash, + hookCanEmit, ns, hookDef->getFieldVL(sfCreateCode), parameters, diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 3c6e625a9..e06bb7c11 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -74,7 +74,7 @@ namespace detail { // Feature.cpp. Because it's only used to reserve storage, and determine how // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // the actual number of amendments. A LogicError on startup will verify this. -static constexpr std::size_t numFeatures = 78; +static constexpr std::size_t numFeatures = 79; /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated @@ -366,6 +366,7 @@ extern uint256 const featureTouch; extern uint256 const fixReduceImport; extern uint256 const fixXahauV3; extern uint256 const fix20250131; +extern uint256 const featureHookCanEmit; } // namespace ripple diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index 0fc34ce09..56aed3a5a 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -450,6 +450,7 @@ extern SF_UINT256 const sfParentHash; extern SF_UINT256 const sfTransactionHash; extern SF_UINT256 const sfAccountHash; extern SF_UINT256 const sfHookOn; +extern SF_UINT256 const sfHookCanEmit; extern SF_UINT256 const sfPreviousTxnID; extern SF_UINT256 const sfLedgerIndex; extern SF_UINT256 const sfWalletLocator; diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index d32950946..f285574ff 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -472,6 +472,7 @@ REGISTER_FEATURE(Touch, Supported::yes, VoteBehavior::De REGISTER_FIX (fixReduceImport, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FIX (fixXahauV3, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FIX (fix20250131, Supported::yes, VoteBehavior::DefaultYes); +REGISTER_FEATURE(HookCanEmit, Supported::yes, VoteBehavior::DefaultNo); // The following amendments are obsolete, but must remain supported // because they could potentially get enabled. diff --git a/src/ripple/protocol/impl/InnerObjectFormats.cpp b/src/ripple/protocol/impl/InnerObjectFormats.cpp index e52d6ff8f..99f826651 100644 --- a/src/ripple/protocol/impl/InnerObjectFormats.cpp +++ b/src/ripple/protocol/impl/InnerObjectFormats.cpp @@ -88,6 +88,7 @@ InnerObjectFormats::InnerObjectFormats() {sfHookNamespace, soeREQUIRED}, {sfHookParameters, soeREQUIRED}, {sfHookOn, soeREQUIRED}, + {sfHookCanEmit, soeOPTIONAL}, {sfHookApiVersion, soeREQUIRED}, {sfFlags, soeREQUIRED}, {sfFee, soeREQUIRED}}); @@ -100,6 +101,7 @@ InnerObjectFormats::InnerObjectFormats() {sfHookNamespace, soeOPTIONAL}, {sfHookParameters, soeOPTIONAL}, {sfHookOn, soeOPTIONAL}, + {sfHookCanEmit, soeOPTIONAL}, {sfHookApiVersion, soeOPTIONAL}, {sfFlags, soeOPTIONAL}}); diff --git a/src/ripple/protocol/impl/LedgerFormats.cpp b/src/ripple/protocol/impl/LedgerFormats.cpp index 4fbcb961a..02b381d33 100644 --- a/src/ripple/protocol/impl/LedgerFormats.cpp +++ b/src/ripple/protocol/impl/LedgerFormats.cpp @@ -225,7 +225,8 @@ LedgerFormats::LedgerFormats() ltHOOK_DEFINITION, { {sfHookHash, soeREQUIRED}, - {sfHookOn, soeREQUIRED}, + {sfHookOn, soeREQUIRED}, + {sfHookCanEmit, soeOPTIONAL}, {sfHookNamespace, soeREQUIRED}, {sfHookParameters, soeREQUIRED}, {sfHookApiVersion, soeREQUIRED}, diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index 3dee6b3a4..8b8050351 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -237,6 +237,7 @@ CONSTRUCT_TYPED_SFIELD(sfURITokenID, "URITokenID", UINT256, CONSTRUCT_TYPED_SFIELD(sfGovernanceFlags, "GovernanceFlags", UINT256, 99); CONSTRUCT_TYPED_SFIELD(sfGovernanceMarks, "GovernanceMarks", UINT256, 98); CONSTRUCT_TYPED_SFIELD(sfEmittedTxnID, "EmittedTxnID", UINT256, 97); +CONSTRUCT_TYPED_SFIELD(sfHookCanEmit, "HookCanEmit", UINT256, 96); // currency amount (common) CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1); diff --git a/src/ripple/protocol/jss.h b/src/ripple/protocol/jss.h index 16e59ec50..961e63c44 100644 --- a/src/ripple/protocol/jss.h +++ b/src/ripple/protocol/jss.h @@ -78,6 +78,7 @@ JSS(GenesisMints); JSS(GovernanceMarks); JSS(GovernanceFlags); JSS(HookApiVersion); // field +JSS(HookCanEmit); // field JSS(HookHash); // field JSS(HookNamespace); // field JSS(HookOn); // field diff --git a/src/test/app/SetHook_test.cpp b/src/test/app/SetHook_test.cpp index f8f8d9cf9..e122616e6 100644 --- a/src/test/app/SetHook_test.cpp +++ b/src/test/app/SetHook_test.cpp @@ -644,6 +644,9 @@ public: using namespace jtx; Env env{*this, features}; + bool const hasHookCanEmit = + env.current()->rules().enabled(featureHookCanEmit); + auto const alice = Account{"alice"}; env.fund(XRP(10000), alice); @@ -678,24 +681,30 @@ public: env.close(); } - // grants, parameters, hookon, hookapiversion, hooknamespace keys must - // be absent + // grants, parameters, hookon, hookcanemit, hookapiversion, + // hooknamespace keys must be absent for (auto const& [key, value] : JSSMap{ {jss::HookGrants, Json::arrayValue}, {jss::HookParameters, Json::arrayValue}, {jss::HookOn, "000000000000000000000000000000000000000000000000000000000000" "0000"}, + {jss::HookCanEmit, + "000000000000000000000000000000000000000000000000000000000000" + "0000"}, {jss::HookApiVersion, "0"}, {jss::HookNamespace, to_string(uint256{beast::zero})}}) { + if (!hasHookCanEmit && key == jss::HookCanEmit) + continue; + Json::Value iv; iv[jss::CreateCode] = ""; iv[key] = value; jv[jss::Hooks][0U][jss::Hook] = iv; env(jv, M("Hook DELETE operation cannot include: grants, params, " - "hookon, apiversion, namespace"), + "hookon, hookcanemit, apiversion, namespace"), HSFEE, ter(temMALFORMED)); env.close(); @@ -832,6 +841,8 @@ public: Env env{*this, features}; bool const fixNS = env.current()->rules().enabled(fixNSDelete); + bool const hasHookCanEmit = + env.current()->rules().enabled(featureHookCanEmit); auto const alice = Account{"alice"}; env.fund(XRP(10000), alice); @@ -851,9 +862,15 @@ public: {jss::HookOn, "000000000000000000000000000000000000000000000000000000000000" "0000"}, + {jss::HookCanEmit, + "000000000000000000000000000000000000000000000000000000000000" + "0000"}, {jss::HookApiVersion, "0"}, }) { + if (!hasHookCanEmit && key == jss::HookCanEmit) + continue; + Json::Value iv; iv[key] = value; iv[jss::Flags] = hsfNSDELETE; @@ -861,7 +878,7 @@ public: jv[jss::Hooks][0U][jss::Hook] = iv; env(jv, M("Hook NSDELETE operation cannot include: grants, params, " - "hookon, apiversion"), + "hookon, hookcanemit, apiversion"), HSFEE, ter(temMALFORMED)); env.close(); @@ -1194,6 +1211,9 @@ public: using namespace jtx; Env env{*this, features}; + bool const hasHookCanEmit = + env.current()->rules().enabled(featureHookCanEmit); + auto const bob = Account{"bob"}; env.fund(XRP(10000), bob); @@ -1237,6 +1257,10 @@ public: iv[jss::HookOn] = "00000000000000000000000000000000000000000000000000000000000000" "00"; + if (hasHookCanEmit) + iv[jss::HookCanEmit] = + "0000000000000000000000000000000000000000000000000000000000" + "000000"; jv[jss::Hooks][0U] = Json::Value{}; jv[jss::Hooks][0U][jss::Hook] = iv; @@ -1255,6 +1279,10 @@ public: iv[jss::HookOn] = "00000000000000000000000000000000000000000000000000000000000000" "00"; + if (hasHookCanEmit) + iv[jss::HookCanEmit] = + "0000000000000000000000000000000000000000000000000000000000" + "000000"; jv[jss::Hooks][0U] = Json::Value{}; jv[jss::Hooks][0U][jss::Hook] = iv; @@ -1274,6 +1302,10 @@ public: iv[jss::HookOn] = "00000000000000000000000000000000000000000000000000000000000000" "00"; + if (hasHookCanEmit) + iv[jss::HookCanEmit] = + "0000000000000000000000000000000000000000000000000000000000" + "000000"; jv[jss::Hooks][0U] = Json::Value{}; jv[jss::Hooks][0U][jss::Hook] = iv; @@ -1290,6 +1322,10 @@ public: iv[jss::CreateCode] = strHex(accept_wasm); iv[jss::HookNamespace] = to_string(uint256{beast::zero}); iv[jss::HookApiVersion] = 0U; + if (hasHookCanEmit) + iv[jss::HookCanEmit] = + "0000000000000000000000000000000000000000000000000000000000" + "000000"; jv[jss::Hooks][0U] = Json::Value{}; jv[jss::Hooks][0U][jss::Hook] = iv; @@ -1436,6 +1472,9 @@ public: using namespace jtx; Env env{*this, features}; + bool const hasHookCanEmit = + env.current()->rules().enabled(featureHookCanEmit); + auto const alice = Account{"alice"}; env.fund(XRP(10000), alice); @@ -1457,6 +1496,10 @@ public: iv[jss::HookOn] = "00000000000000000000000000000000000000000000000000000000000000" "00"; + if (hasHookCanEmit) + iv[jss::HookCanEmit] = + "0000000000000000000000000000000000000000000000000000000000" + "000000"; iv[jss::HookParameters] = Json::Value{Json::arrayValue}; iv[jss::HookParameters][0U] = Json::Value{}; iv[jss::HookParameters][0U][jss::HookParameter] = Json::Value{}; @@ -1538,12 +1581,18 @@ public: {jss::HookOn, "00000000000000000000000000000000000000000000000000000000" "00000001"}, + {jss::HookCanEmit, + "00000000000000000000000000000000000000000000000000000000" + "00000001"}, {jss::HookNamespace, "CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE" "CAFECAFE"}, {jss::HookParameters, params}, {jss::HookGrants, grants}}) { + if (!hasHookCanEmit && key == jss::HookCanEmit) + continue; + Json::Value iv; iv[key] = value; jv[jss::Hooks][0U] = Json::Value{}; @@ -1566,6 +1615,15 @@ public: BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookOn)); BEAST_EXPECT(hooks[0].getFieldH256(sfHookOn) == UINT256_BIT[0]); + if (hasHookCanEmit) + { + BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookCanEmit)); + BEAST_EXPECT( + hooks[0].getFieldH256(sfHookCanEmit) == + ripple::uint256("000000000000000000000000000000000000000000" + "0000000000000000000001")); + } + auto const ns = uint256::fromVoid( (std::array{ 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, @@ -1597,14 +1655,20 @@ public: BEAST_REQUIRE(g[0].getFieldH256(sfHookHash) == accept_hash); } - // reset hookon and namespace to defaults + // reset hookon, hookcanemit, and namespace to defaults { for (auto const& [key, value] : JSSMap{ {jss::HookOn, "00000000000000000000000000000000000000000000000000000000" "00000000"}, + {jss::HookCanEmit, + "00000000000000000000000000000000000000000000000000000000" + "00000000"}, {jss::HookNamespace, to_string(uint256{beast::zero})}}) { + if (key == jss::HookCanEmit && !hasHookCanEmit) + continue; + Json::Value iv; iv[key] = value; jv[jss::Hooks][0U] = Json::Value{}; @@ -1626,6 +1690,7 @@ public: // ensure the two fields are now absent (because they were reset to // the defaults on the hook def) BEAST_EXPECT(!hooks[0].isFieldPresent(sfHookOn)); + BEAST_EXPECT(!hooks[0].isFieldPresent(sfHookCanEmit)); BEAST_EXPECT(!hooks[0].isFieldPresent(sfHookNamespace)); } @@ -1845,12 +1910,18 @@ public: {jss::HookOn, "00000000000000000000000000000000000000000000000000000000" "00000001"}, + {jss::HookCanEmit, + "00000000000000000000000000000000000000000000000000000000" + "00000001"}, {jss::HookNamespace, "CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE" "CAFECAFE"}, {jss::HookParameters, params}, {jss::HookGrants, grants}}) { + if (key == jss::HookCanEmit && !hasHookCanEmit) + continue; + Json::Value iv; iv[key] = value; jv[jss::Hooks][0U] = Json::Value{}; @@ -2366,10 +2437,7 @@ public: { testcase("Test float_emit"); using namespace jtx; - Env env{ - *this, envconfig(), features, nullptr, beast::severities::kWarning - // beast::severities::kTrace - }; + Env env{*this, features}; auto const alice = Account{"alice"}; auto const bob = Account{"bob"}; @@ -12119,6 +12187,551 @@ public: env(pay(bob, alice, XRP(1)), M("test util_verify"), fee(XRP(1))); } + void + testHookCanEmit(FeatureBitset features) + { + testcase("test HookCanEmit"); + using namespace jtx; + Env env{*this, features}; + + auto const caller = Account{"caller"}; + auto const alice = Account{"alice"}; + auto const bob = Account{"bob"}; + auto const charlie = Account{"charlie"}; + auto const hookacc = Account{"hookacc"}; + env.fund(XRP(10000), caller); + env.fund(XRP(10000), alice); + env.fund(XRP(10000), bob); + env.fund(XRP(10000), charlie); + env.fund(XRP(10000), hookacc); + env.close(); + + TestHook hook = 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); + extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code); + extern int64_t emit (uint32_t, uint32_t, uint32_t, uint32_t); + extern int64_t etxn_reserve(uint32_t); + extern int64_t hook_account(uint32_t, uint32_t); + extern int64_t otxn_field(uint32_t, uint32_t, uint32_t); + extern int64_t hook_pos(void); + #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) + #define OUT_OF_BOUNDS (-1) + #define ttPAYMENT 0 + #define ttACCOUNT_SET 3 + #define ttHOOK_SET 22 + #define tfCANONICAL 0x80000000UL + #define amAMOUNT 1U + #define amFEE 8U + #define atACCOUNT 1U + #define DOESNT_EXIST (-5) + #define atDESTINATION 3U + #define SBUF(x) (uint32_t)x,sizeof(x) + #define sfAccount ((8U << 16U) + 1U) + #define EMISSION_FAILURE -11 + + #define ASSERT(x)\ + if (!(x))\ + rollback((uint32_t)#x, sizeof(#x), __LINE__); + + #define PREREQUISITE_NOT_MET -9 + #define ENCODE_DROPS_SIZE 9 + #define ENCODE_DROPS(buf_out, drops, amount_type ) \ + {\ + uint8_t uat = amount_type; \ + uint64_t udrops = drops; \ + buf_out[0] = 0x60U +(uat & 0x0FU ); \ + buf_out[1] = 0b01000000 + (( udrops >> 56 ) & 0b00111111 ); \ + buf_out[2] = (udrops >> 48) & 0xFFU; \ + buf_out[3] = (udrops >> 40) & 0xFFU; \ + buf_out[4] = (udrops >> 32) & 0xFFU; \ + buf_out[5] = (udrops >> 24) & 0xFFU; \ + buf_out[6] = (udrops >> 16) & 0xFFU; \ + buf_out[7] = (udrops >> 8) & 0xFFU; \ + buf_out[8] = (udrops >> 0) & 0xFFU; \ + buf_out += ENCODE_DROPS_SIZE; \ + } + + #define _06_XX_ENCODE_DROPS(buf_out, drops, amount_type )\ + ENCODE_DROPS(buf_out, drops, amount_type ); + + #define ENCODE_DROPS_AMOUNT(buf_out, drops )\ + ENCODE_DROPS(buf_out, drops, amAMOUNT ); + #define _06_01_ENCODE_DROPS_AMOUNT(buf_out, drops )\ + ENCODE_DROPS_AMOUNT(buf_out, drops ); + + #define ENCODE_DROPS_FEE(buf_out, drops )\ + ENCODE_DROPS(buf_out, drops, amFEE ); + #define _06_08_ENCODE_DROPS_FEE(buf_out, drops )\ + ENCODE_DROPS_FEE(buf_out, drops ); + + #define ENCODE_TT_SIZE 3 + #define ENCODE_TT(buf_out, tt )\ + {\ + uint8_t utt = tt;\ + buf_out[0] = 0x12U;\ + buf_out[1] =(utt >> 8 ) & 0xFFU;\ + buf_out[2] =(utt >> 0 ) & 0xFFU;\ + buf_out += ENCODE_TT_SIZE; \ + } + #define _01_02_ENCODE_TT(buf_out, tt)\ + ENCODE_TT(buf_out, tt); + + + #define ENCODE_ACCOUNT_SIZE 22 + #define ENCODE_ACCOUNT(buf_out, account_id, account_type)\ + {\ + uint8_t uat = account_type;\ + buf_out[0] = 0x80U + uat;\ + buf_out[1] = 0x14U;\ + *(uint64_t*)(buf_out + 2) = *(uint64_t*)(account_id + 0);\ + *(uint64_t*)(buf_out + 10) = *(uint64_t*)(account_id + 8);\ + *(uint32_t*)(buf_out + 18) = *(uint32_t*)(account_id + 16);\ + buf_out += ENCODE_ACCOUNT_SIZE;\ + } + #define _08_XX_ENCODE_ACCOUNT(buf_out, account_id, account_type)\ + ENCODE_ACCOUNT(buf_out, account_id, account_type); + + #define ENCODE_ACCOUNT_SRC_SIZE 22 + #define ENCODE_ACCOUNT_SRC(buf_out, account_id)\ + ENCODE_ACCOUNT(buf_out, account_id, atACCOUNT); + #define _08_01_ENCODE_ACCOUNT_SRC(buf_out, account_id)\ + ENCODE_ACCOUNT_SRC(buf_out, account_id); + + #define ENCODE_ACCOUNT_DST_SIZE 22 + #define ENCODE_ACCOUNT_DST(buf_out, account_id)\ + ENCODE_ACCOUNT(buf_out, account_id, atDESTINATION); + #define _08_03_ENCODE_ACCOUNT_DST(buf_out, account_id)\ + ENCODE_ACCOUNT_DST(buf_out, account_id); + + #define ENCODE_ACCOUNT_OWNER_SIZE 22 + #define ENCODE_ACCOUNT_OWNER(buf_out, account_id) \ + ENCODE_ACCOUNT(buf_out, account_id, atOWNER); + #define _08_02_ENCODE_ACCOUNT_OWNER(buf_out, account_id) \ + ENCODE_ACCOUNT_OWNER(buf_out, account_id); + + #define ENCODE_UINT32_COMMON_SIZE 5U + #define ENCODE_UINT32_COMMON(buf_out, i, field)\ + {\ + uint32_t ui = i; \ + uint8_t uf = field; \ + buf_out[0] = 0x20U +(uf & 0x0FU); \ + buf_out[1] =(ui >> 24 ) & 0xFFU; \ + buf_out[2] =(ui >> 16 ) & 0xFFU; \ + buf_out[3] =(ui >> 8 ) & 0xFFU; \ + buf_out[4] =(ui >> 0 ) & 0xFFU; \ + buf_out += ENCODE_UINT32_COMMON_SIZE; \ + } + #define _02_XX_ENCODE_UINT32_COMMON(buf_out, i, field)\ + ENCODE_UINT32_COMMON(buf_out, i, field)\ + + #define ENCODE_UINT32_UNCOMMON_SIZE 6U + #define ENCODE_UINT32_UNCOMMON(buf_out, i, field)\ + {\ + uint32_t ui = i; \ + uint8_t uf = field; \ + buf_out[0] = 0x20U; \ + buf_out[1] = uf; \ + buf_out[2] =(ui >> 24 ) & 0xFFU; \ + buf_out[3] =(ui >> 16 ) & 0xFFU; \ + buf_out[4] =(ui >> 8 ) & 0xFFU; \ + buf_out[5] =(ui >> 0 ) & 0xFFU; \ + buf_out += ENCODE_UINT32_UNCOMMON_SIZE; \ + } + #define _02_XX_ENCODE_UINT32_UNCOMMON(buf_out, i, field)\ + ENCODE_UINT32_UNCOMMON(buf_out, i, field)\ + + #define ENCODE_LLS_SIZE 6U + #define ENCODE_LLS(buf_out, lls )\ + ENCODE_UINT32_UNCOMMON(buf_out, lls, 0x1B ); + #define _02_27_ENCODE_LLS(buf_out, lls )\ + ENCODE_LLS(buf_out, lls ); + + #define ENCODE_FLS_SIZE 6U + #define ENCODE_FLS(buf_out, fls )\ + ENCODE_UINT32_UNCOMMON(buf_out, fls, 0x1A ); + #define _02_26_ENCODE_FLS(buf_out, fls )\ + ENCODE_FLS(buf_out, fls ); + + #define ENCODE_TAG_SRC_SIZE 5 + #define ENCODE_TAG_SRC(buf_out, tag )\ + ENCODE_UINT32_COMMON(buf_out, tag, 0x3U ); + #define _02_03_ENCODE_TAG_SRC(buf_out, tag )\ + ENCODE_TAG_SRC(buf_out, tag ); + + #define ENCODE_TAG_DST_SIZE 5 + #define ENCODE_TAG_DST(buf_out, tag )\ + ENCODE_UINT32_COMMON(buf_out, tag, 0xEU ); + #define _02_14_ENCODE_TAG_DST(buf_out, tag )\ + ENCODE_TAG_DST(buf_out, tag ); + + #define ENCODE_SEQUENCE_SIZE 5 + #define ENCODE_SEQUENCE(buf_out, sequence )\ + ENCODE_UINT32_COMMON(buf_out, sequence, 0x4U ); + #define _02_04_ENCODE_SEQUENCE(buf_out, sequence )\ + ENCODE_SEQUENCE(buf_out, sequence ); + + #define ENCODE_FLAGS_SIZE 5 + #define ENCODE_FLAGS(buf_out, tag )\ + ENCODE_UINT32_COMMON(buf_out, tag, 0x2U ); + #define _02_02_ENCODE_FLAGS(buf_out, tag )\ + ENCODE_FLAGS(buf_out, tag ); + + #define ENCODE_SIGNING_PUBKEY_SIZE 35 + #define ENCODE_SIGNING_PUBKEY(buf_out, pkey )\ + {\ + buf_out[0] = 0x73U;\ + buf_out[1] = 0x21U;\ + *(uint64_t*)(buf_out + 2) = *(uint64_t*)(pkey + 0);\ + *(uint64_t*)(buf_out + 10) = *(uint64_t*)(pkey + 8);\ + *(uint64_t*)(buf_out + 18) = *(uint64_t*)(pkey + 16);\ + *(uint64_t*)(buf_out + 26) = *(uint64_t*)(pkey + 24);\ + buf[34] = pkey[32];\ + buf_out += ENCODE_SIGNING_PUBKEY_SIZE;\ + } + + #define _07_03_ENCODE_SIGNING_PUBKEY(buf_out, pkey )\ + ENCODE_SIGNING_PUBKEY(buf_out, pkey ); + + #define ENCODE_SIGNING_PUBKEY_NULL_SIZE 35 + #define ENCODE_SIGNING_PUBKEY_NULL(buf_out )\ + {\ + buf_out[0] = 0x73U;\ + buf_out[1] = 0x21U;\ + *(uint64_t*)(buf_out+2) = 0;\ + *(uint64_t*)(buf_out+10) = 0;\ + *(uint64_t*)(buf_out+18) = 0;\ + *(uint64_t*)(buf_out+25) = 0;\ + buf_out += ENCODE_SIGNING_PUBKEY_NULL_SIZE;\ + } + + #define _07_03_ENCODE_SIGNING_PUBKEY_NULL(buf_out )\ + ENCODE_SIGNING_PUBKEY_NULL(buf_out ); + + extern int64_t etxn_fee_base ( + uint32_t read_ptr, + uint32_t read_len + ); + extern int64_t etxn_details ( + uint32_t write_ptr, + uint32_t write_len + ); + extern int64_t ledger_seq (void); + + #define PREPARE_PAYMENT_SIMPLE_SIZE 248U + #define PREPARE_PAYMENT_SIMPLE(buf_out_master, drops_amount_raw, to_address, dest_tag_raw, src_tag_raw)\ + {\ + uint8_t* buf_out = buf_out_master;\ + uint8_t acc[20];\ + uint64_t drops_amount = (drops_amount_raw);\ + uint32_t dest_tag = (dest_tag_raw);\ + uint32_t src_tag = (src_tag_raw);\ + uint32_t cls = (uint32_t)ledger_seq();\ + hook_account(SBUF(acc));\ + _01_02_ENCODE_TT (buf_out, ttPAYMENT ); /* uint16 | size 3 */ \ + _02_02_ENCODE_FLAGS (buf_out, tfCANONICAL ); /* uint32 | size 5 */ \ + _02_03_ENCODE_TAG_SRC (buf_out, src_tag ); /* uint32 | size 5 */ \ + _02_04_ENCODE_SEQUENCE (buf_out, 0 ); /* uint32 | size 5 */ \ + _02_14_ENCODE_TAG_DST (buf_out, dest_tag ); /* uint32 | size 5 */ \ + _02_26_ENCODE_FLS (buf_out, cls + 1 ); /* uint32 | size 6 */ \ + _02_27_ENCODE_LLS (buf_out, cls + 5 ); /* uint32 | size 6 */ \ + _06_01_ENCODE_DROPS_AMOUNT (buf_out, drops_amount ); /* amount | size 9 */ \ + uint8_t* fee_ptr = buf_out;\ + _06_08_ENCODE_DROPS_FEE (buf_out, 0 ); /* amount | size 9 */ \ + _07_03_ENCODE_SIGNING_PUBKEY_NULL (buf_out ); /* pk | size 35 */ \ + _08_01_ENCODE_ACCOUNT_SRC (buf_out, acc ); /* account | size 22 */ \ + _08_03_ENCODE_ACCOUNT_DST (buf_out, to_address ); /* account | size 22 */ \ + int64_t edlen = etxn_details((uint32_t)buf_out, PREPARE_PAYMENT_SIMPLE_SIZE); /* emitdet | size 116 */ \ + int64_t fee = etxn_fee_base(buf_out_master, PREPARE_PAYMENT_SIMPLE_SIZE); \ + _06_08_ENCODE_DROPS_FEE (fee_ptr, fee ); \ + } + + #define PREPARE_ACCOUNT_SET_SIZE 207U + #define PREPARE_ACCOUNT_SET(buf_out_master)\ + {\ + uint8_t* buf_out = buf_out_master;\ + uint8_t acc[20];\ + uint32_t cls = (uint32_t)ledger_seq();\ + hook_account(SBUF(acc));\ + _01_02_ENCODE_TT (buf_out, ttACCOUNT_SET ); /* uint16 | size 3 */ \ + _02_02_ENCODE_FLAGS (buf_out, tfCANONICAL ); /* uint32 | size 5 */ \ + _02_04_ENCODE_SEQUENCE (buf_out, 0 ); /* uint32 | size 5 */ \ + _02_26_ENCODE_FLS (buf_out, cls + 1 ); /* uint32 | size 6 */ \ + _02_27_ENCODE_LLS (buf_out, cls + 5 ); /* uint32 | size 6 */ \ + uint8_t* fee_ptr = buf_out;\ + _06_08_ENCODE_DROPS_FEE (buf_out, 0 ); /* amount | size 9 */ \ + _07_03_ENCODE_SIGNING_PUBKEY_NULL (buf_out ); /* pk | size 35 */ \ + _08_01_ENCODE_ACCOUNT_SRC (buf_out, acc ); /* account | size 22 */ \ + int64_t edlen = etxn_details((uint32_t)buf_out, PREPARE_ACCOUNT_SET_SIZE); /* emitdet | size 116 */ \ + int64_t fee = etxn_fee_base(buf_out_master, PREPARE_ACCOUNT_SET_SIZE); \ + _06_08_ENCODE_DROPS_FEE (fee_ptr, fee ); \ + } + + #define PREPARE_HOOK_SET_SIZE 211U + #define PREPARE_HOOK_SET(buf_out_master)\ + {\ + uint8_t* buf_out = buf_out_master;\ + uint8_t acc[20];\ + uint32_t cls = (uint32_t)ledger_seq();\ + hook_account(SBUF(acc));\ + _01_02_ENCODE_TT (buf_out, ttHOOK_SET ); /* uint16 | size 3 */ \ + _02_02_ENCODE_FLAGS (buf_out, tfCANONICAL ); /* uint32 | size 5 */ \ + _02_04_ENCODE_SEQUENCE (buf_out, 0 ); /* uint32 | size 5 */ \ + _02_26_ENCODE_FLS (buf_out, cls + 1 ); /* uint32 | size 6 */ \ + _02_27_ENCODE_LLS (buf_out, cls + 5 ); /* uint32 | size 6 */ \ + buf_out[0] = 0xFBU;\ + buf_out[1] = 0xEEU;\ + buf_out[2] = 0xE1U;\ + buf_out[3] = 0xF1U;\ + buf_out += 4;\ + uint8_t* fee_ptr = buf_out;\ + _06_08_ENCODE_DROPS_FEE (buf_out, 0 ); /* amount | size 9 */ \ + _07_03_ENCODE_SIGNING_PUBKEY_NULL (buf_out ); /* pk | size 35 */ \ + _08_01_ENCODE_ACCOUNT_SRC (buf_out, acc ); /* account | size 22 */ \ + int64_t edlen = etxn_details((uint32_t)buf_out, PREPARE_HOOK_SET_SIZE); /* emitdet | size 116 */ \ + int64_t fee = etxn_fee_base(buf_out_master, PREPARE_HOOK_SET_SIZE); \ + _06_08_ENCODE_DROPS_FEE (fee_ptr, fee ); \ + } + + int64_t hook(uint32_t reserved) + { + _g(1,1); + etxn_reserve(3); + + int8_t otxn_acc[20]; + ASSERT(otxn_field(SBUF(otxn_acc), sfAccount) == 20); + + uint8_t payment_tx[PREPARE_PAYMENT_SIMPLE_SIZE]; + PREPARE_PAYMENT_SIMPLE(payment_tx, 1000, otxn_acc, 0, 0); + + uint8_t account_set_tx[PREPARE_ACCOUNT_SET_SIZE]; + PREPARE_ACCOUNT_SET(account_set_tx); + + uint8_t hook_set_tx[PREPARE_HOOK_SET_SIZE]; + PREPARE_HOOK_SET(hook_set_tx); + + uint8_t hash[32]; + if (hook_pos() == 0) { + // default (hookcanemit not set) + ASSERT(emit(SBUF(hash), SBUF(payment_tx)) == 32); + ASSERT(emit(SBUF(hash), SBUF(account_set_tx)) == 32); + ASSERT(emit(SBUF(hash), SBUF(hook_set_tx)) == 32); + return accept(0, 0, hook_pos()); + } + if (hook_pos() == 1) { + // hookcanemit all low + ASSERT(emit(SBUF(hash), SBUF(payment_tx)) == 32); + ASSERT(emit(SBUF(hash), SBUF(account_set_tx)) == 32); + ASSERT(emit(SBUF(hash), SBUF(hook_set_tx)) == EMISSION_FAILURE); + return accept(0, 0, hook_pos()); + } + if (hook_pos() == 2) { + // hookcanemit all high + ASSERT(emit(SBUF(hash), SBUF(payment_tx)) == EMISSION_FAILURE); + ASSERT(emit(SBUF(hash), SBUF(account_set_tx)) == EMISSION_FAILURE); + ASSERT(emit(SBUF(hash), SBUF(hook_set_tx)) == 32); + return accept(0, 0, hook_pos()); + } + } + )[test.hook]"]; + + bool const hasFeature = + env.current()->rules().enabled(featureHookCanEmit); + + Json::Value jv; + jv[jss::CreateCode] = ""; + jv[jss::Flags] = hsfOVERRIDE; + + auto const deleteHook = [&env](Account const& account) { + Json::Value jv; + jv[jss::Account] = account.human(); + jv[jss::TransactionType] = jss::SetHook; + jv[jss::Flags] = 0; + jv[jss::Hooks] = Json::Value{Json::arrayValue}; + Json::Value iv; + iv[jss::CreateCode] = ""; + iv[jss::Flags] = hsfOVERRIDE; + jv[jss::Hooks][0U][jss::Hook] = iv; + jv[jss::Hooks][1U][jss::Hook] = iv; + jv[jss::Hooks][2U][jss::Hook] = iv; + + env(jv, M("hook DELETE"), HSFEE); + env.close(); + }; + + std::array accounts{{alice, bob, charlie}}; + for (int i = 0; i < 2; i++) + { + auto const acc = accounts[i]; + // i=0: Create + // i=1: Install + // i=2: Update + + if (i == 1) + { + Json::Value h = hso(hook, overrideFlag); + env(ripple::test::jtx::hook(hookacc, {{h}}, 0), + M("set hookcanemit"), + HSFEE); + env.close(); + } + else if (i == 2) + { + Json::Value h = hso(hook, overrideFlag); + env(ripple::test::jtx::hook(acc, {{h}}, 0), + M("set hookcanemit"), + HSFEE); + env.close(); + } + + { + Json::Value h = hso(hook, overrideFlag); + env(ripple::test::jtx::hook(acc, {{h}}, 0), + M("set hookcanemit"), + HSFEE); + env.close(); + + // invoke the hook + env(pay(caller, acc, XRP(1)), + M("test hookcanemit 1"), + fee(XRP(1))); + env.close(); + + auto meta = env.meta(); + + // ensure hook execution occured + BEAST_REQUIRE(meta); + BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions)); + + // ensure there was four hook executions + auto const hookExecutions = + meta->getFieldArray(sfHookExecutions); + BEAST_REQUIRE(hookExecutions.size() == 1); + + // get the data in the return code of the execution + BEAST_EXPECT( + hookExecutions[0].getFieldU64(sfHookReturnCode) == 0); + if (i == 0 || i == 1) + deleteHook(acc); + } + + { + // same result with no-HookCanEmit + Json::Value h = hso(hook, overrideFlag); + h[jss::HookCanEmit] = + "0000000000000000000000000000000000000000000000000000000000" + "400000"; + env(ripple::test::jtx::hook(acc, {{h}}, 0), + M("set hookcanemit"), + HSFEE, + hasFeature ? ter(tesSUCCESS) : ter(temDISABLED)); + env.close(); + + if (hasFeature) + { + // invoke the hook + env(pay(caller, acc, XRP(1)), + M("test hookcanemit 1"), + fee(XRP(1))); + env.close(); + + auto meta = env.meta(); + + // ensure hook execution occured + BEAST_REQUIRE(meta); + BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions)); + + // ensure there was four hook executions + auto const hookExecutions = + meta->getFieldArray(sfHookExecutions); + BEAST_REQUIRE(hookExecutions.size() == 1); + + // get the data in the return code of the execution + BEAST_EXPECT( + hookExecutions[0].getFieldU64(sfHookReturnCode) == 0); + if (i == 0 || i == 1) + deleteHook(acc); + } + } + + { + // install the hook on acc + Json::Value hookCanEmitHook = hso(hook, overrideFlag); + hookCanEmitHook[jss::HookCanEmit] = + "00000000000000000000000000000000000000000000000000" + "00000000000000"; + env(ripple::test::jtx::hook(acc, {{jv, hookCanEmitHook}}, 0), + M("test hookcanemit"), + HSFEE, + hasFeature ? ter(tesSUCCESS) : ter(temDISABLED)); + env.close(); + + if (!hasFeature) + continue; + + // invoke the hook + env(pay(caller, acc, XRP(1)), + M("test hookcanemit 2"), + fee(XRP(1))); + env.close(); + + auto meta = env.meta(); + + // ensure hook execution occured + BEAST_REQUIRE(meta); + BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions)); + + // ensure there was four hook executions + auto const hookExecutions = + meta->getFieldArray(sfHookExecutions); + BEAST_REQUIRE(hookExecutions.size() == 1); + + // get the data in the return code of the execution + BEAST_EXPECT( + hookExecutions[0].getFieldU64(sfHookReturnCode) == 1); + if (i == 0 || i == 1) + deleteHook(acc); + } + + { + // install the hook on acc + Json::Value hookCanEmitHook = hso(hook, overrideFlag); + hookCanEmitHook[jss::HookCanEmit] = + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFF"; + env(ripple::test::jtx::hook( + acc, {{jv, jv, hookCanEmitHook}}, 0), + M("test hookcanemit 3"), + HSFEE); + env.close(); + + // invoke the hook + env(pay(caller, acc, XRP(1)), + M("test hookcanemit 3"), + fee(XRP(1))); + env.close(); + + auto meta = env.meta(); + + // ensure hook execution occured + BEAST_REQUIRE(meta); + BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions)); + + // ensure there was four hook executions + auto const hookExecutions = + meta->getFieldArray(sfHookExecutions); + BEAST_REQUIRE(hookExecutions.size() == 1); + + // get the data in the return code of the execution + BEAST_EXPECT( + hookExecutions[0].getFieldU64(sfHookReturnCode) == 2); + if (i == 0 || i == 1) + deleteHook(acc); + } + } + } + void testWithFeatures(FeatureBitset features) { @@ -12128,6 +12741,7 @@ public: testInferHookSetOperation(); testParams(features); testGrants(features); + testHookCanEmit(features); testDelete(features); testInstall(features); @@ -12245,6 +12859,9 @@ public: testWithFeatures(sa - fixXahauV1 - fixXahauV2 - fixNSDelete); testWithFeatures( sa - fixXahauV1 - fixXahauV2 - fixNSDelete - fixPageCap); + testWithFeatures( + sa - fixXahauV1 - fixXahauV2 - fixNSDelete - fixPageCap - + featureHookCanEmit); } private: diff --git a/src/test/app/SetHook_wasm.h b/src/test/app/SetHook_wasm.h index 68bb253ba..a7e75c827 100644 --- a/src/test/app/SetHook_wasm.h +++ b/src/test/app/SetHook_wasm.h @@ -24806,6 +24806,593 @@ std::map> wasm = { }}, /* ==== WASM: 84 ==== */ + {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); + extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code); + extern int64_t emit (uint32_t, uint32_t, uint32_t, uint32_t); + extern int64_t etxn_reserve(uint32_t); + extern int64_t hook_account(uint32_t, uint32_t); + extern int64_t otxn_field(uint32_t, uint32_t, uint32_t); + extern int64_t hook_pos(void); + #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) + #define OUT_OF_BOUNDS (-1) + #define ttPAYMENT 0 + #define ttACCOUNT_SET 3 + #define ttHOOK_SET 22 + #define tfCANONICAL 0x80000000UL + #define amAMOUNT 1U + #define amFEE 8U + #define atACCOUNT 1U + #define DOESNT_EXIST (-5) + #define atDESTINATION 3U + #define SBUF(x) (uint32_t)x,sizeof(x) + #define sfAccount ((8U << 16U) + 1U) + #define EMISSION_FAILURE -11 + + #define ASSERT(x)\ + if (!(x))\ + rollback((uint32_t)#x, sizeof(#x), __LINE__); + + #define PREREQUISITE_NOT_MET -9 + #define ENCODE_DROPS_SIZE 9 + #define ENCODE_DROPS(buf_out, drops, amount_type ) \ + {\ + uint8_t uat = amount_type; \ + uint64_t udrops = drops; \ + buf_out[0] = 0x60U +(uat & 0x0FU ); \ + buf_out[1] = 0b01000000 + (( udrops >> 56 ) & 0b00111111 ); \ + buf_out[2] = (udrops >> 48) & 0xFFU; \ + buf_out[3] = (udrops >> 40) & 0xFFU; \ + buf_out[4] = (udrops >> 32) & 0xFFU; \ + buf_out[5] = (udrops >> 24) & 0xFFU; \ + buf_out[6] = (udrops >> 16) & 0xFFU; \ + buf_out[7] = (udrops >> 8) & 0xFFU; \ + buf_out[8] = (udrops >> 0) & 0xFFU; \ + buf_out += ENCODE_DROPS_SIZE; \ + } + + #define _06_XX_ENCODE_DROPS(buf_out, drops, amount_type )\ + ENCODE_DROPS(buf_out, drops, amount_type ); + + #define ENCODE_DROPS_AMOUNT(buf_out, drops )\ + ENCODE_DROPS(buf_out, drops, amAMOUNT ); + #define _06_01_ENCODE_DROPS_AMOUNT(buf_out, drops )\ + ENCODE_DROPS_AMOUNT(buf_out, drops ); + + #define ENCODE_DROPS_FEE(buf_out, drops )\ + ENCODE_DROPS(buf_out, drops, amFEE ); + #define _06_08_ENCODE_DROPS_FEE(buf_out, drops )\ + ENCODE_DROPS_FEE(buf_out, drops ); + + #define ENCODE_TT_SIZE 3 + #define ENCODE_TT(buf_out, tt )\ + {\ + uint8_t utt = tt;\ + buf_out[0] = 0x12U;\ + buf_out[1] =(utt >> 8 ) & 0xFFU;\ + buf_out[2] =(utt >> 0 ) & 0xFFU;\ + buf_out += ENCODE_TT_SIZE; \ + } + #define _01_02_ENCODE_TT(buf_out, tt)\ + ENCODE_TT(buf_out, tt); + + + #define ENCODE_ACCOUNT_SIZE 22 + #define ENCODE_ACCOUNT(buf_out, account_id, account_type)\ + {\ + uint8_t uat = account_type;\ + buf_out[0] = 0x80U + uat;\ + buf_out[1] = 0x14U;\ + *(uint64_t*)(buf_out + 2) = *(uint64_t*)(account_id + 0);\ + *(uint64_t*)(buf_out + 10) = *(uint64_t*)(account_id + 8);\ + *(uint32_t*)(buf_out + 18) = *(uint32_t*)(account_id + 16);\ + buf_out += ENCODE_ACCOUNT_SIZE;\ + } + #define _08_XX_ENCODE_ACCOUNT(buf_out, account_id, account_type)\ + ENCODE_ACCOUNT(buf_out, account_id, account_type); + + #define ENCODE_ACCOUNT_SRC_SIZE 22 + #define ENCODE_ACCOUNT_SRC(buf_out, account_id)\ + ENCODE_ACCOUNT(buf_out, account_id, atACCOUNT); + #define _08_01_ENCODE_ACCOUNT_SRC(buf_out, account_id)\ + ENCODE_ACCOUNT_SRC(buf_out, account_id); + + #define ENCODE_ACCOUNT_DST_SIZE 22 + #define ENCODE_ACCOUNT_DST(buf_out, account_id)\ + ENCODE_ACCOUNT(buf_out, account_id, atDESTINATION); + #define _08_03_ENCODE_ACCOUNT_DST(buf_out, account_id)\ + ENCODE_ACCOUNT_DST(buf_out, account_id); + + #define ENCODE_ACCOUNT_OWNER_SIZE 22 + #define ENCODE_ACCOUNT_OWNER(buf_out, account_id) \ + ENCODE_ACCOUNT(buf_out, account_id, atOWNER); + #define _08_02_ENCODE_ACCOUNT_OWNER(buf_out, account_id) \ + ENCODE_ACCOUNT_OWNER(buf_out, account_id); + + #define ENCODE_UINT32_COMMON_SIZE 5U + #define ENCODE_UINT32_COMMON(buf_out, i, field)\ + {\ + uint32_t ui = i; \ + uint8_t uf = field; \ + buf_out[0] = 0x20U +(uf & 0x0FU); \ + buf_out[1] =(ui >> 24 ) & 0xFFU; \ + buf_out[2] =(ui >> 16 ) & 0xFFU; \ + buf_out[3] =(ui >> 8 ) & 0xFFU; \ + buf_out[4] =(ui >> 0 ) & 0xFFU; \ + buf_out += ENCODE_UINT32_COMMON_SIZE; \ + } + #define _02_XX_ENCODE_UINT32_COMMON(buf_out, i, field)\ + ENCODE_UINT32_COMMON(buf_out, i, field)\ + + #define ENCODE_UINT32_UNCOMMON_SIZE 6U + #define ENCODE_UINT32_UNCOMMON(buf_out, i, field)\ + {\ + uint32_t ui = i; \ + uint8_t uf = field; \ + buf_out[0] = 0x20U; \ + buf_out[1] = uf; \ + buf_out[2] =(ui >> 24 ) & 0xFFU; \ + buf_out[3] =(ui >> 16 ) & 0xFFU; \ + buf_out[4] =(ui >> 8 ) & 0xFFU; \ + buf_out[5] =(ui >> 0 ) & 0xFFU; \ + buf_out += ENCODE_UINT32_UNCOMMON_SIZE; \ + } + #define _02_XX_ENCODE_UINT32_UNCOMMON(buf_out, i, field)\ + ENCODE_UINT32_UNCOMMON(buf_out, i, field)\ + + #define ENCODE_LLS_SIZE 6U + #define ENCODE_LLS(buf_out, lls )\ + ENCODE_UINT32_UNCOMMON(buf_out, lls, 0x1B ); + #define _02_27_ENCODE_LLS(buf_out, lls )\ + ENCODE_LLS(buf_out, lls ); + + #define ENCODE_FLS_SIZE 6U + #define ENCODE_FLS(buf_out, fls )\ + ENCODE_UINT32_UNCOMMON(buf_out, fls, 0x1A ); + #define _02_26_ENCODE_FLS(buf_out, fls )\ + ENCODE_FLS(buf_out, fls ); + + #define ENCODE_TAG_SRC_SIZE 5 + #define ENCODE_TAG_SRC(buf_out, tag )\ + ENCODE_UINT32_COMMON(buf_out, tag, 0x3U ); + #define _02_03_ENCODE_TAG_SRC(buf_out, tag )\ + ENCODE_TAG_SRC(buf_out, tag ); + + #define ENCODE_TAG_DST_SIZE 5 + #define ENCODE_TAG_DST(buf_out, tag )\ + ENCODE_UINT32_COMMON(buf_out, tag, 0xEU ); + #define _02_14_ENCODE_TAG_DST(buf_out, tag )\ + ENCODE_TAG_DST(buf_out, tag ); + + #define ENCODE_SEQUENCE_SIZE 5 + #define ENCODE_SEQUENCE(buf_out, sequence )\ + ENCODE_UINT32_COMMON(buf_out, sequence, 0x4U ); + #define _02_04_ENCODE_SEQUENCE(buf_out, sequence )\ + ENCODE_SEQUENCE(buf_out, sequence ); + + #define ENCODE_FLAGS_SIZE 5 + #define ENCODE_FLAGS(buf_out, tag )\ + ENCODE_UINT32_COMMON(buf_out, tag, 0x2U ); + #define _02_02_ENCODE_FLAGS(buf_out, tag )\ + ENCODE_FLAGS(buf_out, tag ); + + #define ENCODE_SIGNING_PUBKEY_SIZE 35 + #define ENCODE_SIGNING_PUBKEY(buf_out, pkey )\ + {\ + buf_out[0] = 0x73U;\ + buf_out[1] = 0x21U;\ + *(uint64_t*)(buf_out + 2) = *(uint64_t*)(pkey + 0);\ + *(uint64_t*)(buf_out + 10) = *(uint64_t*)(pkey + 8);\ + *(uint64_t*)(buf_out + 18) = *(uint64_t*)(pkey + 16);\ + *(uint64_t*)(buf_out + 26) = *(uint64_t*)(pkey + 24);\ + buf[34] = pkey[32];\ + buf_out += ENCODE_SIGNING_PUBKEY_SIZE;\ + } + + #define _07_03_ENCODE_SIGNING_PUBKEY(buf_out, pkey )\ + ENCODE_SIGNING_PUBKEY(buf_out, pkey ); + + #define ENCODE_SIGNING_PUBKEY_NULL_SIZE 35 + #define ENCODE_SIGNING_PUBKEY_NULL(buf_out )\ + {\ + buf_out[0] = 0x73U;\ + buf_out[1] = 0x21U;\ + *(uint64_t*)(buf_out+2) = 0;\ + *(uint64_t*)(buf_out+10) = 0;\ + *(uint64_t*)(buf_out+18) = 0;\ + *(uint64_t*)(buf_out+25) = 0;\ + buf_out += ENCODE_SIGNING_PUBKEY_NULL_SIZE;\ + } + + #define _07_03_ENCODE_SIGNING_PUBKEY_NULL(buf_out )\ + ENCODE_SIGNING_PUBKEY_NULL(buf_out ); + + extern int64_t etxn_fee_base ( + uint32_t read_ptr, + uint32_t read_len + ); + extern int64_t etxn_details ( + uint32_t write_ptr, + uint32_t write_len + ); + extern int64_t ledger_seq (void); + + #define PREPARE_PAYMENT_SIMPLE_SIZE 248U + #define PREPARE_PAYMENT_SIMPLE(buf_out_master, drops_amount_raw, to_address, dest_tag_raw, src_tag_raw)\ + {\ + uint8_t* buf_out = buf_out_master;\ + uint8_t acc[20];\ + uint64_t drops_amount = (drops_amount_raw);\ + uint32_t dest_tag = (dest_tag_raw);\ + uint32_t src_tag = (src_tag_raw);\ + uint32_t cls = (uint32_t)ledger_seq();\ + hook_account(SBUF(acc));\ + _01_02_ENCODE_TT (buf_out, ttPAYMENT ); /* uint16 | size 3 */ \ + _02_02_ENCODE_FLAGS (buf_out, tfCANONICAL ); /* uint32 | size 5 */ \ + _02_03_ENCODE_TAG_SRC (buf_out, src_tag ); /* uint32 | size 5 */ \ + _02_04_ENCODE_SEQUENCE (buf_out, 0 ); /* uint32 | size 5 */ \ + _02_14_ENCODE_TAG_DST (buf_out, dest_tag ); /* uint32 | size 5 */ \ + _02_26_ENCODE_FLS (buf_out, cls + 1 ); /* uint32 | size 6 */ \ + _02_27_ENCODE_LLS (buf_out, cls + 5 ); /* uint32 | size 6 */ \ + _06_01_ENCODE_DROPS_AMOUNT (buf_out, drops_amount ); /* amount | size 9 */ \ + uint8_t* fee_ptr = buf_out;\ + _06_08_ENCODE_DROPS_FEE (buf_out, 0 ); /* amount | size 9 */ \ + _07_03_ENCODE_SIGNING_PUBKEY_NULL (buf_out ); /* pk | size 35 */ \ + _08_01_ENCODE_ACCOUNT_SRC (buf_out, acc ); /* account | size 22 */ \ + _08_03_ENCODE_ACCOUNT_DST (buf_out, to_address ); /* account | size 22 */ \ + int64_t edlen = etxn_details((uint32_t)buf_out, PREPARE_PAYMENT_SIMPLE_SIZE); /* emitdet | size 116 */ \ + int64_t fee = etxn_fee_base(buf_out_master, PREPARE_PAYMENT_SIMPLE_SIZE); \ + _06_08_ENCODE_DROPS_FEE (fee_ptr, fee ); \ + } + + #define PREPARE_ACCOUNT_SET_SIZE 207U + #define PREPARE_ACCOUNT_SET(buf_out_master)\ + {\ + uint8_t* buf_out = buf_out_master;\ + uint8_t acc[20];\ + uint32_t cls = (uint32_t)ledger_seq();\ + hook_account(SBUF(acc));\ + _01_02_ENCODE_TT (buf_out, ttACCOUNT_SET ); /* uint16 | size 3 */ \ + _02_02_ENCODE_FLAGS (buf_out, tfCANONICAL ); /* uint32 | size 5 */ \ + _02_04_ENCODE_SEQUENCE (buf_out, 0 ); /* uint32 | size 5 */ \ + _02_26_ENCODE_FLS (buf_out, cls + 1 ); /* uint32 | size 6 */ \ + _02_27_ENCODE_LLS (buf_out, cls + 5 ); /* uint32 | size 6 */ \ + uint8_t* fee_ptr = buf_out;\ + _06_08_ENCODE_DROPS_FEE (buf_out, 0 ); /* amount | size 9 */ \ + _07_03_ENCODE_SIGNING_PUBKEY_NULL (buf_out ); /* pk | size 35 */ \ + _08_01_ENCODE_ACCOUNT_SRC (buf_out, acc ); /* account | size 22 */ \ + int64_t edlen = etxn_details((uint32_t)buf_out, PREPARE_ACCOUNT_SET_SIZE); /* emitdet | size 116 */ \ + int64_t fee = etxn_fee_base(buf_out_master, PREPARE_ACCOUNT_SET_SIZE); \ + _06_08_ENCODE_DROPS_FEE (fee_ptr, fee ); \ + } + + #define PREPARE_HOOK_SET_SIZE 211U + #define PREPARE_HOOK_SET(buf_out_master)\ + {\ + uint8_t* buf_out = buf_out_master;\ + uint8_t acc[20];\ + uint32_t cls = (uint32_t)ledger_seq();\ + hook_account(SBUF(acc));\ + _01_02_ENCODE_TT (buf_out, ttHOOK_SET ); /* uint16 | size 3 */ \ + _02_02_ENCODE_FLAGS (buf_out, tfCANONICAL ); /* uint32 | size 5 */ \ + _02_04_ENCODE_SEQUENCE (buf_out, 0 ); /* uint32 | size 5 */ \ + _02_26_ENCODE_FLS (buf_out, cls + 1 ); /* uint32 | size 6 */ \ + _02_27_ENCODE_LLS (buf_out, cls + 5 ); /* uint32 | size 6 */ \ + buf_out[0] = 0xFBU;\ + buf_out[1] = 0xEEU;\ + buf_out[2] = 0xE1U;\ + buf_out[3] = 0xF1U;\ + buf_out += 4;\ + uint8_t* fee_ptr = buf_out;\ + _06_08_ENCODE_DROPS_FEE (buf_out, 0 ); /* amount | size 9 */ \ + _07_03_ENCODE_SIGNING_PUBKEY_NULL (buf_out ); /* pk | size 35 */ \ + _08_01_ENCODE_ACCOUNT_SRC (buf_out, acc ); /* account | size 22 */ \ + int64_t edlen = etxn_details((uint32_t)buf_out, PREPARE_HOOK_SET_SIZE); /* emitdet | size 116 */ \ + int64_t fee = etxn_fee_base(buf_out_master, PREPARE_HOOK_SET_SIZE); \ + _06_08_ENCODE_DROPS_FEE (fee_ptr, fee ); \ + } + + int64_t hook(uint32_t reserved) + { + _g(1,1); + etxn_reserve(3); + + int8_t otxn_acc[20]; + ASSERT(otxn_field(SBUF(otxn_acc), sfAccount) == 20); + + uint8_t payment_tx[PREPARE_PAYMENT_SIMPLE_SIZE]; + PREPARE_PAYMENT_SIMPLE(payment_tx, 1000, otxn_acc, 0, 0); + + uint8_t account_set_tx[PREPARE_ACCOUNT_SET_SIZE]; + PREPARE_ACCOUNT_SET(account_set_tx); + + uint8_t hook_set_tx[PREPARE_HOOK_SET_SIZE]; + PREPARE_HOOK_SET(hook_set_tx); + + uint8_t hash[32]; + if (hook_pos() == 0) { + // default (hookcanemit not set) + ASSERT(emit(SBUF(hash), SBUF(payment_tx)) == 32); + ASSERT(emit(SBUF(hash), SBUF(account_set_tx)) == 32); + ASSERT(emit(SBUF(hash), SBUF(hook_set_tx)) == 32); + return accept(0, 0, hook_pos()); + } + if (hook_pos() == 1) { + // hookcanemit all low + ASSERT(emit(SBUF(hash), SBUF(payment_tx)) == 32); + ASSERT(emit(SBUF(hash), SBUF(account_set_tx)) == 32); + ASSERT(emit(SBUF(hash), SBUF(hook_set_tx)) == EMISSION_FAILURE); + return accept(0, 0, hook_pos()); + } + if (hook_pos() == 2) { + // hookcanemit all high + ASSERT(emit(SBUF(hash), SBUF(payment_tx)) == EMISSION_FAILURE); + ASSERT(emit(SBUF(hash), SBUF(account_set_tx)) == EMISSION_FAILURE); + ASSERT(emit(SBUF(hash), SBUF(hook_set_tx)) == 32); + return accept(0, 0, hook_pos()); + } + } + )[test.hook]", + { + 0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U, 0x2CU, + 0x07U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x01U, 0x7FU, + 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, + 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU, 0x60U, 0x00U, 0x01U, 0x7EU, + 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x04U, 0x7FU, 0x7FU, + 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x02U, 0xAFU, 0x01U, 0x0BU, 0x03U, 0x65U, + 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, + 0x76U, 0x0CU, 0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x72U, 0x65U, 0x73U, + 0x65U, 0x72U, 0x76U, 0x65U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, + 0x0AU, 0x6FU, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x66U, 0x69U, 0x65U, 0x6CU, + 0x64U, 0x00U, 0x02U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x08U, 0x72U, 0x6FU, + 0x6CU, 0x6CU, 0x62U, 0x61U, 0x63U, 0x6BU, 0x00U, 0x03U, 0x03U, 0x65U, + 0x6EU, 0x76U, 0x0AU, 0x6CU, 0x65U, 0x64U, 0x67U, 0x65U, 0x72U, 0x5FU, + 0x73U, 0x65U, 0x71U, 0x00U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x0CU, + 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x61U, 0x63U, 0x63U, 0x6FU, 0x75U, + 0x6EU, 0x74U, 0x00U, 0x05U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x0CU, 0x65U, + 0x74U, 0x78U, 0x6EU, 0x5FU, 0x64U, 0x65U, 0x74U, 0x61U, 0x69U, 0x6CU, + 0x73U, 0x00U, 0x05U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x0DU, 0x65U, 0x74U, + 0x78U, 0x6EU, 0x5FU, 0x66U, 0x65U, 0x65U, 0x5FU, 0x62U, 0x61U, 0x73U, + 0x65U, 0x00U, 0x05U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x08U, 0x68U, 0x6FU, + 0x6FU, 0x6BU, 0x5FU, 0x70U, 0x6FU, 0x73U, 0x00U, 0x04U, 0x03U, 0x65U, + 0x6EU, 0x76U, 0x04U, 0x65U, 0x6DU, 0x69U, 0x74U, 0x00U, 0x06U, 0x03U, + 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U, + 0x00U, 0x03U, 0x03U, 0x02U, 0x01U, 0x01U, 0x05U, 0x03U, 0x01U, 0x00U, + 0x02U, 0x06U, 0x21U, 0x05U, 0x7FU, 0x01U, 0x41U, 0xE0U, 0x8AU, 0x04U, + 0x0BU, 0x7FU, 0x00U, 0x41U, 0xD6U, 0x0AU, 0x0BU, 0x7FU, 0x00U, 0x41U, + 0x80U, 0x08U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0xE0U, 0x8AU, 0x04U, 0x0BU, + 0x7FU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0x07U, 0x08U, 0x01U, 0x04U, + 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x0BU, 0x0AU, 0xE9U, 0x8EU, 0x00U, + 0x01U, 0xE5U, 0x8EU, 0x00U, 0x03U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U, + 0x7FU, 0x23U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x41U, 0xF0U, 0x05U, + 0x6BU, 0x22U, 0x01U, 0x24U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x41U, + 0x01U, 0x41U, 0x01U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x41U, 0x03U, 0x10U, 0x81U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x02U, + 0x40U, 0x20U, 0x01U, 0x41U, 0xD0U, 0x05U, 0x6AU, 0x41U, 0x14U, 0x41U, + 0x81U, 0x80U, 0x20U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, + 0x14U, 0x51U, 0x0DU, 0x00U, 0x41U, 0x80U, 0x88U, 0x80U, 0x80U, 0x00U, + 0x41U, 0x2CU, 0x42U, 0xA7U, 0x02U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x0BU, 0x10U, 0x84U, 0x80U, 0x80U, 0x80U, 0x00U, 0x21U, + 0x02U, 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU, 0x41U, 0x14U, 0x10U, 0x85U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0xE1U, 0x80U, + 0x01U, 0x3BU, 0x00U, 0xF3U, 0x03U, 0x20U, 0x01U, 0x41U, 0xA0U, 0x36U, + 0x3BU, 0x00U, 0xEDU, 0x03U, 0x20U, 0x01U, 0x41U, 0xA0U, 0x34U, 0x3BU, + 0x00U, 0xE7U, 0x03U, 0x20U, 0x01U, 0x41U, 0x00U, 0x36U, 0x00U, 0xE3U, + 0x03U, 0x20U, 0x01U, 0x41U, 0x2EU, 0x3AU, 0x00U, 0xE2U, 0x03U, 0x20U, + 0x01U, 0x41U, 0x00U, 0x36U, 0x01U, 0xDEU, 0x03U, 0x20U, 0x01U, 0x41U, + 0x24U, 0x3AU, 0x00U, 0xDDU, 0x03U, 0x20U, 0x01U, 0x41U, 0x00U, 0x36U, + 0x00U, 0xD9U, 0x03U, 0x20U, 0x01U, 0x41U, 0x23U, 0x3AU, 0x00U, 0xD8U, + 0x03U, 0x20U, 0x01U, 0x42U, 0x92U, 0x80U, 0x80U, 0x90U, 0x82U, 0x10U, + 0x37U, 0x03U, 0xD0U, 0x03U, 0x20U, 0x01U, 0x20U, 0x02U, 0xA7U, 0x22U, + 0x03U, 0x41U, 0x05U, 0x6AU, 0x22U, 0x04U, 0x3AU, 0x00U, 0xF2U, 0x03U, + 0x20U, 0x01U, 0x20U, 0x04U, 0x41U, 0x08U, 0x76U, 0x3AU, 0x00U, 0xF1U, + 0x03U, 0x20U, 0x01U, 0x20U, 0x04U, 0x41U, 0x10U, 0x76U, 0x3AU, 0x00U, + 0xF0U, 0x03U, 0x20U, 0x01U, 0x20U, 0x04U, 0x41U, 0x18U, 0x76U, 0x3AU, + 0x00U, 0xEFU, 0x03U, 0x20U, 0x01U, 0x20U, 0x03U, 0x41U, 0x01U, 0x6AU, + 0x22U, 0x04U, 0x3AU, 0x00U, 0xECU, 0x03U, 0x20U, 0x01U, 0x20U, 0x04U, + 0x41U, 0x08U, 0x76U, 0x3AU, 0x00U, 0xEBU, 0x03U, 0x20U, 0x01U, 0x20U, + 0x04U, 0x41U, 0x10U, 0x76U, 0x3AU, 0x00U, 0xEAU, 0x03U, 0x20U, 0x01U, + 0x20U, 0x04U, 0x41U, 0x18U, 0x76U, 0x3AU, 0x00U, 0xE9U, 0x03U, 0x20U, + 0x01U, 0x41U, 0xF9U, 0x03U, 0x6AU, 0x41U, 0x00U, 0x3AU, 0x00U, 0x00U, + 0x20U, 0x01U, 0x41U, 0xD0U, 0x03U, 0x6AU, 0x41U, 0x3FU, 0x6AU, 0x42U, + 0x00U, 0x37U, 0x03U, 0x00U, 0x20U, 0x01U, 0x41U, 0x97U, 0x04U, 0x6AU, + 0x42U, 0x00U, 0x37U, 0x03U, 0x00U, 0x20U, 0x01U, 0x41U, 0x9EU, 0x04U, + 0x6AU, 0x42U, 0x00U, 0x37U, 0x00U, 0x00U, 0x20U, 0x01U, 0x41U, 0x00U, + 0x36U, 0x00U, 0xF5U, 0x03U, 0x20U, 0x01U, 0x42U, 0x83U, 0xD0U, 0xA3U, + 0x83U, 0x04U, 0x37U, 0x01U, 0xFAU, 0x03U, 0x20U, 0x01U, 0x41U, 0x00U, + 0x36U, 0x00U, 0x81U, 0x04U, 0x20U, 0x01U, 0x41U, 0xF3U, 0xC2U, 0x00U, + 0x3BU, 0x00U, 0x85U, 0x04U, 0x20U, 0x01U, 0x42U, 0x00U, 0x37U, 0x03U, + 0x87U, 0x04U, 0x20U, 0x01U, 0x41U, 0x81U, 0x29U, 0x3BU, 0x01U, 0xA8U, + 0x04U, 0x20U, 0x01U, 0x41U, 0x83U, 0x29U, 0x3BU, 0x01U, 0xBEU, 0x04U, + 0x20U, 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0x20U, 0x37U, 0x03U, 0xAAU, + 0x04U, 0x20U, 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0x28U, 0x37U, 0x03U, + 0xB2U, 0x04U, 0x20U, 0x01U, 0x20U, 0x01U, 0x28U, 0x02U, 0x30U, 0x36U, + 0x02U, 0xBAU, 0x04U, 0x20U, 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0xD0U, + 0x05U, 0x37U, 0x03U, 0xC0U, 0x04U, 0x20U, 0x01U, 0x20U, 0x01U, 0x29U, + 0x03U, 0xD8U, 0x05U, 0x37U, 0x03U, 0xC8U, 0x04U, 0x20U, 0x01U, 0x20U, + 0x01U, 0x28U, 0x02U, 0xE0U, 0x05U, 0x36U, 0x02U, 0xD0U, 0x04U, 0x20U, + 0x01U, 0x41U, 0xD4U, 0x04U, 0x6AU, 0x41U, 0xF8U, 0x01U, 0x10U, 0x86U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x20U, 0x01U, 0x41U, + 0xD0U, 0x03U, 0x6AU, 0x41U, 0xF8U, 0x01U, 0x10U, 0x87U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x22U, 0x02U, 0x3CU, 0x00U, 0x84U, 0x04U, 0x20U, 0x01U, + 0x41U, 0xE8U, 0x00U, 0x3AU, 0x00U, 0xFCU, 0x03U, 0x20U, 0x01U, 0x20U, + 0x02U, 0x42U, 0x08U, 0x88U, 0x3CU, 0x00U, 0x83U, 0x04U, 0x20U, 0x01U, + 0x20U, 0x02U, 0x42U, 0x10U, 0x88U, 0x3CU, 0x00U, 0x82U, 0x04U, 0x20U, + 0x01U, 0x20U, 0x02U, 0x42U, 0x18U, 0x88U, 0x3CU, 0x00U, 0x81U, 0x04U, + 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x20U, 0x88U, 0x3CU, 0x00U, 0x80U, + 0x04U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x28U, 0x88U, 0x3CU, 0x00U, + 0xFFU, 0x03U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x30U, 0x88U, 0x3CU, + 0x00U, 0xFEU, 0x03U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x38U, 0x88U, + 0xA7U, 0x41U, 0x3FU, 0x71U, 0x41U, 0xC0U, 0x00U, 0x72U, 0x3AU, 0x00U, + 0xFDU, 0x03U, 0x10U, 0x84U, 0x80U, 0x80U, 0x80U, 0x00U, 0x21U, 0x02U, + 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU, 0x41U, 0x14U, 0x10U, 0x85U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, 0xBBU, 0x02U, 0x6AU, + 0x42U, 0x00U, 0x37U, 0x00U, 0x00U, 0x20U, 0x01U, 0x41U, 0xB4U, 0x02U, + 0x6AU, 0x42U, 0x00U, 0x37U, 0x03U, 0x00U, 0x20U, 0x01U, 0x41U, 0xACU, + 0x02U, 0x6AU, 0x42U, 0x00U, 0x37U, 0x03U, 0x00U, 0x20U, 0x01U, 0x41U, + 0xE8U, 0x80U, 0x01U, 0x3BU, 0x00U, 0x99U, 0x02U, 0x20U, 0x01U, 0x41U, + 0xA0U, 0x36U, 0x3BU, 0x00U, 0x93U, 0x02U, 0x20U, 0x01U, 0x41U, 0xA0U, + 0x34U, 0x3BU, 0x00U, 0x8DU, 0x02U, 0x20U, 0x01U, 0x41U, 0x00U, 0x36U, + 0x00U, 0x89U, 0x02U, 0x20U, 0x01U, 0x41U, 0x24U, 0x3AU, 0x00U, 0x88U, + 0x02U, 0x20U, 0x01U, 0x42U, 0x92U, 0x80U, 0x8CU, 0x90U, 0x82U, 0x10U, + 0x37U, 0x03U, 0x80U, 0x02U, 0x20U, 0x01U, 0x41U, 0x00U, 0x36U, 0x00U, + 0x9EU, 0x02U, 0x20U, 0x01U, 0x41U, 0x00U, 0x36U, 0x00U, 0x9BU, 0x02U, + 0x20U, 0x01U, 0x41U, 0xF3U, 0xC2U, 0x00U, 0x3BU, 0x01U, 0xA2U, 0x02U, + 0x20U, 0x01U, 0x42U, 0x00U, 0x37U, 0x03U, 0xA4U, 0x02U, 0x20U, 0x01U, + 0x20U, 0x02U, 0xA7U, 0x22U, 0x03U, 0x41U, 0x05U, 0x6AU, 0x22U, 0x04U, + 0x3AU, 0x00U, 0x98U, 0x02U, 0x20U, 0x01U, 0x20U, 0x04U, 0x41U, 0x08U, + 0x76U, 0x3AU, 0x00U, 0x97U, 0x02U, 0x20U, 0x01U, 0x20U, 0x04U, 0x41U, + 0x10U, 0x76U, 0x3AU, 0x00U, 0x96U, 0x02U, 0x20U, 0x01U, 0x20U, 0x04U, + 0x41U, 0x18U, 0x76U, 0x3AU, 0x00U, 0x95U, 0x02U, 0x20U, 0x01U, 0x20U, + 0x03U, 0x41U, 0x01U, 0x6AU, 0x22U, 0x04U, 0x3AU, 0x00U, 0x92U, 0x02U, + 0x20U, 0x01U, 0x20U, 0x04U, 0x41U, 0x08U, 0x76U, 0x3AU, 0x00U, 0x91U, + 0x02U, 0x20U, 0x01U, 0x20U, 0x04U, 0x41U, 0x10U, 0x76U, 0x3AU, 0x00U, + 0x90U, 0x02U, 0x20U, 0x01U, 0x20U, 0x04U, 0x41U, 0x18U, 0x76U, 0x3AU, + 0x00U, 0x8FU, 0x02U, 0x20U, 0x01U, 0x41U, 0x81U, 0x29U, 0x3BU, 0x00U, + 0xC5U, 0x02U, 0x20U, 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0x20U, 0x37U, + 0x03U, 0xC7U, 0x02U, 0x20U, 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0x28U, + 0x37U, 0x03U, 0xCFU, 0x02U, 0x20U, 0x01U, 0x20U, 0x01U, 0x28U, 0x02U, + 0x30U, 0x36U, 0x02U, 0xD7U, 0x02U, 0x20U, 0x01U, 0x41U, 0xDBU, 0x02U, + 0x6AU, 0x41U, 0xCFU, 0x01U, 0x10U, 0x86U, 0x80U, 0x80U, 0x80U, 0x00U, + 0x1AU, 0x20U, 0x01U, 0x20U, 0x01U, 0x41U, 0x80U, 0x02U, 0x6AU, 0x41U, + 0xCFU, 0x01U, 0x10U, 0x87U, 0x80U, 0x80U, 0x80U, 0x00U, 0x22U, 0x02U, + 0x3CU, 0x00U, 0xA1U, 0x02U, 0x20U, 0x01U, 0x41U, 0xE8U, 0x00U, 0x3AU, + 0x00U, 0x99U, 0x02U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x08U, 0x88U, + 0x3CU, 0x00U, 0xA0U, 0x02U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x10U, + 0x88U, 0x3CU, 0x00U, 0x9FU, 0x02U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, + 0x18U, 0x88U, 0x3CU, 0x00U, 0x9EU, 0x02U, 0x20U, 0x01U, 0x20U, 0x02U, + 0x42U, 0x20U, 0x88U, 0x3CU, 0x00U, 0x9DU, 0x02U, 0x20U, 0x01U, 0x20U, + 0x02U, 0x42U, 0x28U, 0x88U, 0x3CU, 0x00U, 0x9CU, 0x02U, 0x20U, 0x01U, + 0x20U, 0x02U, 0x42U, 0x30U, 0x88U, 0x3CU, 0x00U, 0x9BU, 0x02U, 0x20U, + 0x01U, 0x20U, 0x02U, 0x42U, 0x38U, 0x88U, 0xA7U, 0x41U, 0x3FU, 0x71U, + 0x41U, 0xC0U, 0x00U, 0x72U, 0x3AU, 0x00U, 0x9AU, 0x02U, 0x10U, 0x84U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x21U, 0x02U, 0x20U, 0x01U, 0x41U, 0x14U, + 0x10U, 0x85U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x41U, + 0xE8U, 0x80U, 0x01U, 0x3BU, 0x00U, 0x3DU, 0x20U, 0x01U, 0x41U, 0xFBU, + 0xDDU, 0x87U, 0x8FU, 0x7FU, 0x36U, 0x00U, 0x39U, 0x20U, 0x01U, 0x41U, + 0xA0U, 0x36U, 0x3BU, 0x00U, 0x33U, 0x20U, 0x01U, 0x41U, 0xA0U, 0x34U, + 0x3BU, 0x00U, 0x2DU, 0x20U, 0x01U, 0x41U, 0x00U, 0x36U, 0x00U, 0x29U, + 0x20U, 0x01U, 0x41U, 0x24U, 0x3AU, 0x00U, 0x28U, 0x20U, 0x01U, 0x42U, + 0x92U, 0x80U, 0xD8U, 0x90U, 0x82U, 0x10U, 0x37U, 0x03U, 0x20U, 0x20U, + 0x01U, 0x41U, 0x00U, 0x36U, 0x00U, 0x42U, 0x20U, 0x01U, 0x41U, 0x00U, + 0x36U, 0x00U, 0x3FU, 0x20U, 0x01U, 0x41U, 0xF3U, 0xC2U, 0x00U, 0x3BU, + 0x01U, 0x46U, 0x20U, 0x01U, 0x20U, 0x02U, 0xA7U, 0x22U, 0x03U, 0x41U, + 0x05U, 0x6AU, 0x22U, 0x04U, 0x3AU, 0x00U, 0x38U, 0x20U, 0x01U, 0x20U, + 0x04U, 0x41U, 0x08U, 0x76U, 0x3AU, 0x00U, 0x37U, 0x20U, 0x01U, 0x20U, + 0x04U, 0x41U, 0x10U, 0x76U, 0x3AU, 0x00U, 0x36U, 0x20U, 0x01U, 0x20U, + 0x04U, 0x41U, 0x18U, 0x76U, 0x3AU, 0x00U, 0x35U, 0x20U, 0x01U, 0x20U, + 0x03U, 0x41U, 0x01U, 0x6AU, 0x22U, 0x04U, 0x3AU, 0x00U, 0x32U, 0x20U, + 0x01U, 0x20U, 0x04U, 0x41U, 0x08U, 0x76U, 0x3AU, 0x00U, 0x31U, 0x20U, + 0x01U, 0x20U, 0x04U, 0x41U, 0x10U, 0x76U, 0x3AU, 0x00U, 0x30U, 0x20U, + 0x01U, 0x20U, 0x04U, 0x41U, 0x18U, 0x76U, 0x3AU, 0x00U, 0x2FU, 0x20U, + 0x01U, 0x41U, 0x20U, 0x6AU, 0x41U, 0x3FU, 0x6AU, 0x42U, 0x00U, 0x37U, + 0x00U, 0x00U, 0x20U, 0x01U, 0x41U, 0xD8U, 0x00U, 0x6AU, 0x42U, 0x00U, + 0x37U, 0x03U, 0x00U, 0x20U, 0x01U, 0x41U, 0xD0U, 0x00U, 0x6AU, 0x42U, + 0x00U, 0x37U, 0x03U, 0x00U, 0x20U, 0x01U, 0x42U, 0x00U, 0x37U, 0x03U, + 0x48U, 0x20U, 0x01U, 0x41U, 0x81U, 0x29U, 0x3BU, 0x00U, 0x69U, 0x20U, + 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0x00U, 0x37U, 0x03U, 0x6BU, 0x20U, + 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0x08U, 0x37U, 0x03U, 0x73U, 0x20U, + 0x01U, 0x20U, 0x01U, 0x28U, 0x02U, 0x10U, 0x36U, 0x02U, 0x7BU, 0x20U, + 0x01U, 0x41U, 0xFFU, 0x00U, 0x6AU, 0x41U, 0xD3U, 0x01U, 0x10U, 0x86U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x20U, 0x01U, 0x20U, 0x01U, 0x41U, + 0x20U, 0x6AU, 0x41U, 0xD3U, 0x01U, 0x10U, 0x87U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x22U, 0x02U, 0x3CU, 0x00U, 0x45U, 0x20U, 0x01U, 0x41U, 0xE8U, + 0x00U, 0x3AU, 0x00U, 0x3DU, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x08U, + 0x88U, 0x3CU, 0x00U, 0x44U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x10U, + 0x88U, 0x3CU, 0x00U, 0x43U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x18U, + 0x88U, 0x3CU, 0x00U, 0x42U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x20U, + 0x88U, 0x3CU, 0x00U, 0x41U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x28U, + 0x88U, 0x3CU, 0x00U, 0x40U, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x30U, + 0x88U, 0x3CU, 0x00U, 0x3FU, 0x20U, 0x01U, 0x20U, 0x02U, 0x42U, 0x38U, + 0x88U, 0xA7U, 0x41U, 0x3FU, 0x71U, 0x41U, 0xC0U, 0x00U, 0x72U, 0x3AU, + 0x00U, 0x3EU, 0x02U, 0x40U, 0x02U, 0x40U, 0x02U, 0x40U, 0x10U, 0x88U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x00U, 0x52U, 0x0DU, 0x00U, 0x02U, + 0x40U, 0x20U, 0x01U, 0x41U, 0x20U, 0x20U, 0x01U, 0x41U, 0xD0U, 0x03U, + 0x6AU, 0x41U, 0xF8U, 0x01U, 0x10U, 0x89U, 0x80U, 0x80U, 0x80U, 0x00U, + 0x42U, 0x20U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xACU, 0x88U, 0x80U, 0x80U, + 0x00U, 0x41U, 0x29U, 0x42U, 0xB5U, 0x02U, 0x10U, 0x83U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x20U, 0x01U, 0x41U, 0x20U, + 0x20U, 0x01U, 0x41U, 0x80U, 0x02U, 0x6AU, 0x41U, 0xCFU, 0x01U, 0x10U, + 0x89U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x20U, 0x51U, 0x0DU, 0x00U, + 0x41U, 0xD5U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x2DU, 0x42U, 0xB6U, + 0x02U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x20U, + 0x01U, 0x41U, 0x20U, 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU, 0x41U, 0xD3U, + 0x01U, 0x10U, 0x89U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x20U, 0x51U, + 0x0DU, 0x01U, 0x41U, 0x82U, 0x89U, 0x80U, 0x80U, 0x00U, 0x41U, 0x2AU, + 0x42U, 0xB7U, 0x02U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x0CU, 0x01U, 0x0BU, 0x02U, 0x40U, 0x10U, 0x88U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x42U, 0x01U, 0x52U, 0x0DU, 0x00U, 0x02U, 0x40U, 0x20U, 0x01U, + 0x41U, 0x20U, 0x20U, 0x01U, 0x41U, 0xD0U, 0x03U, 0x6AU, 0x41U, 0xF8U, + 0x01U, 0x10U, 0x89U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x20U, 0x51U, + 0x0DU, 0x00U, 0x41U, 0xACU, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x29U, + 0x42U, 0xBCU, 0x02U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x0BU, 0x02U, 0x40U, 0x20U, 0x01U, 0x41U, 0x20U, 0x20U, 0x01U, 0x41U, + 0x80U, 0x02U, 0x6AU, 0x41U, 0xCFU, 0x01U, 0x10U, 0x89U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x42U, 0x20U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xD5U, 0x88U, + 0x80U, 0x80U, 0x00U, 0x41U, 0x2DU, 0x42U, 0xBDU, 0x02U, 0x10U, 0x83U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x20U, 0x01U, 0x41U, 0x20U, + 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU, 0x41U, 0xD3U, 0x01U, 0x10U, 0x89U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x75U, 0x51U, 0x0DU, 0x01U, 0x41U, + 0xACU, 0x89U, 0x80U, 0x80U, 0x00U, 0x41U, 0x38U, 0x42U, 0xBEU, 0x02U, + 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0CU, 0x01U, 0x0BU, + 0x10U, 0x88U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x02U, 0x52U, 0x0DU, + 0x01U, 0x02U, 0x40U, 0x20U, 0x01U, 0x41U, 0x20U, 0x20U, 0x01U, 0x41U, + 0xD0U, 0x03U, 0x6AU, 0x41U, 0xF8U, 0x01U, 0x10U, 0x89U, 0x80U, 0x80U, + 0x80U, 0x00U, 0x42U, 0x75U, 0x51U, 0x0DU, 0x00U, 0x41U, 0xE4U, 0x89U, + 0x80U, 0x80U, 0x00U, 0x41U, 0x37U, 0x42U, 0xC3U, 0x02U, 0x10U, 0x83U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x0BU, 0x02U, 0x40U, 0x20U, 0x01U, + 0x41U, 0x20U, 0x20U, 0x01U, 0x41U, 0x80U, 0x02U, 0x6AU, 0x41U, 0xCFU, + 0x01U, 0x10U, 0x89U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x75U, 0x51U, + 0x0DU, 0x00U, 0x41U, 0x9BU, 0x8AU, 0x80U, 0x80U, 0x00U, 0x41U, 0x3BU, + 0x42U, 0xC4U, 0x02U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, + 0x0BU, 0x20U, 0x01U, 0x41U, 0x20U, 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU, + 0x41U, 0xD3U, 0x01U, 0x10U, 0x89U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, + 0x20U, 0x51U, 0x0DU, 0x00U, 0x41U, 0x82U, 0x89U, 0x80U, 0x80U, 0x00U, + 0x41U, 0x2AU, 0x42U, 0xC5U, 0x02U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, + 0x00U, 0x1AU, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x10U, 0x88U, 0x80U, + 0x80U, 0x80U, 0x00U, 0x10U, 0x8AU, 0x80U, 0x80U, 0x80U, 0x00U, 0x21U, + 0x02U, 0x0BU, 0x20U, 0x01U, 0x41U, 0xF0U, 0x05U, 0x6AU, 0x24U, 0x80U, + 0x80U, 0x80U, 0x80U, 0x00U, 0x20U, 0x02U, 0x0BU, 0x0BU, 0xDEU, 0x02U, + 0x01U, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0xD6U, 0x02U, 0x6FU, 0x74U, + 0x78U, 0x6EU, 0x5FU, 0x66U, 0x69U, 0x65U, 0x6CU, 0x64U, 0x28U, 0x53U, + 0x42U, 0x55U, 0x46U, 0x28U, 0x6FU, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x61U, + 0x63U, 0x63U, 0x29U, 0x2CU, 0x20U, 0x73U, 0x66U, 0x41U, 0x63U, 0x63U, + 0x6FU, 0x75U, 0x6EU, 0x74U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x32U, + 0x30U, 0x00U, 0x65U, 0x6DU, 0x69U, 0x74U, 0x28U, 0x53U, 0x42U, 0x55U, + 0x46U, 0x28U, 0x68U, 0x61U, 0x73U, 0x68U, 0x29U, 0x2CU, 0x20U, 0x53U, + 0x42U, 0x55U, 0x46U, 0x28U, 0x70U, 0x61U, 0x79U, 0x6DU, 0x65U, 0x6EU, + 0x74U, 0x5FU, 0x74U, 0x78U, 0x29U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, + 0x33U, 0x32U, 0x00U, 0x65U, 0x6DU, 0x69U, 0x74U, 0x28U, 0x53U, 0x42U, + 0x55U, 0x46U, 0x28U, 0x68U, 0x61U, 0x73U, 0x68U, 0x29U, 0x2CU, 0x20U, + 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, 0x61U, 0x63U, 0x63U, 0x6FU, 0x75U, + 0x6EU, 0x74U, 0x5FU, 0x73U, 0x65U, 0x74U, 0x5FU, 0x74U, 0x78U, 0x29U, + 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, 0x65U, 0x6DU, + 0x69U, 0x74U, 0x28U, 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, 0x68U, 0x61U, + 0x73U, 0x68U, 0x29U, 0x2CU, 0x20U, 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, + 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x73U, 0x65U, 0x74U, 0x5FU, 0x74U, + 0x78U, 0x29U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x33U, 0x32U, 0x00U, + 0x65U, 0x6DU, 0x69U, 0x74U, 0x28U, 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, + 0x68U, 0x61U, 0x73U, 0x68U, 0x29U, 0x2CU, 0x20U, 0x53U, 0x42U, 0x55U, + 0x46U, 0x28U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x73U, 0x65U, 0x74U, + 0x5FU, 0x74U, 0x78U, 0x29U, 0x29U, 0x20U, 0x3DU, 0x3DU, 0x20U, 0x45U, + 0x4DU, 0x49U, 0x53U, 0x53U, 0x49U, 0x4FU, 0x4EU, 0x5FU, 0x46U, 0x41U, + 0x49U, 0x4CU, 0x55U, 0x52U, 0x45U, 0x00U, 0x65U, 0x6DU, 0x69U, 0x74U, + 0x28U, 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, 0x68U, 0x61U, 0x73U, 0x68U, + 0x29U, 0x2CU, 0x20U, 0x53U, 0x42U, 0x55U, 0x46U, 0x28U, 0x70U, 0x61U, + 0x79U, 0x6DU, 0x65U, 0x6EU, 0x74U, 0x5FU, 0x74U, 0x78U, 0x29U, 0x29U, + 0x20U, 0x3DU, 0x3DU, 0x20U, 0x45U, 0x4DU, 0x49U, 0x53U, 0x53U, 0x49U, + 0x4FU, 0x4EU, 0x5FU, 0x46U, 0x41U, 0x49U, 0x4CU, 0x55U, 0x52U, 0x45U, + 0x00U, 0x65U, 0x6DU, 0x69U, 0x74U, 0x28U, 0x53U, 0x42U, 0x55U, 0x46U, + 0x28U, 0x68U, 0x61U, 0x73U, 0x68U, 0x29U, 0x2CU, 0x20U, 0x53U, 0x42U, + 0x55U, 0x46U, 0x28U, 0x61U, 0x63U, 0x63U, 0x6FU, 0x75U, 0x6EU, 0x74U, + 0x5FU, 0x73U, 0x65U, 0x74U, 0x5FU, 0x74U, 0x78U, 0x29U, 0x29U, 0x20U, + 0x3DU, 0x3DU, 0x20U, 0x45U, 0x4DU, 0x49U, 0x53U, 0x53U, 0x49U, 0x4FU, + 0x4EU, 0x5FU, 0x46U, 0x41U, 0x49U, 0x4CU, 0x55U, 0x52U, 0x45U, 0x00U, + }}, + + /* ==== WASM: 85 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -24834,7 +25421,7 @@ std::map> wasm = { 0x0BU, }}, - /* ==== WASM: 85 ==== */ + /* ==== WASM: 86 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -24866,7 +25453,7 @@ std::map> wasm = { 0x20U, 0x52U, 0x65U, 0x6AU, 0x65U, 0x63U, 0x74U, 0x65U, 0x64U, 0x00U, }}, - /* ==== WASM: 86 ==== */ + /* ==== WASM: 87 ==== */ {R"[test.hook]( (module (type (;0;) (func (param i32 i32 i64) (result i64))) @@ -24893,7 +25480,7 @@ std::map> wasm = { 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x00U, 0x10U, 0x00U, 0x0BU, }}, - /* ==== WASM: 87 ==== */ + /* ==== WASM: 88 ==== */ {R"[test.hook]( (module (type (;0;) (func (param i32 i32) (result i32))) @@ -24946,7 +25533,7 @@ std::map> wasm = { 0x00U, 0x1AU, 0x0BU, }}, - /* ==== WASM: 88 ==== */ + /* ==== WASM: 89 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -31589,7 +32176,7 @@ std::map> wasm = { 0x39U, 0x30U, 0x31U, 0x32U, 0x33U, 0x00U, }}, - /* ==== WASM: 89 ==== */ + /* ==== WASM: 90 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter); @@ -31635,7 +32222,7 @@ std::map> wasm = { 0x0BU, 0x06U, 0x76U, 0x61U, 0x6CU, 0x75U, 0x65U, 0x00U, }}, - /* ==== WASM: 90 ==== */ + /* ==== WASM: 91 ==== */ {R"[test.hook]( #include extern int32_t _g (uint32_t id, uint32_t maxiter);