Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
19e2036
HookAdministrator Amendment
tequdev Jan 5, 2026
90b009d
Merge remote-tracking branch 'upstream/dev' into HookAdministrator
tequdev Jan 5, 2026
07008da
Update sfcodes.h
tequdev Jan 5, 2026
0825bdd
Enhance new account creation at SetHook
tequdev Jan 10, 2026
e5b21f0
Merge pull request #692 from Xahau/sync-2.4.0-rebased
RichardAH Feb 24, 2026
65837f4
fix: Add AMMv1_3 amendment (#5203)
gregtatcam Jun 2, 2025
ec65e62
Merge fixAMMv1_3 amendment into featureAMM amendment
tequdev Feb 19, 2026
8673599
fixAMMClawbackRounding: adjust last holder's LPToken balance (#5513)
yinyiqian1 Jul 11, 2025
8cfee6c
Merge fixAMMClawbackRounding amendment into featureAMMClawback amendment
tequdev Feb 19, 2026
f96d9b6
Add tests for Hooks fee
tequdev Jan 20, 2026
1ba444a
Updated tests to align with the changes merged into the dev branch.
tequdev Feb 17, 2026
9bfca63
Update util_keylet fee test
tequdev Feb 24, 2026
2d29518
fix: typo `SignersListSet`
tequdev Mar 5, 2026
8c4c158
output ccache configuration in release-builder
tequdev Nov 27, 2025
f90ed41
enable ccache direct_mode
tequdev Nov 27, 2025
25123b3
chore: replace levelization shell script with python
sublimator Mar 13, 2026
4150f03
chore: use improved levelization script with threading and argparse
sublimator Mar 13, 2026
7f6ac75
Revert "chore: use improved levelization script with threading and ar…
sublimator Mar 13, 2026
66f7294
Test: hint build_test_hooks.sh when hook wasm is empty in hso()
tequdev Apr 1, 2026
05a3e04
Fix BEAST_ENHANCED_LOGGING not working and restore original behavior
tequdev Mar 6, 2026
cd00ed7
change build instructions url
alloynetworks Mar 13, 2026
8cfd5c3
Merge remote-tracking branch 'upstream/dev' into HookAdministrator
tequdev Apr 27, 2026
60bb552
clang-format
tequdev Apr 27, 2026
9ffd130
Merge branch 'dev' into HookAdministrator
tequdev Jun 9, 2026
b5fdae6
Update tx_flags.h
tequdev Jun 9, 2026
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
1 change: 1 addition & 0 deletions hook/sfcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@
#define sfLockingChainDoor ((8U << 16U) + 22U)
#define sfIssuingChainDoor ((8U << 16U) + 23U)
#define sfSubject ((8U << 16U) + 24U)
#define sfHookAdministrator ((8U << 16U) + 98U)
#define sfInform ((8U << 16U) + 99U)
#define sfIndexes ((19U << 16U) + 1U)
#define sfHashes ((19U << 16U) + 2U)
Expand Down
4 changes: 4 additions & 0 deletions hook/tx_flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ enum NFTokenCreateOfferFlags : uint32_t {
tfSellNFToken = 0x00000001,
};

enum SetHookFlags : uint32_t {
tfNewAccount = 0x00000001,
};

enum ClaimRewardFlags : uint32_t {
tfOptOut = 0x00000001,
};
Expand Down
5 changes: 5 additions & 0 deletions include/xrpl/protocol/TxFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ constexpr std::uint32_t const tfNFTokenCancelOfferMask = ~tfUniversal;
// NFTokenAcceptOffer flags:
constexpr std::uint32_t const tfNFTokenAcceptOfferMask = ~tfUniversal;

enum SetHookFlags : uint32_t {
tfNewAccount = 0x00000001,
};
constexpr std::uint32_t const tfSetHookMask = ~(tfUniversal | tfNewAccount);

// URIToken mask
constexpr std::uint32_t const tfURITokenMintMask = ~(tfUniversal | tfBurnable);
constexpr std::uint32_t const tfURITokenNonMintMask = ~tfUniversal;
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/features.macro
Original file line number Diff line number Diff line change
Expand Up @@ -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(HookAdministrator, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (GuardDepth32, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(NamedHooks, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(IOURewardClaim, Supported::yes, VoteBehavior::DefaultNo)
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/ledger_entries.macro
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({
{sfHookStateScale, soeOPTIONAL},
{sfCron, soeOPTIONAL},
{sfAMMID, soeOPTIONAL},
{sfHookAdministrator, soeOPTIONAL},
}))

/** A ledger object which contains a list of object identifiers.
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/sfields.macro
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ TYPED_SFIELD(sfAttestationRewardAccount, ACCOUNT, 21)
TYPED_SFIELD(sfLockingChainDoor, ACCOUNT, 22)
TYPED_SFIELD(sfIssuingChainDoor, ACCOUNT, 23)
TYPED_SFIELD(sfSubject, ACCOUNT, 24)
TYPED_SFIELD(sfHookAdministrator, ACCOUNT, 98)
TYPED_SFIELD(sfInform, ACCOUNT, 99)

// vector of 256-bit
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/transactions.macro
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, ({
/** This transaction type installs a hook. */
TRANSACTION(ttHOOK_SET, 22, SetHook, ({
{sfHooks, soeREQUIRED},
{sfDestination, soeOPTIONAL},
}))

/** This transaction mints a new NFT. */
Expand Down
3 changes: 3 additions & 0 deletions src/xrpld/app/tx/detail/DeleteAccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@
if (sleAccount->isFieldPresent(sfHookNamespaces) ||
sleAccount->isFieldPresent(sfHooks))
return tecHAS_OBLIGATIONS;

if (sleAccount->isFieldPresent(sfHookAdministrator))
return tecHAS_OBLIGATIONS;

Check warning on line 335 in src/xrpld/app/tx/detail/DeleteAccount.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/DeleteAccount.cpp#L335

Added line #L335 was not covered by tests
}

// When fixNFTokenRemint is enabled, we don't allow an account to be
Expand Down
4 changes: 3 additions & 1 deletion src/xrpld/app/tx/detail/InvariantCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,9 @@ ValidNewAccountRoot::finalize(
if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT ||
tt == ttREMIT || tt == ttAMM_CREATE ||
tt == ttXCHAIN_ADD_CLAIM_ATTESTATION ||
tt == ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION) &&
tt == ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION ||
(tt == ttHOOK_SET &&
view.rules().enabled(featureHookAdministrator))) &&
isTesSuccess(result))
{
std::uint32_t const startingSeq{
Expand Down
132 changes: 128 additions & 4 deletions src/xrpld/app/tx/detail/SetHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,21 @@
TER
SetHook::preclaim(ripple::PreclaimContext const& ctx)
{
if (ctx.tx.isFieldPresent(sfHookAdministrator))
{
auto const& administrator = ctx.tx.getAccountID(sfHookAdministrator);
auto const& sle = ctx.view.read(keylet::account(administrator));
if (!sle)
return tecNO_DST;

Check warning on line 712 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L712

Added line #L712 was not covered by tests

if (!sle->isFieldPresent(sfHookAdministrator))
return tecNO_PERMISSION;

Check warning on line 715 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L715

Added line #L715 was not covered by tests

if (sle->getAccountID(sfHookAdministrator) !=
ctx.tx.getAccountID(sfAccount))
return tecNO_PERMISSION;

Check warning on line 719 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L719

Added line #L719 was not covered by tests
}

auto const& hookSets = ctx.tx.getFieldArray(sfHooks);

for (auto const& hookSetObj : hookSets)
Expand Down Expand Up @@ -743,12 +758,46 @@
return ret;

if (ctx.rules.enabled(fixInvalidTxFlags) &&
ctx.tx.getFlags() & tfUniversalMask)
ctx.tx.getFlags() & tfSetHookMask)
{
JLOG(ctx.j.trace()) << "SetHook: Invalid flags set.";
return temINVALID_FLAG;
}

if (ctx.tx.isFlag(tfNewAccount) &&
!ctx.rules.enabled(featureHookAdministrator))
{
JLOG(ctx.j.trace()) << "SetHook: New account flag set but hook "
"administrator amendment is not enabled.";
return temDISABLED;

Check warning on line 772 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L772

Added line #L772 was not covered by tests
}

if (ctx.tx.isFieldPresent(sfDestination))
{
if (!ctx.rules.enabled(featureHookAdministrator))
{
JLOG(ctx.j.trace())
<< "HookSet: Hook administrator amendment not enabled.";
return temDISABLED;

Check warning on line 781 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L781

Added line #L781 was not covered by tests
}

if (ctx.tx.isFlag(tfNewAccount))
{
JLOG(ctx.j.trace())
<< "HookSet: Both new account flag and destination set. "
"New account flag and destination cannot be set at the same "
"time.";
return temMALFORMED;

Check warning on line 790 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L790

Added line #L790 was not covered by tests
}

if (ctx.tx.getAccountID(sfDestination) ==
ctx.tx.getAccountID(sfAccount))
{
JLOG(ctx.j.trace()) << "HookSet: Redundant hook administrator.";
return temREDUNDANT;

Check warning on line 797 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L797

Added line #L797 was not covered by tests
}
}

if (!ctx.tx.isFieldPresent(sfHooks))
{
JLOG(ctx.j.trace())
Expand Down Expand Up @@ -1288,6 +1337,23 @@
}
};

AccountID
randomAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey)
{
// This number must not be changed without an amendment
constexpr std::uint16_t maxAccountAttempts = 256;
for (std::uint16_t i = 0; i < maxAccountAttempts; ++i)
{
ripesha_hasher rsh;
auto const hash = sha512Half(i, view.info().parentHash, pseudoOwnerKey);
rsh(hash.data(), hash.size());
AccountID const ret{static_cast<ripesha_hasher::result_type>(rsh)};
if (!view.read(keylet::account(ret)))
return ret;
}
return beast::zero;

Check warning on line 1354 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L1354

Added line #L1354 was not covered by tests
}

TER
SetHook::setHook()
{
Expand All @@ -1307,11 +1373,69 @@
.app = ctx_.app,
.rules = ctx_.view().rules()};

auto targetAccount = ctx.tx[~sfDestination].value_or(account_);
if (ctx_.tx.isFlag(tfNewAccount))
{
// create the new account
auto const newAccount = randomAccountAddress(ctx_.view(), uint256{});
if (newAccount == beast::zero)
return tecDUPLICATE;

Check warning on line 1382 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L1382

Added line #L1382 was not covered by tests

auto sleNewAccount = std::make_shared<SLE>(keylet::account(newAccount));
sleNewAccount->setAccountID(sfAccount, newAccount);
sleNewAccount->setFieldAmount(sfBalance, STAmount{});
sleNewAccount->setFieldU32(sfOwnerCount, 1); // ltHook
std::uint32_t const seqno{
ctx_.view().rules().enabled(featureXahauGenesis)
? ctx_.view().info().parentCloseTime.time_since_epoch().count()
: ctx_.view().rules().enabled(featureDeletableAccounts)
? ctx_.view().seq()
: 1};
sleNewAccount->setFieldU32(sfSequence, seqno);
sleNewAccount->setFieldU32(sfFlags, lsfDisableMaster);

sleNewAccount->setAccountID(sfHookAdministrator, account_);

auto sleFees = view().peek(keylet::fees());
if (sleFees && view().rules().enabled(featureXahauGenesis))
{
auto actIdx = sleFees->isFieldPresent(sfAccountCount)
? sleFees->getFieldU64(sfAccountCount)
: 0;
sleNewAccount->setFieldU64(sfAccountIndex, actIdx);
sleFees->setFieldU64(sfAccountCount, actIdx + 1);
view().update(sleFees);
}

// fund AccountReserve + ObjectReserve (ltHook)
auto const requiredDrops = ctx_.view().fees().accountReserve(1);

auto sourceSle = ctx_.view().peek(keylet::account(account_));
if (!sourceSle)
return tefINTERNAL;

Check warning on line 1415 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L1415

Added line #L1415 was not covered by tests

auto const sourceCurrentReserve = ctx_.view().fees().accountReserve(
sourceSle->getFieldU32(sfOwnerCount));

auto const sourceBalance = sourceSle->getFieldAmount(sfBalance).xrp();

if (sourceBalance < sourceCurrentReserve + requiredDrops)
return tecUNFUNDED;

Check warning on line 1423 in src/xrpld/app/tx/detail/SetHook.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/SetHook.cpp#L1423

Added line #L1423 was not covered by tests

sourceSle->setFieldAmount(sfBalance, sourceBalance - requiredDrops);
ctx_.view().update(sourceSle);

sleNewAccount->setFieldAmount(sfBalance, requiredDrops);
ctx_.view().insert(sleNewAccount);

targetAccount = newAccount;
}

const int blobMax = hook::maxHookWasmSize();
auto const accountKeylet = keylet::account(account_);
auto const hookKeylet = keylet::hook(account_);

auto accountSLE = view().peek(accountKeylet);
auto const hookKeylet = keylet::hook(targetAccount);

auto accountSLE = view().peek(keylet::account(targetAccount));

ripple::STArray newHooks{sfHooks, 8};
auto newHookSLE = std::make_shared<SLE>(hookKeylet);
Expand Down
Loading