feat(node): tipping subsystem — owner lookup + auto-tip engine + $DIG spend + tip ledger (#378)#25
Merged
Merged
Conversation
f3a1d9d to
2732cfa
Compare
… spend + tip ledger (#378)
Node-side tipping subsystem (#378, child of the auto-tip epic #377): the node
resolves a store's on-chain owner, runs the auto-tip policy engine (hard caps +
crash-safe idempotency, all fail-closed), builds+signs+broadcasts the $DIG CAT
tip via the canonical Cat::spend_all path, records a persisted tip ledger, and
exposes tip.* over the WS wallet/control transport with a {type:"tip"} push.
Creator auto-tip is default-on (real on-chain recipient); the DIG dev-account
daily tip is default-off and REFUSES while DIG_DEV_ACCOUNT_PH is the placeholder.
No live mainnet broadcast in tests (simulator + MockBroadcaster).
Refs #377
Closes #378
Co-Authored-By: Claude <noreply@anthropic.com>
5b778a0 to
94c7ef9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
Node-side tipping subsystem (#378, child of the auto-tip epic #377): the dig-node resolves a store's on-chain owner, runs an auto-tip policy engine (hard caps + crash-safe idempotency + fail-closed-on-unreadable-state, all fail-closed), builds+signs+broadcasts the $DIG CAT tip via the canonical
Cat::spend_allpath (never hand-rolled CLVM), records a persisted tip ledger (durable fsync writes), and exposestip.*over the WS wallet/control transport (§4.8) with a{type:"tip"}push. Both creator and dev-account tips are default-ON — the dev tip pays the existing canonical DIG treasury shared contract (digstore_chain::dig::treasury_inner_puzzle_hash(), the per-capsule-payment recipient), sourced not re-hardcoded. No live mainnet broadcast in tests (simulator +MockBroadcaster).Draft — not for merge until re-verified. Extension Tip tab/toggle (#379/#380) is a separate later branch.
What changed
crates/dig-wallet/src/sage/tipping.rs(new) — the engine + seams:TippingConfig/AutoTipPolicy/TipMode(persistedtipping-config.json; creator + dev both default-ON).dig_treasury_ph_hex()→digstore_chain::dig::treasury_inner_puzzle_hash()(the shared-contract treasury inner PHec7c…6ce8; sourced, never re-hardcoded — no 4th copy to drift).OwnerResolverseam +ChainOwnerResolver(reusesdigstore_chain::singleton::sync_datastore(...).info.owner_puzzle_hash, cached per store).TipSpenderseam +NodeTipSpender.TippingEngine:auto_tip_for_store/dev_daily_tip/manual_tip/get_config/set_config/get_ledger;TipEventBusfor the WS push.crates/dig-wallet/src/sage/rpc.rs—WalletBackend: tipping wiring;build_and_broadcast_dig_tip($DIG CAT viaspend::build_cat_send,dig-clvmvalidate, node-custody sign, injected broadcaster);tip.*dispatch.crates/dig-node-service/src/wallet_authz.rs— tip mutations paired-token gated (§7.12); reads open.crates/dig-node-service/src/server.rs—/wsforwardsTipEventas{type:"tip"}on a dedicated bus.crates/dig-wallet/src/sage/service.rs— offline-safe bring-up attaches the engine.SPEC.md— §18.23 (tipping) + §4.8{type:"tip"}frame.0.20.0 → 0.21.0(workspace/binary) + dig-wallet0.7.0 → 0.8.0— additivefeat.Money-safety design (real mainnet $DIG)
Pending) +Confirmed+ ambiguous-Failedall count → no double-count into over-spend. Over-cap → skip.Pendingledger reservation is persisted immediately before the broadcast (the only money-moving step). A crash leaves ≤1 reserved entry per(kind, site, UTC-day); on restart the reloaded engine treats it as already-tipped and skips — never a double-spend. Pre-broadcastNotExecutablerolls back (retryable); an ambiguous broadcast error keepsFailed(never retried that day).state-unreadable) until resolved, so a corrupt/locked/truncated ledger can NEVER reset caps+idempotency to "empty → tip freely". A present-but-unreadable config fails closed to DISABLED (never silently re-enables a disabled auto-tip).unwrap_or_default()on the persisted read is gone.fsync→ atomicrename→ parent-dirfsync(best-effort), so a crash can't leave a truncated ledger.MockSpender; the $DIG CAT spend build tested on thechia-sdk-testsimulator +MockBroadcaster. The shipped node ships no broadcaster yet (offline-safe bring-up) so a tip cleanly reportsNotExecutable/wallet-unavailableuntil the wallet spend path's live sync/lineage lands (§18.12).How verified
cargo test -p dig-wallet→ 246 + 22 passed, 0 failed — incl. the money-safety regressions: corrupt/truncated ledger → fail-closed no re-tip; corrupt ledger after a real tip → no double-spend; corrupt config → not re-enabled; absent files → clean first run still tips; durable reload; per-site + shared daily caps; once-per-site/day idempotency; crash-retry; dev tip pays the treasury once/day; NotExecutable rollback; ambiguous-not-retried; manual bypasses caps/idempotency.dig_tip_cat_send_records_via_mock_broadcaster_never_sends— the $DIG CAT tip builds+validates(dig-clvm)+signs, records viaMockBroadcaster(not sent), and the same bundle is accepted by the real simulator.cargo clippy --all-targets --all-features -- -D warningsclean;cargo fmt --all --checkclean. Fullgh pr checksstate reported on the PR.Blast radius
Additive: new
tippingmodule + additiveWalletBackendmethods/fields + atip.*dispatch prefix + aGATED_WALLET_MUTATIONSextension + one/wsselect arm. No existing method / wire shape / Sage-parity surface changed (SUPPORTED_METHODSunchanged → Sage OpenAPI conformance untouched; tip events use a separate bus soGET /eventsis unchanged).Recipients — canonical shared contracts (no new constants)
digstore_chain::dig::treasury_inner_puzzle_hash()), the same value that receives per-capsule payments. No placeholder, no pending user input.Refs #377 · Closes #378