Closes luxfi/node#115 by landing the admission-hook half of the issue.
The hook is the substrate the encrypted-mempool partition will use to
admit FHE-ciphertext transactions on (signature + fee + NIZK) without
decryption per LP-066 / luxfi/precompile/fhe.
Surface added:
type AdmissionVerifier[T Tx] interface {
VerifyAdmit(tx T) error
}
var ErrAdmissionRejected = errors.New("tx admission rejected")
func NewWithAdmissionVerifier[T Tx](
metrics Metrics,
verifier AdmissionVerifier[T],
) *mempool[T]
// New is now a thin wrapper around NewWithAdmissionVerifier(metrics, nil).
// No behavioral change for existing callers.
Add() invokes verifier.VerifyAdmit last, after duplicate / size / space /
conflict have all passed. Ordering rationale: cheap rejects fire first,
so NIZK verification cost is only paid on a tx that passes structural
admission. A non-nil verifier return is wrapped in ErrAdmissionRejected,
records the drop reason via the existing droppedTxIDs LRU, and never
inserts the tx.
What this does NOT do:
- It does not define the encrypted-payload tx type or its NIZK proof
format. Those live in vms/platformvm/txs (or wherever the consumer
decides) and ship in a follow-up.
- It does not wire the FHE precompile's bootstrap-meter consultation —
callers implementing AdmissionVerifier do that themselves.
- It does not add the per-account FIFO partition for encrypted txs.
The existing unissuedTxs Hashmap is per-mempool; the partition is
a separate construction over multiple mempool instances and is a
follow-up.
What it DOES do: lands the only mempool-side hook the issue requires
without locking us in on the encrypted-tx representation. The hook is
generic (`AdmissionVerifier[T Tx]`) so any future Tx variant can plug
in the same way.
Tests (CGO_ENABLED=0 go test -run TestAdmissionVerifier ./vms/txs/mempool/...
all pass):
- nil verifier matches New (byte-identical behavior)
- verifier returning nil admits the tx; gate fires exactly once on
Add and not on Get / Peek / Iterate / Remove
- verifier returning an error rejects with ErrAdmissionRejected
wrapping the verifier's reason; drop reason recorded
- cheap checks short-circuit before the gate runs (duplicate /
oversize / conflict all skip the gate)
Refs:
- issue #115 (this repo)
- LP-066 (F-Chain confidential compute)
- LP-183 (ZAP envelope decode precompile; downstream consumer once
encrypted-payload tx propagation through gossip lands)
- luxfi/threshold issue #20 (real distributed FHE decryption; the
block-proposer-side counterpart to this admission-side hook)
The github.com/luxfi/node/errors package shipped with zero tests despite
being a public utility surface (sentinel errors, WrappedError context
chain, Multi/Join aggregation, retry helpers). This adds 22 tests covering:
- WrappedError.Error format (with and without message)
- Wrap/WrapWithContext nil pass-through and context preservation
- errors.Is / errors.As chain through Wrap (incl. double-wrap)
- IsNotFound, IsClosed, IsTimeout helpers
- IsTemporary against both sentinel errors and the Temporary() interface
- IsPermanent across all permanent sentinels + fmt.Errorf %w chains
- GetCategory: wrapped category, sentinel inference, unknown fallback,
and the precedence rule (wrapped category beats inferred category)
- Multi.Error/Add/Err for 0, 1, and N errors; nil-add ignored
- Join nil pass-through and multi-error combination
Result: 100.0% statement coverage, race-clean.
README badge and prerequisites listed Go 1.21.12 / 1.23.9 in different
places, but go.mod requires Go 1.26.3. Bring the docs in sync so new
contributors install a toolchain that can actually build the node.