Skip to content

Flags (on the sfHook object) ​

Purpose: a 32-bit bitfield inside each sfHook entry carrying SetHook-specific control bits (hsoDELETE/hsoNSDELETE requirements, hsoINSTALL/hsoCREATE override permission) and one runtime bit, hsfCOLLECT, that persists onto the ledger and gates weak-execution hook chains. This is a different field from the transaction's own top-level sfFlags — see the warning below.

Field declaration: UINT32, code 2.

sfFlags is a generic, protocol-wide field, not hook-specific — it is declared once and reused as a common field on every transaction type and (automatically) on every ledger entry type. It is soeOPTIONAL on sfHook and is not listed explicitly in ltHOOK_DEFINITION's template because sfFlags is one of the fields auto-attached to every LEDGER_ENTRY (alongside sfLedgerIndex, sfLedgerEntryType, sfRemarks) — yet it is still legally stored and read on a ltHOOK_DEFINITION SLE (see below).

Don't confuse this with the transaction-level sfFlags. A SetHook transaction, like every transaction, carries its own top-level sfFlags (the standard signing/canonical-signature bits). That field is checked independently, unrelated to any sfHook object's flags, and gated by fixInvalidTxFlags.

SetHook defines no tfXxx transaction flags of its own, so this simply rejects any bit outside the universal mask (e.g. tfFullyCanonicalSig) on the transaction itself. Everything else on this page is about the per-sfHook-object sfFlags, which uses an entirely separate bit namespace (hsf*, below).

Hook-set flag values — HookSetFlags:

FlagValueMeaning
hsfOVERRIDE0x1Permission bit: this operation may replace/delete a hash already occupying the chain slot.
hsfNSDELETE0x2Request bit: destroy a namespace's Hook State as part of this operation (see HookNamespace for full mechanics, including its use as a side effect on non-hsoNSDELETE operations).
hsfCOLLECT0x4Persistent bit: marks this hook eligible to run during weak (non-strong) "collect" execution.

Per-operation legality:

  • hsoNSDELETE: sfFlags required, and must equal exactly hsfNSDELETE — no other bit, including hsfOVERRIDE or hsfCOLLECT, may accompany it (NSDELETE_FLAGS).
  • hsoDELETE: sfFlags required and must include hsfOVERRIDE (OVERRIDE_MISSING otherwise); allowed bits are limited to hsfOVERRIDE | hsfNSDELETE | hsfCOLLECT — any other bit is FLAGS_INVALID. A missing flag and an all-1s 2147483648 value are both rejected.
  • hsoUPDATE: hsfOVERRIDE is forbidden outright; hsfNSDELETE is allowed only together with a newly supplied sfHookNamespace in the same entry (both conditions checked in one guard, FLAGS_INVALID). An override flag on update and NSDELETE without a namespace are both rejected.
  • hsoINSTALL/hsoCREATE: validation places no explicit restriction on sfFlags for these two — but hsfOVERRIDE is enforced separately, at apply time, as the permission to replace whatever currently occupies the target chain slot: tecREQUIRES_FLAG if a hook already exists there and hsfOVERRIDE is absent. An install without override and a second create without override both produce tecREQUIRES_FLAG.

What actually gets persisted — control bits are stripped before storage on hsoCREATE/hsoINSTALL; hsoUPDATE stores the raw value, but validation makes the two control bits unreachable there anyway. Control-only bits are removed before storage.

This stripped value (effectively just hsfCOLLECT plus any undefined higher bits) is what gets written to the ledger for hsoCREATE's new ltHOOK_DEFINITION and for hsoINSTALL's per-account entry. hsoUPDATE instead stores the raw, unstripped submitted value. This looks like it could leave a stray hsfOVERRIDE/hsfNSDELETE bit on the ledger, but operation inference and the hsoUPDATE validation guard prevent that in a transaction that actually validates: operation inference only returns hsoUPDATE when either sfHookNamespace is absent or hsfNSDELETE is unset (any (Namespace present, hsfNSDELETE set) pair is classified hsoNSDELETE instead), and hsoUPDATE's own validation rejects flags & hsfNSDELETE whenever sfHookNamespace is absent. The only namespace/flag combination that reaches hsoUPDATE validation with hsfNSDELETE set is therefore exactly the one the guard rejects — so hsfNSDELETE can never survive into an applied hsoUPDATE, and hsfOVERRIDE is rejected outright regardless of sfHookNamespace. Raw-vs-stripped storage is a real difference in the code, but it has no observable effect for hsoUPDATE in practice, since validation already guarantees both control bits are absent by the time the apply loop runs.

Runtime use — hsfCOLLECT gates weak execution. Both fee calculation and actual execution read a hook chain entry's stored sfFlags (falling back to the definition's sfFlags if the entry has none) and skip non-strong ("weak"/collect-only) invocations that lack hsfCOLLECT.

The fee-estimation counterpart is identical in spirit, gating whether a hook's fee is even added when only "collect calls" are being priced. This is the mechanism that lets a hook opt in to firing during weakly-executed (TSH/collect) transaction processing, as referenced from HookOn's execution-mode discussion.

Common mistakes:

  • Setting the transaction's own top-level Flags field expecting it to control hook behavior — hsf* bits only exist inside each sfHook object's own Flags sub-field.
  • Assuming hsoINSTALL/hsoCREATE forbid arbitrary flag bits the way hsoDELETE/hsoNSDELETE do. They don't restrict the value at validation time — the only enforcement is hsfOVERRIDE's apply-time permission check.
  • Assuming hsoUPDATE's raw (unstripped) flag storage means hsfNSDELETE can end up persisted on a hook entry. Validation guarantees it can't: operation inference reclassifies any (Namespace present, hsfNSDELETE set) pair as hsoNSDELETE, and hsoUPDATE's own guard rejects the only remaining case (hsfNSDELETE set with sfHookNamespace absent).
  • Forgetting hsoNSDELETE rejects any extra bit, including hsfOVERRIDE/hsfCOLLECT — it must be exactly hsfNSDELETE, not merely "include" it.
  • HookNamespace — full hsfNSDELETE deletion mechanics, including its side-effect behavior on non-hsoNSDELETE operations.
  • HookOn — the strong/weak execution model hsfCOLLECT plugs into.
  • Operation Matrix — full per-operation field/flag matrix.