mirror of
https://github.com/Xahau/xahaud.git
synced 2026-06-02 16:26:37 +00:00
Named Hook (#718)
This commit is contained in:
@@ -221,6 +221,7 @@
|
||||
#define sfProvider ((7U << 16U) + 30U)
|
||||
#define sfMPTokenMetadata ((7U << 16U) + 31U)
|
||||
#define sfCredentialType ((7U << 16U) + 32U)
|
||||
#define sfHookName ((7U << 16U) + 97U)
|
||||
#define sfRemarkValue ((7U << 16U) + 98U)
|
||||
#define sfRemarkName ((7U << 16U) + 99U)
|
||||
#define sfAccount ((8U << 16U) + 1U)
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
// If you add an amendment here, then do not forget to increment `numFeatures`
|
||||
// in include/xrpl/protocol/Feature.h.
|
||||
|
||||
XRPL_FEATURE(NamedHooks, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(IOURewardClaim, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (IOULockedBalanceInvariant, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (ImportIssuer, Supported::yes, VoteBehavior::DefaultYes)
|
||||
|
||||
@@ -293,6 +293,7 @@ TYPED_SFIELD(sfAssetClass, VL, 29)
|
||||
TYPED_SFIELD(sfProvider, VL, 30)
|
||||
TYPED_SFIELD(sfMPTokenMetadata, VL, 31)
|
||||
TYPED_SFIELD(sfCredentialType, VL, 32)
|
||||
TYPED_SFIELD(sfHookName, VL, 97)
|
||||
TYPED_SFIELD(sfRemarkValue, VL, 98)
|
||||
TYPED_SFIELD(sfRemarkName, VL, 99)
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ JSS(Holder); // field.
|
||||
JSS(HookApiVersion); // field
|
||||
JSS(HookCanEmit); // field
|
||||
JSS(HookHash); // field
|
||||
JSS(HookName); // field
|
||||
JSS(HookNamespace); // field
|
||||
JSS(HookOn); // field
|
||||
JSS(HookOnIncoming); // field
|
||||
|
||||
@@ -99,6 +99,7 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
{sfHookOnOutgoing, soeOPTIONAL},
|
||||
{sfHookCanEmit, soeOPTIONAL},
|
||||
{sfHookApiVersion, soeOPTIONAL},
|
||||
{sfHookName, soeOPTIONAL},
|
||||
{sfFlags, soeOPTIONAL}});
|
||||
|
||||
add(sfHookGrant.jsonName,
|
||||
|
||||
@@ -48,6 +48,7 @@ TxFormats::TxFormats()
|
||||
{sfFirstLedgerSequence, soeOPTIONAL},
|
||||
{sfNetworkID, soeOPTIONAL},
|
||||
{sfHookParameters, soeOPTIONAL},
|
||||
{sfHookName, soeOPTIONAL},
|
||||
};
|
||||
|
||||
#pragma push_macro("UNWRAP")
|
||||
|
||||
@@ -690,6 +690,8 @@ public:
|
||||
|
||||
bool const hasHookCanEmit =
|
||||
env.current()->rules().enabled(featureHookCanEmit);
|
||||
bool const hasNamedHooks =
|
||||
env.current()->rules().enabled(featureNamedHooks);
|
||||
|
||||
auto const alice = Account{"alice"};
|
||||
env.fund(XRP(10000), alice);
|
||||
@@ -726,7 +728,8 @@ public:
|
||||
}
|
||||
|
||||
// grants, parameters, hookon, hookonincoming, hookonoutgoing,
|
||||
// hookcanemit, hookapiversion, hooknamespace keys must be absent
|
||||
// hookcanemit, hookapiversion, hooknamespace, hookname keys must be
|
||||
// absent
|
||||
for (auto const& [key, value] : JSSMap{
|
||||
{jss::HookGrants, Json::arrayValue},
|
||||
{jss::HookParameters, Json::arrayValue},
|
||||
@@ -743,11 +746,16 @@ public:
|
||||
"000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000"},
|
||||
{jss::HookApiVersion, "0"},
|
||||
{jss::HookNamespace, to_string(uint256{beast::zero})}})
|
||||
{jss::HookNamespace, to_string(uint256{beast::zero})},
|
||||
{jss::HookName, strHex(std::string{"DEADBEEF"})},
|
||||
})
|
||||
{
|
||||
if (!hasHookCanEmit && key == jss::HookCanEmit)
|
||||
continue;
|
||||
|
||||
if (!hasNamedHooks && key == jss::HookName)
|
||||
continue;
|
||||
|
||||
Json::Value iv;
|
||||
iv[jss::CreateCode] = "";
|
||||
iv[key] = value;
|
||||
@@ -755,7 +763,7 @@ public:
|
||||
env(jv,
|
||||
M("Hook DELETE operation cannot include: grants, params, "
|
||||
"hookon, HookOnIncoming, HookOnOutgoing, hookcanemit, "
|
||||
"apiversion, namespace"),
|
||||
"apiversion, namespace, hookname"),
|
||||
HSFEE,
|
||||
ter(temMALFORMED));
|
||||
env.close();
|
||||
@@ -894,6 +902,8 @@ public:
|
||||
bool const fixNS = env.current()->rules().enabled(fixNSDelete);
|
||||
bool const hasHookCanEmit =
|
||||
env.current()->rules().enabled(featureHookCanEmit);
|
||||
bool const hasNamedHooks =
|
||||
env.current()->rules().enabled(featureNamedHooks);
|
||||
|
||||
auto const alice = Account{"alice"};
|
||||
env.fund(XRP(10000), alice);
|
||||
@@ -926,11 +936,15 @@ public:
|
||||
"000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000"},
|
||||
{jss::HookApiVersion, "0"},
|
||||
{jss::HookName, strHex(std::string{"DEADBEEF"})},
|
||||
})
|
||||
{
|
||||
if (!hasHookCanEmit && key == jss::HookCanEmit)
|
||||
continue;
|
||||
|
||||
if (!hasNamedHooks && key == jss::HookName)
|
||||
continue;
|
||||
|
||||
Json::Value iv;
|
||||
iv[key] = value;
|
||||
iv[jss::Flags] = hsfNSDELETE;
|
||||
@@ -939,7 +953,7 @@ public:
|
||||
env(jv,
|
||||
M("Hook NSDELETE operation cannot include: grants, params, "
|
||||
"hookon, hookonincoming, hookonoutgoing, hookcanemit, "
|
||||
"apiversion"),
|
||||
"apiversion, hookname"),
|
||||
HSFEE,
|
||||
ter(temMALFORMED));
|
||||
env.close();
|
||||
@@ -1684,6 +1698,340 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testHookName(FeatureBitset features)
|
||||
{
|
||||
testcase("Test hook name");
|
||||
using namespace jtx;
|
||||
|
||||
auto const alice = Account{"alice"};
|
||||
auto const bob = Account{"bob"};
|
||||
auto const charlie = Account{"charlie"};
|
||||
auto const USD = alice["USD"];
|
||||
|
||||
Env env{*this, features};
|
||||
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env.close();
|
||||
|
||||
// Invalid hook name (length=3,17, not utf-8)
|
||||
for (auto const name : {
|
||||
"414243", // ABC (length=3)
|
||||
"4142434445464748494A4B4C4D4E4F5051", // ABCDEFGHIJKLMNOPQ
|
||||
// (length=17)
|
||||
"DEADBEEF", // not utf-8
|
||||
})
|
||||
{
|
||||
auto jvh = hso(accept_wasm);
|
||||
jvh[jss::HookName] = name;
|
||||
env(ripple::test::jtx::hook(alice, {{jvh}}, 0),
|
||||
M("Hook name must be between 8 and 32 hex characters and be a "
|
||||
"valid UTF-8 string"),
|
||||
HSFEE,
|
||||
features[featureNamedHooks] ? ter(temMALFORMED)
|
||||
: ter(temDISABLED));
|
||||
|
||||
auto jvi = invoke::invoke(alice);
|
||||
jvi[jss::HookName] = name;
|
||||
env(jvi,
|
||||
M("Call named hook with the invalid hook name"),
|
||||
HSFEE,
|
||||
ter(temMALFORMED));
|
||||
}
|
||||
|
||||
if (!features[featureNamedHooks])
|
||||
return;
|
||||
|
||||
{
|
||||
/// Create, Install, Update named Hook
|
||||
Env env{*this, features};
|
||||
|
||||
env.fund(XRP(10000), alice, bob, charlie);
|
||||
env.close();
|
||||
|
||||
// Create with hook name
|
||||
auto jvh = hso(accept_wasm);
|
||||
jvh[jss::HookName] = "41424344";
|
||||
env(ripple::test::jtx::hook(alice, {{jvh}}, 0),
|
||||
M("Create named hook"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
// Check hook definition
|
||||
{
|
||||
auto const hookDef =
|
||||
env.le(keylet::hookDefinition(accept_hash));
|
||||
BEAST_EXPECT(hookDef);
|
||||
BEAST_EXPECT(!hookDef->isFieldPresent(sfHookName));
|
||||
}
|
||||
// Check Hook
|
||||
{
|
||||
auto const hooks = env.le(keylet::hook(alice));
|
||||
BEAST_EXPECT(hooks && hooks->isFieldPresent(sfHooks));
|
||||
auto const& hooksArray = hooks->getFieldArray(sfHooks);
|
||||
BEAST_EXPECT(hooksArray.size() == 1);
|
||||
BEAST_EXPECT(hooksArray[0].isFieldPresent(sfHookName));
|
||||
BEAST_EXPECT(
|
||||
strHex(hooksArray[0].getFieldVL(sfHookName)) == "41424344");
|
||||
}
|
||||
|
||||
// install with hook name
|
||||
jvh[jss::HookName] = "41424344";
|
||||
env(ripple::test::jtx::hook(bob, {{jvh}}, 0),
|
||||
M("Install named hook"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// Check Hook
|
||||
{
|
||||
auto const hooks = env.le(keylet::hook(bob));
|
||||
BEAST_EXPECT(hooks && hooks->isFieldPresent(sfHooks));
|
||||
auto const& hooksArray = hooks->getFieldArray(sfHooks);
|
||||
BEAST_EXPECT(hooksArray.size() == 1);
|
||||
BEAST_EXPECT(hooksArray[0].isFieldPresent(sfHookName));
|
||||
BEAST_EXPECT(
|
||||
strHex(hooksArray[0].getFieldVL(sfHookName)) == "41424344");
|
||||
}
|
||||
|
||||
// install without hook name
|
||||
jvh.removeMember(jss::HookName);
|
||||
env(ripple::test::jtx::hook(charlie, {{jvh}}, 0),
|
||||
M("Install non-named hook"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// Check Hook
|
||||
{
|
||||
auto const hooks = env.le(keylet::hook(charlie));
|
||||
BEAST_EXPECT(hooks && hooks->isFieldPresent(sfHooks));
|
||||
auto const& hooksArray = hooks->getFieldArray(sfHooks);
|
||||
BEAST_EXPECT(hooksArray.size() == 1);
|
||||
BEAST_EXPECT(!hooksArray[0].isFieldPresent(sfHookName));
|
||||
}
|
||||
|
||||
// Update named hook to non-named hook
|
||||
jvh[jss::HookName] = "";
|
||||
jvh[jss::Flags] = hsfOVERRIDE;
|
||||
env(ripple::test::jtx::hook(alice, {{jvh}}, 0),
|
||||
M("Update named hook to non-named hook"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// Check Hook
|
||||
{
|
||||
auto const hooks = env.le(keylet::hook(alice));
|
||||
BEAST_EXPECT(hooks && hooks->isFieldPresent(sfHooks));
|
||||
auto const& hooksArray = hooks->getFieldArray(sfHooks);
|
||||
BEAST_EXPECT(hooksArray.size() == 1);
|
||||
BEAST_EXPECT(!hooksArray[0].isFieldPresent(sfHookName));
|
||||
}
|
||||
|
||||
// Update named hook to named hook
|
||||
jvh[jss::HookName] = "4142434445";
|
||||
jvh[jss::Flags] = hsfOVERRIDE;
|
||||
env(ripple::test::jtx::hook(bob, {{jvh}}, 0),
|
||||
M("Update non-named hook to named hook"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// Check Hook
|
||||
{
|
||||
auto const hooks = env.le(keylet::hook(bob));
|
||||
BEAST_EXPECT(hooks && hooks->isFieldPresent(sfHooks));
|
||||
auto const& hooksArray = hooks->getFieldArray(sfHooks);
|
||||
BEAST_EXPECT(hooksArray.size() == 1);
|
||||
BEAST_EXPECT(hooksArray[0].isFieldPresent(sfHookName));
|
||||
BEAST_EXPECT(
|
||||
strHex(hooksArray[0].getFieldVL(sfHookName)) ==
|
||||
"4142434445");
|
||||
}
|
||||
|
||||
// Update non-named hook to named hook
|
||||
jvh[jss::HookName] = "41424344";
|
||||
jvh[jss::Flags] = hsfOVERRIDE;
|
||||
env(ripple::test::jtx::hook(charlie, {{jvh}}, 0),
|
||||
M("Update non-named hook to named hook"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// Check Hook
|
||||
{
|
||||
auto const hooks = env.le(keylet::hook(charlie));
|
||||
BEAST_EXPECT(hooks && hooks->isFieldPresent(sfHooks));
|
||||
auto const& hooksArray = hooks->getFieldArray(sfHooks);
|
||||
BEAST_EXPECT(hooksArray.size() == 1);
|
||||
BEAST_EXPECT(hooksArray[0].isFieldPresent(sfHookName));
|
||||
BEAST_EXPECT(
|
||||
strHex(hooksArray[0].getFieldVL(sfHookName)) == "41424344");
|
||||
}
|
||||
}
|
||||
|
||||
// Install named hook
|
||||
auto jvh = hso(accept_wasm);
|
||||
jvh[jss::HookName] = "41424344";
|
||||
jvh[jss::Flags] = hsfCOLLECT;
|
||||
auto jvh2 = hso(accept2_wasm);
|
||||
jvh2[jss::Flags] = hsfCOLLECT;
|
||||
env(ripple::test::jtx::hook(alice, {{jvh, jvh2}}, 0),
|
||||
M("Install named hook"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
//
|
||||
// Test Strong
|
||||
//
|
||||
// Call named hook without specifying the hook name
|
||||
{
|
||||
auto jv = invoke::invoke(alice);
|
||||
auto expectedFee =
|
||||
calculateBaseFee(*env.current(), *env.jt(jv).stx);
|
||||
BEAST_EXPECT(expectedFee == drops(19));
|
||||
env(jv,
|
||||
M("Call named hook without specifying the hook name"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// execute only non-named hook
|
||||
auto const hookExecutions =
|
||||
env.meta()->getFieldArray(sfHookExecutions);
|
||||
BEAST_EXPECT(hookExecutions.size() == 1);
|
||||
BEAST_EXPECT(
|
||||
hookExecutions[0].getFieldH256(sfHookHash) == accept2_hash);
|
||||
}
|
||||
|
||||
// Call named hook with the wrong hook name
|
||||
{
|
||||
auto jv = invoke::invoke(alice);
|
||||
jv[jss::HookName] = "41424345";
|
||||
auto expectedFee =
|
||||
calculateBaseFee(*env.current(), *env.jt(jv).stx);
|
||||
BEAST_EXPECT(expectedFee == drops(19));
|
||||
env(jv, M("Call named hook with the wrong hook name"), HSFEE);
|
||||
env.close();
|
||||
// execute only non-named hook
|
||||
auto const hookExecutions =
|
||||
env.meta()->getFieldArray(sfHookExecutions);
|
||||
BEAST_EXPECT(hookExecutions.size() == 1);
|
||||
BEAST_EXPECT(
|
||||
hookExecutions[0].getFieldH256(sfHookHash) == accept2_hash);
|
||||
}
|
||||
|
||||
// Call named hook with the correct hook name
|
||||
{
|
||||
auto jv = invoke::invoke(alice);
|
||||
jv[jss::HookName] = "41424344";
|
||||
auto expectedFee =
|
||||
calculateBaseFee(*env.current(), *env.jt(jv).stx);
|
||||
BEAST_EXPECT(expectedFee == drops(28));
|
||||
env(jv, M("Call named hook with the correct hook name"), HSFEE);
|
||||
env.close();
|
||||
// execute both named and non-named hooks
|
||||
auto const hookExecutions =
|
||||
env.meta()->getFieldArray(sfHookExecutions);
|
||||
BEAST_EXPECT(hookExecutions.size() == 2);
|
||||
BEAST_EXPECT(
|
||||
hookExecutions[0].getFieldH256(sfHookHash) == accept_hash);
|
||||
BEAST_EXPECT(
|
||||
hookExecutions[1].getFieldH256(sfHookHash) == accept2_hash);
|
||||
}
|
||||
|
||||
//
|
||||
// Test Weak
|
||||
//
|
||||
env(fset(alice, asfTshCollect), fee(XRP(1)));
|
||||
env.close();
|
||||
// Call named hook without specifying the hook name
|
||||
{
|
||||
auto jv = trust(bob, USD(1000));
|
||||
env(jv,
|
||||
M("Call named hook without specifying the hook name"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// execute only non-named hook
|
||||
auto const hookExecutions =
|
||||
env.meta()->getFieldArray(sfHookExecutions);
|
||||
BEAST_EXPECT(hookExecutions.size() == 1);
|
||||
BEAST_EXPECT(
|
||||
hookExecutions[0].getFieldH256(sfHookHash) == accept2_hash);
|
||||
}
|
||||
|
||||
// Call named hook with the wrong hook name
|
||||
{
|
||||
auto jv = trust(bob, USD(1000));
|
||||
jv[jss::HookName] = "41424345";
|
||||
env(jv, M("Call named hook with the wrong hook name"), HSFEE);
|
||||
env.close();
|
||||
// execute only non-named hook
|
||||
auto const hookExecutions =
|
||||
env.meta()->getFieldArray(sfHookExecutions);
|
||||
BEAST_EXPECT(hookExecutions.size() == 1);
|
||||
BEAST_EXPECT(
|
||||
hookExecutions[0].getFieldH256(sfHookHash) == accept2_hash);
|
||||
}
|
||||
|
||||
// Call named hook with the correct hook name
|
||||
{
|
||||
auto jv = invoke::invoke(alice);
|
||||
jv[jss::HookName] = "41424344";
|
||||
env(jv, M("Call named hook with the correct hook name"), HSFEE);
|
||||
env.close();
|
||||
// execute both named and non-named hooks
|
||||
auto const hookExecutions =
|
||||
env.meta()->getFieldArray(sfHookExecutions);
|
||||
BEAST_EXPECT(hookExecutions.size() == 2);
|
||||
BEAST_EXPECT(
|
||||
hookExecutions[0].getFieldH256(sfHookHash) == accept_hash);
|
||||
BEAST_EXPECT(
|
||||
hookExecutions[1].getFieldH256(sfHookHash) == accept2_hash);
|
||||
}
|
||||
env(fclear(alice, asfTshCollect), fee(XRP(1)));
|
||||
env.close();
|
||||
|
||||
//
|
||||
// Callback Execution
|
||||
//
|
||||
jvh = hso(emit_invoke_wasm);
|
||||
jvh[jss::HookName] = "41424344";
|
||||
jvh[jss::Flags] = hsfOVERRIDE;
|
||||
env(ripple::test::jtx::hook(alice, {{jvh}}, 0),
|
||||
M("Install named callback hook"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// Call named hook without specifying the hook name
|
||||
{
|
||||
auto jv = invoke::invoke(alice);
|
||||
env(jv,
|
||||
M("Call named hook without specifying the hook name"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
// execute only non-named hook
|
||||
BEAST_EXPECT(!env.meta()->isFieldPresent(sfHookEmissions));
|
||||
}
|
||||
|
||||
// Call named hook with the wrong hook name
|
||||
{
|
||||
auto jv = invoke::invoke(alice);
|
||||
jv[jss::HookName] = "41424345";
|
||||
env(jv, M("Call named hook with the wrong hook name"), HSFEE);
|
||||
env.close();
|
||||
// execute only non-named hook
|
||||
BEAST_EXPECT(!env.meta()->isFieldPresent(sfHookEmissions));
|
||||
}
|
||||
|
||||
// Call named hook with the correct hook name
|
||||
{
|
||||
auto jv = invoke::invoke(alice);
|
||||
jv[jss::HookName] = "41424344";
|
||||
env(jv, M("Call named hook with the correct hook name"), HSFEE);
|
||||
env.close();
|
||||
// execute both named and non-named hooks
|
||||
BEAST_EXPECT(env.meta()->isFieldPresent(sfHookEmissions));
|
||||
auto const hookEmissions =
|
||||
env.meta()->getFieldArray(sfHookEmissions)[0];
|
||||
auto const etxn = hookEmissions.getFieldH256(sfEmittedTxnID);
|
||||
env.close();
|
||||
auto const tx = env.closed()->txRead(etxn);
|
||||
BEAST_EXPECT(tx.first && tx.second);
|
||||
BEAST_EXPECT(!tx.first->isFieldPresent(sfHookName));
|
||||
// Callback transaction doesn't need to have hook name
|
||||
BEAST_EXPECT(tx.second->isFieldPresent(sfHookExecutions));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testFillCopy(FeatureBitset features)
|
||||
{
|
||||
@@ -1741,6 +2089,8 @@ public:
|
||||
|
||||
bool const hasHookCanEmit =
|
||||
env.current()->rules().enabled(featureHookCanEmit);
|
||||
bool const hasNamedHooks =
|
||||
env.current()->rules().enabled(featureNamedHooks);
|
||||
|
||||
auto const bob = Account{"bob"};
|
||||
env.fund(XRP(10000), bob);
|
||||
@@ -1789,6 +2139,8 @@ public:
|
||||
iv[jss::HookCanEmit] =
|
||||
"0000000000000000000000000000000000000000000000000000000000"
|
||||
"000000";
|
||||
if (hasNamedHooks)
|
||||
iv[jss::HookName] = strHex(std::string{"DEADBEEF"});
|
||||
jv[jss::Hooks][0U] = Json::Value{};
|
||||
jv[jss::Hooks][0U][jss::Hook] = iv;
|
||||
|
||||
@@ -1811,6 +2163,8 @@ public:
|
||||
iv[jss::HookCanEmit] =
|
||||
"0000000000000000000000000000000000000000000000000000000000"
|
||||
"000000";
|
||||
if (hasNamedHooks)
|
||||
iv[jss::HookName] = strHex(std::string{"DEADBEEF"});
|
||||
jv[jss::Hooks][0U] = Json::Value{};
|
||||
jv[jss::Hooks][0U][jss::Hook] = iv;
|
||||
|
||||
@@ -1834,6 +2188,8 @@ public:
|
||||
iv[jss::HookCanEmit] =
|
||||
"0000000000000000000000000000000000000000000000000000000000"
|
||||
"000000";
|
||||
if (hasNamedHooks)
|
||||
iv[jss::HookName] = strHex(std::string{"DEADBEEF"});
|
||||
jv[jss::Hooks][0U] = Json::Value{};
|
||||
jv[jss::Hooks][0U][jss::Hook] = iv;
|
||||
|
||||
@@ -1854,6 +2210,8 @@ public:
|
||||
iv[jss::HookCanEmit] =
|
||||
"0000000000000000000000000000000000000000000000000000000000"
|
||||
"000000";
|
||||
if (hasNamedHooks)
|
||||
iv[jss::HookName] = strHex(std::string{"DEADBEEF"});
|
||||
jv[jss::Hooks][0U] = Json::Value{};
|
||||
jv[jss::Hooks][0U][jss::Hook] = iv;
|
||||
|
||||
@@ -2002,6 +2360,8 @@ public:
|
||||
|
||||
bool const hasHookCanEmit =
|
||||
env.current()->rules().enabled(featureHookCanEmit);
|
||||
bool const hasNamedHooks =
|
||||
env.current()->rules().enabled(featureNamedHooks);
|
||||
|
||||
auto const alice = Account{"alice"};
|
||||
env.fund(XRP(10000), alice);
|
||||
@@ -2028,6 +2388,8 @@ public:
|
||||
iv[jss::HookCanEmit] =
|
||||
"0000000000000000000000000000000000000000000000000000000000"
|
||||
"000000";
|
||||
if (hasNamedHooks)
|
||||
iv[jss::HookName] = strHex(std::string{"DEADBEEF"});
|
||||
iv[jss::HookParameters] = Json::Value{Json::arrayValue};
|
||||
iv[jss::HookParameters][0U] = Json::Value{};
|
||||
iv[jss::HookParameters][0U][jss::HookParameter] = Json::Value{};
|
||||
@@ -2116,11 +2478,16 @@ public:
|
||||
"CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE"
|
||||
"CAFECAFE"},
|
||||
{jss::HookParameters, params},
|
||||
{jss::HookGrants, grants}})
|
||||
{jss::HookGrants, grants},
|
||||
{jss::HookName, strHex(std::string{"DEADBEEF"})},
|
||||
})
|
||||
{
|
||||
if (!hasHookCanEmit && key == jss::HookCanEmit)
|
||||
continue;
|
||||
|
||||
if (!hasNamedHooks && key == jss::HookName)
|
||||
continue;
|
||||
|
||||
Json::Value iv;
|
||||
iv[key] = value;
|
||||
jv[jss::Hooks][0U] = Json::Value{};
|
||||
@@ -2192,11 +2559,16 @@ public:
|
||||
{jss::HookCanEmit,
|
||||
"00000000000000000000000000000000000000000000000000000000"
|
||||
"00000000"},
|
||||
{jss::HookNamespace, to_string(uint256{beast::zero})}})
|
||||
{jss::HookNamespace, to_string(uint256{beast::zero})},
|
||||
{jss::HookName, strHex(std::string{"DEADBEEF"})},
|
||||
})
|
||||
{
|
||||
if (key == jss::HookCanEmit && !hasHookCanEmit)
|
||||
continue;
|
||||
|
||||
if (!hasNamedHooks && key == jss::HookName)
|
||||
continue;
|
||||
|
||||
Json::Value iv;
|
||||
iv[key] = value;
|
||||
jv[jss::Hooks][0U] = Json::Value{};
|
||||
@@ -2445,11 +2817,16 @@ public:
|
||||
"CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE"
|
||||
"CAFECAFE"},
|
||||
{jss::HookParameters, params},
|
||||
{jss::HookGrants, grants}})
|
||||
{jss::HookGrants, grants},
|
||||
{jss::HookName, strHex(std::string{"DEADBEEF"})},
|
||||
})
|
||||
{
|
||||
if (key == jss::HookCanEmit && !hasHookCanEmit)
|
||||
continue;
|
||||
|
||||
if (!hasNamedHooks && key == jss::HookName)
|
||||
continue;
|
||||
|
||||
Json::Value iv;
|
||||
iv[key] = value;
|
||||
jv[jss::Hooks][0U] = Json::Value{};
|
||||
@@ -14721,6 +15098,7 @@ public:
|
||||
testPageCap(features);
|
||||
|
||||
testHookOnV2(features);
|
||||
testHookName(features);
|
||||
|
||||
testFillCopy(features);
|
||||
|
||||
@@ -14824,7 +15202,7 @@ public:
|
||||
using namespace test::jtx;
|
||||
static FeatureBitset const all{supported_amendments()};
|
||||
|
||||
static std::array<FeatureBitset, 7> const feats{
|
||||
static std::array<FeatureBitset, 8> const feats{
|
||||
all,
|
||||
all - fixXahauV2,
|
||||
all - fixXahauV1 - fixXahauV2,
|
||||
@@ -14834,6 +15212,7 @@ public:
|
||||
featureHookCanEmit,
|
||||
all - fixXahauV1 - fixXahauV2 - fixNSDelete - fixPageCap -
|
||||
featureExtendedHookState,
|
||||
all - featureNamedHooks,
|
||||
};
|
||||
|
||||
if (BEAST_EXPECT(instance < feats.size()))
|
||||
@@ -14985,6 +15364,98 @@ private:
|
||||
)[test.hook]"];
|
||||
|
||||
HASH_WASM(accept2);
|
||||
|
||||
// This hook is used to test Callback
|
||||
TestHook emit_invoke_wasm = // WASM: 7
|
||||
wasm[
|
||||
R"[test.hook](
|
||||
#include <stdint.h>
|
||||
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 write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
|
||||
extern int64_t hook_account(uint32_t write_ptr, uint32_t write_len);
|
||||
extern int64_t etxn_reserve(uint32_t);
|
||||
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 SBUF(x) (uint32_t)x,sizeof(x)
|
||||
// clang-format off
|
||||
uint8_t txn[229] =
|
||||
{
|
||||
/* size, upto, field name */
|
||||
/* 3, 0, tt = Invoke */ 0x12U, 0x00U, 0x63U,
|
||||
/* 5, 3, flags */ 0x22U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 5, 8, sequence */ 0x24U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 6, 13, firstledgersequence */ 0x20U, 0x1AU, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 6, 19, lastledgersequence */ 0x20U, 0x1BU, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 9, 25, fee */ 0x68U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 35, 34, signingpubkey */ 0x73U, 0x21U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
/* 22, 69, account */ 0x81U, 0x14U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
/* 116, 91, emit details */
|
||||
/* 0, 229, */
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// TX BUILDER
|
||||
#define FLAGS_OUT (txn + 4U)
|
||||
#define FLS_OUT (txn + 15U)
|
||||
#define LLS_OUT (txn + 21U)
|
||||
#define FEE_OUT (txn + 26U)
|
||||
#define ACCOUNT_OUT (txn + 71U)
|
||||
#define EMIT_OUT (txn + 91U)
|
||||
|
||||
#define FLIP_ENDIAN_32(value) \
|
||||
(uint32_t)(((value & 0xFFU) << 24) | ((value & 0xFF00U) << 8) | \
|
||||
((value & 0xFF0000U) >> 8) | ((value & 0xFF000000U) >> 24))
|
||||
|
||||
#define SET_UINT32(ptr, value) *((uint32_t *)(ptr)) = FLIP_ENDIAN_32(value);
|
||||
|
||||
#define SET_NATIVE_AMOUNT(ptr, amount) \
|
||||
do { \
|
||||
uint8_t *b = (ptr); \
|
||||
*b++ = 0b01000000 + ((amount >> 56) & 0b00111111); \
|
||||
*b++ = (amount >> 48) & 0xFFU; \
|
||||
*b++ = (amount >> 40) & 0xFFU; \
|
||||
*b++ = (amount >> 32) & 0xFFU; \
|
||||
*b++ = (amount >> 24) & 0xFFU; \
|
||||
*b++ = (amount >> 16) & 0xFFU; \
|
||||
*b++ = (amount >> 8) & 0xFFU; \
|
||||
*b++ = (amount >> 0) & 0xFFU; \
|
||||
} while (0)
|
||||
|
||||
#define PREPARE_TXN() \
|
||||
do { \
|
||||
etxn_reserve(1); \
|
||||
uint32_t fls = (uint32_t)ledger_seq() + 1; \
|
||||
SET_UINT32(FLS_OUT, fls); \
|
||||
SET_UINT32(LLS_OUT, fls + 4); \
|
||||
hook_account(ACCOUNT_OUT, 20); \
|
||||
etxn_details(EMIT_OUT, 138U); \
|
||||
int64_t fee = etxn_fee_base(SBUF(txn)); \
|
||||
SET_NATIVE_AMOUNT(FEE_OUT, fee); \
|
||||
} while (0)
|
||||
|
||||
|
||||
int64_t cbak(uint32_t r)
|
||||
{
|
||||
_g(1,1);
|
||||
return accept(0,0,0);
|
||||
}
|
||||
|
||||
int64_t hook(uint32_t reserved)
|
||||
{
|
||||
_g(1,1);
|
||||
PREPARE_TXN();
|
||||
uint8_t emithash[32];
|
||||
int64_t emit_result = emit(SBUF(emithash), SBUF(txn));
|
||||
if (emit_result < 0)
|
||||
return rollback(SBUF("Emit failed."), __LINE__);
|
||||
return accept(SBUF("Emit succeeded."), __LINE__);
|
||||
}
|
||||
)[test.hook]"];
|
||||
|
||||
HASH_WASM(emit_invoke);
|
||||
};
|
||||
|
||||
#define SETHOOK_TEST(i, last) \
|
||||
@@ -15002,7 +15473,8 @@ SETHOOK_TEST(2, false)
|
||||
SETHOOK_TEST(3, false)
|
||||
SETHOOK_TEST(4, false)
|
||||
SETHOOK_TEST(5, false)
|
||||
SETHOOK_TEST(6, true)
|
||||
SETHOOK_TEST(6, false)
|
||||
SETHOOK_TEST(7, true)
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(SetHook0, app, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(SetHook1, app, ripple, 2);
|
||||
@@ -15011,6 +15483,7 @@ BEAST_DEFINE_TESTSUITE_PRIO(SetHook3, app, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(SetHook4, app, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(SetHook5, app, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(SetHook6, app, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(SetHook7, app, ripple, 2);
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
#undef M
|
||||
|
||||
@@ -34110,6 +34110,193 @@ std::map<std::string, std::vector<uint8_t>> wasm = {
|
||||
0x0BU,
|
||||
}},
|
||||
|
||||
/* ==== WASM: 100 ==== */
|
||||
{R"[test.hook](
|
||||
#include <stdint.h>
|
||||
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 write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len);
|
||||
extern int64_t hook_account(uint32_t write_ptr, uint32_t write_len);
|
||||
extern int64_t etxn_reserve(uint32_t);
|
||||
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 SBUF(x) (uint32_t)x,sizeof(x)
|
||||
// clang-format off
|
||||
uint8_t txn[229] =
|
||||
{
|
||||
/* size, upto, field name */
|
||||
/* 3, 0, tt = Invoke */ 0x12U, 0x00U, 0x63U,
|
||||
/* 5, 3, flags */ 0x22U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 5, 8, sequence */ 0x24U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 6, 13, firstledgersequence */ 0x20U, 0x1AU, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 6, 19, lastledgersequence */ 0x20U, 0x1BU, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 9, 25, fee */ 0x68U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
/* 35, 34, signingpubkey */ 0x73U, 0x21U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
/* 22, 69, account */ 0x81U, 0x14U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
/* 116, 91, emit details */
|
||||
/* 0, 229, */
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// TX BUILDER
|
||||
#define FLAGS_OUT (txn + 4U)
|
||||
#define FLS_OUT (txn + 15U)
|
||||
#define LLS_OUT (txn + 21U)
|
||||
#define FEE_OUT (txn + 26U)
|
||||
#define ACCOUNT_OUT (txn + 71U)
|
||||
#define EMIT_OUT (txn + 91U)
|
||||
|
||||
#define FLIP_ENDIAN_32(value) \
|
||||
(uint32_t)(((value & 0xFFU) << 24) | ((value & 0xFF00U) << 8) | \
|
||||
((value & 0xFF0000U) >> 8) | ((value & 0xFF000000U) >> 24))
|
||||
|
||||
#define SET_UINT32(ptr, value) *((uint32_t *)(ptr)) = FLIP_ENDIAN_32(value);
|
||||
|
||||
#define SET_NATIVE_AMOUNT(ptr, amount) \
|
||||
do { \
|
||||
uint8_t *b = (ptr); \
|
||||
*b++ = 0b01000000 + ((amount >> 56) & 0b00111111); \
|
||||
*b++ = (amount >> 48) & 0xFFU; \
|
||||
*b++ = (amount >> 40) & 0xFFU; \
|
||||
*b++ = (amount >> 32) & 0xFFU; \
|
||||
*b++ = (amount >> 24) & 0xFFU; \
|
||||
*b++ = (amount >> 16) & 0xFFU; \
|
||||
*b++ = (amount >> 8) & 0xFFU; \
|
||||
*b++ = (amount >> 0) & 0xFFU; \
|
||||
} while (0)
|
||||
|
||||
#define PREPARE_TXN() \
|
||||
do { \
|
||||
etxn_reserve(1); \
|
||||
uint32_t fls = (uint32_t)ledger_seq() + 1; \
|
||||
SET_UINT32(FLS_OUT, fls); \
|
||||
SET_UINT32(LLS_OUT, fls + 4); \
|
||||
hook_account(ACCOUNT_OUT, 20); \
|
||||
etxn_details(EMIT_OUT, 138U); \
|
||||
int64_t fee = etxn_fee_base(SBUF(txn)); \
|
||||
SET_NATIVE_AMOUNT(FEE_OUT, fee); \
|
||||
} while (0)
|
||||
|
||||
|
||||
int64_t cbak(uint32_t r)
|
||||
{
|
||||
_g(1,1);
|
||||
return accept(0,0,0);
|
||||
}
|
||||
|
||||
int64_t hook(uint32_t reserved)
|
||||
{
|
||||
_g(1,1);
|
||||
PREPARE_TXN();
|
||||
uint8_t emithash[32];
|
||||
int64_t emit_result = emit(SBUF(emithash), SBUF(txn));
|
||||
if (emit_result < 0)
|
||||
return rollback(SBUF("Emit failed."), __LINE__);
|
||||
return accept(SBUF("Emit succeeded."), __LINE__);
|
||||
}
|
||||
)[test.hook]",
|
||||
{
|
||||
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U, 0x25U,
|
||||
0x06U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x03U, 0x7FU,
|
||||
0x7FU, 0x7EU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x60U,
|
||||
0x00U, 0x01U, 0x7EU, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U,
|
||||
0x04U, 0x7FU, 0x7FU, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x02U, 0x8FU, 0x01U,
|
||||
0x09U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U, 0x00U, 0x00U,
|
||||
0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U, 0x63U, 0x63U, 0x65U, 0x70U,
|
||||
0x74U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x0CU, 0x65U, 0x74U,
|
||||
0x78U, 0x6EU, 0x5FU, 0x72U, 0x65U, 0x73U, 0x65U, 0x72U, 0x76U, 0x65U,
|
||||
0x00U, 0x02U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x0AU, 0x6CU, 0x65U, 0x64U,
|
||||
0x67U, 0x65U, 0x72U, 0x5FU, 0x73U, 0x65U, 0x71U, 0x00U, 0x03U, 0x03U,
|
||||
0x65U, 0x6EU, 0x76U, 0x0CU, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x5FU, 0x61U,
|
||||
0x63U, 0x63U, 0x6FU, 0x75U, 0x6EU, 0x74U, 0x00U, 0x04U, 0x03U, 0x65U,
|
||||
0x6EU, 0x76U, 0x0CU, 0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x64U, 0x65U,
|
||||
0x74U, 0x61U, 0x69U, 0x6CU, 0x73U, 0x00U, 0x04U, 0x03U, 0x65U, 0x6EU,
|
||||
0x76U, 0x0DU, 0x65U, 0x74U, 0x78U, 0x6EU, 0x5FU, 0x66U, 0x65U, 0x65U,
|
||||
0x5FU, 0x62U, 0x61U, 0x73U, 0x65U, 0x00U, 0x04U, 0x03U, 0x65U, 0x6EU,
|
||||
0x76U, 0x04U, 0x65U, 0x6DU, 0x69U, 0x74U, 0x00U, 0x05U, 0x03U, 0x65U,
|
||||
0x6EU, 0x76U, 0x08U, 0x72U, 0x6FU, 0x6CU, 0x6CU, 0x62U, 0x61U, 0x63U,
|
||||
0x6BU, 0x00U, 0x01U, 0x03U, 0x03U, 0x02U, 0x02U, 0x02U, 0x05U, 0x03U,
|
||||
0x01U, 0x00U, 0x02U, 0x06U, 0x27U, 0x06U, 0x7FU, 0x01U, 0x41U, 0x90U,
|
||||
0x8AU, 0x04U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0x82U, 0x0AU, 0x0BU, 0x7FU,
|
||||
0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0x90U, 0x8AU,
|
||||
0x04U, 0x0BU, 0x7FU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0x7FU, 0x00U,
|
||||
0x41U, 0x80U, 0x08U, 0x0BU, 0x07U, 0x0FU, 0x02U, 0x04U, 0x63U, 0x62U,
|
||||
0x61U, 0x6BU, 0x00U, 0x09U, 0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U,
|
||||
0x0AU, 0x0AU, 0xA8U, 0x83U, 0x00U, 0x02U, 0x99U, 0x80U, 0x00U, 0x00U,
|
||||
0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U,
|
||||
0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x00U, 0x10U, 0x81U, 0x80U,
|
||||
0x80U, 0x80U, 0x00U, 0x0BU, 0x88U, 0x83U, 0x00U, 0x02U, 0x03U, 0x7FU,
|
||||
0x01U, 0x7EU, 0x23U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x41U, 0x20U,
|
||||
0x6BU, 0x22U, 0x01U, 0x24U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x41U,
|
||||
0x01U, 0x41U, 0x01U, 0x10U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU,
|
||||
0x41U, 0x01U, 0x10U, 0x82U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U,
|
||||
0x00U, 0x10U, 0x83U, 0x80U, 0x80U, 0x80U, 0x00U, 0xA7U, 0x22U, 0x02U,
|
||||
0x41U, 0x05U, 0x6AU, 0x22U, 0x03U, 0x41U, 0x18U, 0x74U, 0x20U, 0x03U,
|
||||
0x41U, 0x08U, 0x74U, 0x41U, 0x80U, 0x80U, 0xFCU, 0x07U, 0x71U, 0x72U,
|
||||
0x20U, 0x03U, 0x41U, 0x08U, 0x76U, 0x41U, 0x80U, 0xFEU, 0x03U, 0x71U,
|
||||
0x20U, 0x03U, 0x41U, 0x18U, 0x76U, 0x72U, 0x72U, 0x36U, 0x02U, 0x95U,
|
||||
0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x00U, 0x20U, 0x02U, 0x41U, 0x01U,
|
||||
0x6AU, 0x22U, 0x03U, 0x41U, 0x18U, 0x74U, 0x20U, 0x03U, 0x41U, 0x08U,
|
||||
0x74U, 0x41U, 0x80U, 0x80U, 0xFCU, 0x07U, 0x71U, 0x72U, 0x20U, 0x03U,
|
||||
0x41U, 0x08U, 0x76U, 0x41U, 0x80U, 0xFEU, 0x03U, 0x71U, 0x20U, 0x03U,
|
||||
0x41U, 0x18U, 0x76U, 0x72U, 0x72U, 0x36U, 0x02U, 0x8FU, 0x88U, 0x80U,
|
||||
0x80U, 0x00U, 0x41U, 0xC7U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x14U,
|
||||
0x10U, 0x84U, 0x80U, 0x80U, 0x80U, 0x00U, 0x1AU, 0x41U, 0xDBU, 0x88U,
|
||||
0x80U, 0x80U, 0x00U, 0x41U, 0x8AU, 0x01U, 0x10U, 0x85U, 0x80U, 0x80U,
|
||||
0x80U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x80U, 0x88U, 0x80U, 0x80U,
|
||||
0x00U, 0x41U, 0xE5U, 0x01U, 0x10U, 0x86U, 0x80U, 0x80U, 0x80U, 0x00U,
|
||||
0x22U, 0x04U, 0x3CU, 0x00U, 0xA1U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U,
|
||||
0x00U, 0x20U, 0x04U, 0x42U, 0x08U, 0x88U, 0x3CU, 0x00U, 0xA0U, 0x88U,
|
||||
0x80U, 0x80U, 0x00U, 0x41U, 0x00U, 0x20U, 0x04U, 0x42U, 0x10U, 0x88U,
|
||||
0x3CU, 0x00U, 0x9FU, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x00U, 0x20U,
|
||||
0x04U, 0x42U, 0x18U, 0x88U, 0x3CU, 0x00U, 0x9EU, 0x88U, 0x80U, 0x80U,
|
||||
0x00U, 0x41U, 0x00U, 0x20U, 0x04U, 0x42U, 0x20U, 0x88U, 0x3CU, 0x00U,
|
||||
0x9DU, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U, 0x00U, 0x20U, 0x04U, 0x42U,
|
||||
0x28U, 0x88U, 0x3CU, 0x00U, 0x9CU, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U,
|
||||
0x00U, 0x20U, 0x04U, 0x42U, 0x30U, 0x88U, 0x3CU, 0x00U, 0x9BU, 0x88U,
|
||||
0x80U, 0x80U, 0x00U, 0x41U, 0x00U, 0x20U, 0x04U, 0x42U, 0x38U, 0x88U,
|
||||
0xA7U, 0x41U, 0x3FU, 0x71U, 0x41U, 0xC0U, 0x00U, 0x72U, 0x3AU, 0x00U,
|
||||
0x9AU, 0x88U, 0x80U, 0x80U, 0x00U, 0x02U, 0x40U, 0x02U, 0x40U, 0x20U,
|
||||
0x01U, 0x41U, 0x20U, 0x41U, 0x80U, 0x88U, 0x80U, 0x80U, 0x00U, 0x41U,
|
||||
0xE5U, 0x01U, 0x10U, 0x87U, 0x80U, 0x80U, 0x80U, 0x00U, 0x42U, 0x7FU,
|
||||
0x55U, 0x0DU, 0x00U, 0x41U, 0xE5U, 0x89U, 0x80U, 0x80U, 0x00U, 0x41U,
|
||||
0x0DU, 0x42U, 0xD3U, 0x00U, 0x10U, 0x88U, 0x80U, 0x80U, 0x80U, 0x00U,
|
||||
0x21U, 0x04U, 0x0CU, 0x01U, 0x0BU, 0x41U, 0xF2U, 0x89U, 0x80U, 0x80U,
|
||||
0x00U, 0x41U, 0x10U, 0x42U, 0xD4U, 0x00U, 0x10U, 0x81U, 0x80U, 0x80U,
|
||||
0x80U, 0x00U, 0x21U, 0x04U, 0x0BU, 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU,
|
||||
0x24U, 0x80U, 0x80U, 0x80U, 0x80U, 0x00U, 0x20U, 0x04U, 0x0BU, 0x0BU,
|
||||
0x90U, 0x02U, 0x02U, 0x00U, 0x41U, 0x80U, 0x08U, 0x0BU, 0xE5U, 0x01U,
|
||||
0x12U, 0x00U, 0x63U, 0x22U, 0x00U, 0x00U, 0x00U, 0x00U, 0x24U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x20U, 0x1AU, 0x00U, 0x00U, 0x00U, 0x00U, 0x20U,
|
||||
0x1BU, 0x00U, 0x00U, 0x00U, 0x00U, 0x68U, 0x40U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x73U, 0x21U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x81U,
|
||||
0x14U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x41U, 0xE5U, 0x09U, 0x0BU, 0x1DU, 0x45U, 0x6DU, 0x69U, 0x74U, 0x20U,
|
||||
0x66U, 0x61U, 0x69U, 0x6CU, 0x65U, 0x64U, 0x2EU, 0x00U, 0x45U, 0x6DU,
|
||||
0x69U, 0x74U, 0x20U, 0x73U, 0x75U, 0x63U, 0x63U, 0x65U, 0x65U, 0x64U,
|
||||
0x65U, 0x64U, 0x2EU, 0x00U,
|
||||
}},
|
||||
|
||||
};
|
||||
}
|
||||
} // namespace ripple
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <xrpld/app/ledger/Ledger.h>
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/ledger/OpenLedger.h>
|
||||
#include <xrpld/app/tx/detail/URIToken.h>
|
||||
#include <xrpld/ledger/ApplyView.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/hook/Enum.h>
|
||||
@@ -230,6 +231,7 @@ SetHook::inferOperation(STObject const& hookSetObj)
|
||||
hookSetObj.isFieldPresent(sfHookOnIncoming))) &&
|
||||
!hookSetObj.isFieldPresent(sfHookCanEmit) &&
|
||||
!hookSetObj.isFieldPresent(sfHookApiVersion) &&
|
||||
!hookSetObj.isFieldPresent(sfHookName) &&
|
||||
!hookSetObj.isFieldPresent(sfFlags))
|
||||
return hsoNOOP;
|
||||
|
||||
@@ -267,6 +269,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
hookSetObj.isFieldPresent(sfHookOnIncoming) ||
|
||||
hookSetObj.isFieldPresent(sfHookCanEmit) ||
|
||||
hookSetObj.isFieldPresent(sfHookApiVersion) ||
|
||||
hookSetObj.isFieldPresent(sfHookName) ||
|
||||
!hookSetObj.isFieldPresent(sfFlags) ||
|
||||
!hookSetObj.isFieldPresent(sfHookNamespace))
|
||||
{
|
||||
@@ -300,6 +303,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
hookSetObj.isFieldPresent(sfHookCanEmit) ||
|
||||
hookSetObj.isFieldPresent(sfHookApiVersion) ||
|
||||
hookSetObj.isFieldPresent(sfHookNamespace) ||
|
||||
hookSetObj.isFieldPresent(sfHookName) ||
|
||||
!hookSetObj.isFieldPresent(sfFlags))
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
@@ -510,6 +514,14 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
// pass
|
||||
}
|
||||
|
||||
// validate sfHookName
|
||||
if (hookSetObj.isFieldPresent(sfHookName))
|
||||
{
|
||||
auto name = hookSetObj.getFieldVL(sfHookName);
|
||||
if (!validateHookName(name, ctx.j))
|
||||
return false;
|
||||
}
|
||||
|
||||
// finally validate web assembly byte code
|
||||
{
|
||||
if (!hookSetObj.isFieldPresent(sfCreateCode))
|
||||
@@ -609,6 +621,23 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SetHook::validateHookName(Blob const& name, beast::Journal const& j)
|
||||
{
|
||||
if (name.size() != 0 && (name.size() < 4 || 16 < name.size()))
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "sfHookName must be between 8 and 32 hex characters.";
|
||||
return false;
|
||||
}
|
||||
if (!URIToken::validateUTF8(name))
|
||||
{
|
||||
JLOG(j.trace()) << "sfHookName must be a valid UTF-8 string.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note that if fee calculation causes an overflow then INITIAL_XRP is returned
|
||||
// as a way of ensuring that the txn cannot possibly meet the fee requirement.
|
||||
XRPAmount
|
||||
@@ -783,6 +812,10 @@ SetHook::preflight(PreflightContext const& ctx)
|
||||
hookSetObj.isFieldPresent(sfHookCanEmit))
|
||||
return temDISABLED;
|
||||
|
||||
if (!ctx.rules.enabled(featureNamedHooks) &&
|
||||
hookSetObj.isFieldPresent(sfHookName))
|
||||
return temDISABLED;
|
||||
|
||||
for (auto const& hookSetElement : hookSetObj)
|
||||
{
|
||||
auto const& name = hookSetElement.getFName();
|
||||
@@ -792,7 +825,7 @@ SetHook::preflight(PreflightContext const& ctx)
|
||||
name != sfHookOn && name != sfHookOnOutgoing &&
|
||||
name != sfHookOnIncoming && name != sfHookGrants &&
|
||||
name != sfHookApiVersion && name != sfFlags &&
|
||||
name != sfHookCanEmit)
|
||||
name != sfHookCanEmit && name != sfHookName)
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << hook::log::HOOK_INVALID_FIELD << ")["
|
||||
@@ -1348,6 +1381,8 @@ SetHook::setHook()
|
||||
std::optional<uint256> newHookCanEmit;
|
||||
std::optional<uint256> defHookCanEmit;
|
||||
|
||||
std::optional<Blob> newHookName;
|
||||
|
||||
// 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
|
||||
@@ -1416,7 +1451,6 @@ SetHook::setHook()
|
||||
|
||||
if (oldDefSLE && oldDefSLE->isFieldPresent(sfHookCanEmit))
|
||||
defHookCanEmit = oldDefSLE->getFieldH256(sfHookCanEmit);
|
||||
|
||||
if (oldHook && oldHook->get().isFieldPresent(sfHookCanEmit))
|
||||
oldHookCanEmit = oldHook->get().getFieldH256(sfHookCanEmit);
|
||||
else if (defHookCanEmit)
|
||||
@@ -1453,6 +1487,9 @@ SetHook::setHook()
|
||||
newNamespace = hookSetObj->get().getFieldH256(sfHookNamespace);
|
||||
newDirKeylet = keylet::hookStateDir(account_, *newNamespace);
|
||||
}
|
||||
|
||||
if (hookSetObj->get().isFieldPresent(sfHookName))
|
||||
newHookName = hookSetObj->get().getFieldVL(sfHookName);
|
||||
}
|
||||
|
||||
// users may destroy a namespace in any operation except NOOP and
|
||||
@@ -1574,6 +1611,9 @@ SetHook::setHook()
|
||||
newHook.setFieldH256(
|
||||
sfHookNamespace,
|
||||
oldHook->get().getFieldH256(sfHookNamespace));
|
||||
if (oldHook->get().isFieldPresent(sfHookName))
|
||||
newHook.setFieldVL(
|
||||
sfHookName, oldHook->get().getFieldVL(sfHookName));
|
||||
|
||||
// set the namespace if it differs from the definition namespace
|
||||
if (newNamespace)
|
||||
@@ -1638,6 +1678,20 @@ SetHook::setHook()
|
||||
newHook.setFieldH256(sfHookCanEmit, *newHookCanEmit);
|
||||
}
|
||||
|
||||
// set the hookname field on ltHook when it is explicitly
|
||||
// provided
|
||||
if (newHookName)
|
||||
{
|
||||
if (newHookName->size() == 0)
|
||||
{
|
||||
newHook.makeFieldAbsent(sfHookName);
|
||||
}
|
||||
else
|
||||
{
|
||||
newHook.setFieldVL(sfHookName, *newHookName);
|
||||
}
|
||||
}
|
||||
|
||||
// parameters
|
||||
if (hookSetObj->get().isFieldPresent(sfHookParameters) &&
|
||||
hookSetObj->get().getFieldArray(sfHookParameters).empty())
|
||||
@@ -1839,6 +1893,12 @@ SetHook::setHook()
|
||||
newHook.setFieldArray(sfHookGrants, grants);
|
||||
}
|
||||
|
||||
if (hookSetObj->get().isFieldPresent(sfHookName) &&
|
||||
hookSetObj->get().getFieldVL(sfHookName).size() > 0)
|
||||
newHook.setFieldVL(
|
||||
sfHookName,
|
||||
hookSetObj->get().getFieldVL(sfHookName));
|
||||
|
||||
slesToInsert.emplace(keylet, newHookDef);
|
||||
newHook.setFieldH256(sfHookHash, *createHookHash);
|
||||
newHooks.push_back(std::move(newHook));
|
||||
@@ -1950,6 +2010,11 @@ SetHook::setHook()
|
||||
*defHookCanEmit == *newHookCanEmit))
|
||||
newHook.setFieldH256(sfHookCanEmit, *newHookCanEmit);
|
||||
|
||||
// set the hookname field on ltHook when it is explicitly
|
||||
// provided
|
||||
if (newHookName && newHookName->size() > 0)
|
||||
newHook.setFieldVL(sfHookName, *newHookName);
|
||||
|
||||
// parameters
|
||||
TER result = updateHookParameters(
|
||||
ctx,
|
||||
@@ -1999,7 +2064,8 @@ SetHook::setHook()
|
||||
// sfParameters: 1 reserve PER entry
|
||||
// sfGrants are: 1 reserve PER entry
|
||||
// sfHookHash, sfHookNamespace, sfHookOn, sfHookOnOutgoing,
|
||||
// sfHookOnIncoming, sfHookCanEmit sfHookApiVersion, sfFlags: free
|
||||
// sfHookOnIncoming, sfHookCanEmit sfHookApiVersion, sfFlags,
|
||||
// sfHookName: free
|
||||
|
||||
// ltHookDefinition is not reserved because it is an unowned object,
|
||||
// rather the uploader is billed via fee according to the following:
|
||||
|
||||
@@ -91,6 +91,9 @@ public:
|
||||
static HookSetValidation
|
||||
validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj);
|
||||
|
||||
static bool
|
||||
validateHookName(Blob const& name, beast::Journal const& j);
|
||||
|
||||
static uint32_t
|
||||
computeHookReserve(STObject const& hookObj);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <xrpld/app/misc/LoadFeeTrack.h>
|
||||
#include <xrpld/app/tx/apply.h>
|
||||
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
||||
#include <xrpld/app/tx/detail/SetHook.h>
|
||||
#include <xrpld/app/tx/detail/SignerEntries.h>
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
#include <xrpld/core/Config.h>
|
||||
@@ -148,6 +149,16 @@ preflight1(PreflightContext const& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfHookName))
|
||||
{
|
||||
if (!ctx.rules.enabled(featureHooks) ||
|
||||
!ctx.rules.enabled(featureNamedHooks))
|
||||
return temMALFORMED;
|
||||
|
||||
if (!SetHook::validateHookName(ctx.tx.getFieldVL(sfHookName), ctx.j))
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const spk = ctx.tx.getSigningPubKey();
|
||||
|
||||
if (!spk.empty() && !publicKeyType(makeSlice(spk)))
|
||||
@@ -266,8 +277,24 @@ Transactor::calculateHookChainFee(
|
||||
// at the same ledger the fee calculation for it can no longer occur
|
||||
if (!hookDef)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
printf("calculateHookChainFee edge case\n");
|
||||
continue;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
std::optional<Blob> requiredHookName;
|
||||
if (hookObj.isFieldPresent(sfHookName) &&
|
||||
hookObj.getFieldVL(sfHookName).size() > 0)
|
||||
requiredHookName = hookObj.getFieldVL(sfHookName);
|
||||
|
||||
if (requiredHookName)
|
||||
{
|
||||
// need to specify same hook name in the transaction
|
||||
if (!tx.isFieldPresent(sfHookName))
|
||||
continue;
|
||||
if (*requiredHookName != tx.getFieldVL(sfHookName))
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
@@ -1311,16 +1338,34 @@ Transactor::executeHookChain(
|
||||
|
||||
if (hookSkips.find(hookHash) != hookSkips.end())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.trace()) << "HookInfo: Skipping " << hookHash;
|
||||
continue;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const& hookDef =
|
||||
ctx_.view().peek(keylet::hookDefinition(hookHash));
|
||||
if (!hookDef)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.warn()) << "HookError[]: Failure: hook def missing (send)";
|
||||
continue;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
std::optional<Blob> requiredHookName;
|
||||
if (hookObj.isFieldPresent(sfHookName) &&
|
||||
hookObj.getFieldVL(sfHookName).size() > 0)
|
||||
requiredHookName = hookObj.getFieldVL(sfHookName);
|
||||
|
||||
if (requiredHookName)
|
||||
{
|
||||
// need to specify same hook name in the transaction
|
||||
if (!ctx_.tx.isFieldPresent(sfHookName))
|
||||
continue;
|
||||
if (*requiredHookName != ctx_.tx.getFieldVL(sfHookName))
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if the hook can fire
|
||||
|
||||
Reference in New Issue
Block a user