Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HookEmit [DO NOT MERGE] #392

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@ git-subtree. See those directories' README files for more details.
- [explorer.xahau.network](https://explorer.xahau.network)
- **Testnet & Faucet**: Test applications and obtain test XAH at [xahau-test.net](https://xahau-test.net) and use the testnet explorer at [explorer.xahau.network](https://explorer.xahau.network).
- **Supporting Wallets**: A list of wallets that support XAH and Xahau-based assets.
- [Xumm](https://xumm.app)
- [Xaman](https://xaman.app)
- [Crossmark](https://crossmark.io)
5 changes: 5 additions & 0 deletions src/ripple/app/hook/applyHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@ namespace hook {
bool
canHook(ripple::TxType txType, ripple::uint256 hookOn);

bool
canEmit(ripple::TxType txType, ripple::uint256 hookEmit);

struct HookResult;

HookResult
Expand All @@ -436,6 +439,7 @@ apply(
used for caching (one day) */
ripple::uint256 const&
hookHash, /* hash of the actual hook byte code, used for metadata */
ripple::uint256 const& hookEmit,
ripple::uint256 const& hookNamespace,
ripple::Blob const& wasm,
std::map<
Expand Down Expand Up @@ -472,6 +476,7 @@ struct HookResult
{
ripple::uint256 const hookSetTxnID;
ripple::uint256 const hookHash;
ripple::uint256 const hookEmit;
ripple::Keylet const accountKeylet;
ripple::Keylet const ownerDirKeylet;
ripple::Keylet const hookKeylet;
Expand Down
18 changes: 18 additions & 0 deletions src/ripple/app/hook/impl/applyHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,12 @@ hook::canHook(ripple::TxType txType, ripple::uint256 hookOn)
return (hookOn & UINT256_BIT[txType]) != beast::zero;
}

bool
hook::canEmit(ripple::TxType txType, ripple::uint256 hookEmit)
{
return hook::canHook(txType, hookEmit);
}

// Update HookState ledger objects for the hook... only called after accept()
// assumes the specified acc has already been checked for authoriation (hook
// grants)
Expand Down Expand Up @@ -1179,6 +1185,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& hookEmit,
ripple::uint256 const& hookNamespace,
ripple::Blob const& wasm,
std::map<
Expand Down Expand Up @@ -1206,6 +1213,7 @@ hook::apply(
.result =
{.hookSetTxnID = hookSetTxnID,
.hookHash = hookHash,
.hookEmit = hookEmit,
.accountKeylet = keylet::account(account),
.ownerDirKeylet = keylet::ownerDir(account),
.hookKeylet = keylet::hook(account),
Expand Down Expand Up @@ -3269,6 +3277,16 @@ DEFINE_HOOK_FUNCTION(
return EMISSION_FAILURE;
}

ripple::TxType txType = stpTrans->getTxnType();

ripple::uint256 const& hookEmit = hookCtx.result.hookEmit;
if (!hook::canEmit(txType, hookEmit))
{
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
Expand Down
58 changes: 52 additions & 6 deletions src/ripple/app/tx/impl/SetHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ validateHookParams(SetHookCtx& ctx, STArray const& hookParams)
// infer which operation the user is attempting to execute from the present and
// absent fields
HookSetOperation
SetHook::inferOperation(STObject const& hookSetObj)
SetHook::inferOperation(SetHookCtx& ctx, STObject const& hookSetObj)
{
uint64_t wasmByteCount = hookSetObj.isFieldPresent(sfCreateCode)
? hookSetObj.getFieldVL(sfCreateCode).size()
Expand All @@ -225,6 +225,8 @@ SetHook::inferOperation(STObject const& hookSetObj)
!hookSetObj.isFieldPresent(sfHookNamespace) &&
!hookSetObj.isFieldPresent(sfHookParameters) &&
!hookSetObj.isFieldPresent(sfHookOn) &&
(!ctx.rules.enabled(featureHookEmit) ||
!hookSetObj.isFieldPresent(sfHookEmit)) &&
!hookSetObj.isFieldPresent(sfHookApiVersion) &&
!hookSetObj.isFieldPresent(sfFlags))
return hsoNOOP;
Expand All @@ -248,7 +250,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
? hookSetObj.getFieldU32(sfFlags)
: 0;

switch (inferOperation(hookSetObj))
switch (inferOperation(ctx, hookSetObj))
{
case hsoNOOP: {
return true;
Expand All @@ -259,6 +261,8 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
if (hookSetObj.isFieldPresent(sfHookGrants) ||
hookSetObj.isFieldPresent(sfHookParameters) ||
hookSetObj.isFieldPresent(sfHookOn) ||
(ctx.rules.enabled(featureHookEmit) &&
hookSetObj.isFieldPresent(sfHookEmit)) ||
hookSetObj.isFieldPresent(sfHookApiVersion) ||
!hookSetObj.isFieldPresent(sfFlags) ||
!hookSetObj.isFieldPresent(sfHookNamespace))
Expand Down Expand Up @@ -288,6 +292,8 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
if (hookSetObj.isFieldPresent(sfHookGrants) ||
hookSetObj.isFieldPresent(sfHookParameters) ||
hookSetObj.isFieldPresent(sfHookOn) ||
(ctx.rules.enabled(featureHookEmit) &&
hookSetObj.isFieldPresent(sfHookEmit)) ||
hookSetObj.isFieldPresent(sfHookApiVersion) ||
hookSetObj.isFieldPresent(sfHookNamespace) ||
!hookSetObj.isFieldPresent(sfFlags))
Expand Down Expand Up @@ -452,6 +458,9 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
return false;
}

// validate sfHookEmit
// HookEmit field is an optional field for backward compatibility

// finally validate web assembly byte code
{
if (!hookSetObj.isFieldPresent(sfCreateCode))
Expand Down Expand Up @@ -720,7 +729,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 &&
(!ctx.rules.enabled(featureHookEmit) || name != sfHookEmit))
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HOOK_INVALID_FIELD << ")["
Expand Down Expand Up @@ -1221,6 +1231,10 @@ SetHook::setHook()
std::optional<uint256> newHookOn;
std::optional<uint256> defHookOn;

std::optional<uint256> oldHookEmit;
std::optional<uint256> newHookEmit;
std::optional<uint256> defHookEmit;

// 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
Expand All @@ -1243,7 +1257,7 @@ SetHook::setHook()
HookSetOperation op = hsoNOOP;

if (hookSetObj)
op = inferOperation(hookSetObj->get());
op = inferOperation(ctx, hookSetObj->get());

// these flags are not able to be passed onto the ledger object
int newFlags = 0;
Expand Down Expand Up @@ -1281,6 +1295,14 @@ SetHook::setHook()
oldHookOn = oldHook->get().getFieldH256(sfHookOn);
else if (defHookOn)
oldHookOn = *defHookOn;

if (oldDefSLE && oldDefSLE->isFieldPresent(sfHookEmit))
defHookEmit = oldDefSLE->getFieldH256(sfHookEmit);

if (oldHook && oldHook->get().isFieldPresent(sfHookEmit))
oldHookEmit = oldHook->get().getFieldH256(sfHookEmit);
else if (defHookEmit)
oldHookEmit = *defHookEmit;
}

// in preparation for three way merge populate fields if they are
Expand All @@ -1297,6 +1319,9 @@ SetHook::setHook()
if (hookSetObj->get().isFieldPresent(sfHookOn))
newHookOn = hookSetObj->get().getFieldH256(sfHookOn);

if (hookSetObj->get().isFieldPresent(sfHookEmit))
newHookEmit = hookSetObj->get().getFieldH256(sfHookEmit);

if (hookSetObj->get().isFieldPresent(sfHookNamespace))
{
newNamespace = hookSetObj->get().getFieldH256(sfHookNamespace);
Expand Down Expand Up @@ -1406,6 +1431,9 @@ SetHook::setHook()
if (oldHook->get().isFieldPresent(sfHookOn))
newHook.setFieldH256(
sfHookOn, oldHook->get().getFieldH256(sfHookOn));
if (oldHook->get().isFieldPresent(sfHookEmit))
newHook.setFieldH256(
sfHookEmit, oldHook->get().getFieldH256(sfHookEmit));
if (oldHook->get().isFieldPresent(sfHookNamespace))
newHook.setFieldH256(
sfHookNamespace,
Expand Down Expand Up @@ -1435,6 +1463,18 @@ SetHook::setHook()
newHook.setFieldH256(sfHookOn, *newHookOn);
}

// set the hookemit field if it differs from definition
if (newHookEmit)
{
if (*defHookEmit == *newHookEmit)
{
if (newHook.isFieldPresent(sfHookEmit))
newHook.makeFieldAbsent(sfHookEmit);
}
else
newHook.setFieldH256(sfHookEmit, *newHookEmit);
}

// parameters
if (hookSetObj->get().isFieldPresent(sfHookParameters) &&
hookSetObj->get().getFieldArray(sfHookParameters).empty())
Expand Down Expand Up @@ -1584,6 +1624,7 @@ SetHook::setHook()
auto newHookDef = std::make_shared<SLE>(keylet);
newHookDef->setFieldH256(sfHookHash, *createHookHash);
newHookDef->setFieldH256(sfHookOn, *newHookOn);
newHookDef->setFieldH256(sfHookEmit, *newHookEmit);
newHookDef->setFieldH256(sfHookNamespace, *newNamespace);
newHookDef->setFieldArray(
sfHookParameters,
Expand Down Expand Up @@ -1677,6 +1718,7 @@ SetHook::setHook()
// change which definition we're using to the new target
defNamespace = newDefSLE->getFieldH256(sfHookNamespace);
defHookOn = newDefSLE->getFieldH256(sfHookOn);
defHookEmit = newDefSLE->getFieldH256(sfHookEmit);

// set the namespace if it differs from the definition namespace
if (newNamespace && *defNamespace != *newNamespace)
Expand All @@ -1686,6 +1728,10 @@ SetHook::setHook()
if (newHookOn && *defHookOn != *newHookOn)
newHook.setFieldH256(sfHookOn, *newHookOn);

// set the hookemit field if it differs from definition
if (newHookEmit && *defHookEmit != *newHookEmit)
newHook.setFieldH256(sfHookEmit, *newHookEmit);

// parameters
TER result = updateHookParameters(
ctx,
Expand Down Expand Up @@ -1734,8 +1780,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, sfHookEmit, sfHookApiVersion,
// sfFlags: free

// sfHookDefinition is not reserved because it is an unowned object,
// rather the uploader is billed via fee according to the following:
Expand Down
2 changes: 1 addition & 1 deletion src/ripple/app/tx/impl/SetHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class SetHook : public Transactor
calculateBaseFee(ReadView const& view, STTx const& tx);

static HookSetOperation
inferOperation(STObject const& hookSetObj);
inferOperation(SetHookCtx& ctx, STObject const& hookSetObj);

static HookSetValidation
validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj);
Expand Down
36 changes: 36 additions & 0 deletions src/ripple/app/tx/impl/Transactor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,17 @@ Transactor::executeHookChain(
if (!hook::canHook(ctx_.tx.getTxnType(), hookOn))
continue; // skip if it can't

// default allows all transaction types
uint256 defaultHookEmit = uint256();
defaultHookEmit ^= UINT256_BIT[ttHOOK_SET];

uint256 hookEmit =
(hookObj.isFieldPresent(sfHookEmit)
? hookObj.getFieldH256(sfHookEmit)
: hookDef->isFieldPresent(sfHookEmit)
? hookDef->getFieldH256(sfHookEmit)
: defaultHookEmit);

uint32_t flags =
(hookObj.isFieldPresent(sfFlags) ? hookObj.getFieldU32(sfFlags)
: hookDef->getFieldU32(sfFlags));
Expand Down Expand Up @@ -1247,6 +1258,7 @@ Transactor::executeHookChain(
results.push_back(hook::apply(
hookDef->getFieldH256(sfHookSetTxnID),
hookHash,
hookEmit,
ns,
hookDef->getFieldVL(sfCreateCode),
parameters,
Expand Down Expand Up @@ -1369,6 +1381,17 @@ Transactor::doHookCallback(
if (hookObj.getFieldH256(sfHookHash) != callbackHookHash)
continue;

// default allows all transaction types
uint256 defaultHookEmit = uint256();
defaultHookEmit ^= UINT256_BIT[ttHOOK_SET];

uint256 hookEmit =
(hookObj.isFieldPresent(sfHookEmit)
? hookObj.getFieldH256(sfHookEmit)
: hookDef->isFieldPresent(sfHookEmit)
? hookDef->getFieldH256(sfHookEmit)
: defaultHookEmit);

// fetch the namespace either from the hook object of, if absent, the
// hook def
uint256 const& ns =
Expand All @@ -1394,6 +1417,7 @@ Transactor::doHookCallback(
hook::HookResult callbackResult = hook::apply(
hookDef->getFieldH256(sfHookSetTxnID),
callbackHookHash,
hookEmit,
ns,
hookDef->getFieldVL(sfCreateCode),
parameters,
Expand Down Expand Up @@ -1639,6 +1663,17 @@ Transactor::doAgainAsWeak(
continue;
}

// default allows all transaction types
uint256 defaultHookEmit = uint256();
defaultHookEmit ^= UINT256_BIT[ttHOOK_SET];

uint256 hookEmit =
(hookObj.isFieldPresent(sfHookEmit)
? hookObj.getFieldH256(sfHookEmit)
: hookDef->isFieldPresent(sfHookEmit)
? hookDef->getFieldH256(sfHookEmit)
: defaultHookEmit);

// fetch the namespace either from the hook object of, if absent, the
// hook def
uint256 const& ns =
Expand All @@ -1659,6 +1694,7 @@ Transactor::doAgainAsWeak(
hook::HookResult aawResult = hook::apply(
hookDef->getFieldH256(sfHookSetTxnID),
hookHash,
hookEmit,
ns,
hookDef->getFieldVL(sfCreateCode),
parameters,
Expand Down
5 changes: 1 addition & 4 deletions src/ripple/nodestore/impl/Shard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -710,10 +710,7 @@ Shard::finalize(bool writeSQLite, std::optional<uint256> const& referenceHash)
if (writeSQLite && !storeSQLite(ledger))
return fail("failed storing to SQLite databases");

assert(
ledger->info().seq == ledgerSeq &&
(ledger->info().seq < XRP_LEDGER_EARLIEST_FEES ||
ledger->read(keylet::fees())));
assert(ledger->info().seq == ledgerSeq && ledger->read(keylet::fees()));

hash = ledger->info().parentHash;
next = std::move(ledger);
Expand Down
3 changes: 2 additions & 1 deletion src/ripple/protocol/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 74;
static constexpr std::size_t numFeatures = 75;

/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
Expand Down Expand Up @@ -362,6 +362,7 @@ extern uint256 const fix240819;
extern uint256 const fixPageCap;
extern uint256 const fix240911;
extern uint256 const fixFloatDivide;
extern uint256 const featureHookEmit;

} // namespace ripple

Expand Down
1 change: 1 addition & 0 deletions src/ripple/protocol/SField.h
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,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 sfHookEmit;
extern SF_UINT256 const sfPreviousTxnID;
extern SF_UINT256 const sfLedgerIndex;
extern SF_UINT256 const sfWalletLocator;
Expand Down
1 change: 1 addition & 0 deletions src/ripple/protocol/impl/Feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ REGISTER_FIX (fix240819, Supported::yes, VoteBehavior::De
REGISTER_FIX (fixPageCap, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix240911, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixFloatDivide, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(HookEmit, Supported::yes, VoteBehavior::DefaultNo);

// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.
Expand Down
Loading
Loading