feat(payouts): confirm payouts on-chain with a view-only wallet (#381)#471
Merged
Conversation
Add optional on-chain payout confirmation: when the operator supplies the private VIEW key for monero.wallet_address, the stack runs a view-only monero-wallet-rpc against the LOCAL node (scan-only, cannot spend) and the dashboard confirms P2Pool coinbase payouts actually landed. - build/monero: extract monero-wallet-rpc from the already-verified CLI tarball (no new image/pin); new wallet-entrypoint.sh (create-from-view-key, key via tmpfs file never argv) + wallet-healthcheck.sh. - docker-compose: new wallet-rpc service on the payout_confirm profile, non-root uid 1000, read-only rootfs + tmpfs, loopback-only publish 127.0.0.1:18082, gated on the view key + local mode, depends on monerod healthy. - pithead: validate/render monero.view_key (secret, handled like node_password; refused on a remote node; 64-hex checked) + payout_scan_height; generate + preserve the wallet-rpc login; add the payout_confirm profile; mask view_key in the control config; keep the secrets off the #33 editable allowlist. - dashboard: MoneroWalletClient polls get_transfers on the 10th-poll cadence; new payouts table keyed (chain, txid) so the Tari sibling (#462) reuses it; confirmed 24h/7d/all-time totals beside the earnings estimate; one payout_confirmed alert per new txid (idempotent storage => no replay on restart; coinbase recorded on confirmation, not maturity). - docs: configuration.md entries, a Payout confirmation section in dashboard.md stating the full view-key security model, CHANGELOG under [Unreleased]. - tests: wallet client parsing + payouts round-trip/idempotency + alert edge (tier 1); fake wallet-rpc contract test (tier 2); config->.env render + view-key-never-leaked (tier-1 shell). Closes #381 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…yout-confirm-381 # Conflicts: # CHANGELOG.md
- gitleaks: generalize the curl-auth-user allowlist to ${VAR:-default} env
expansions (wallet-healthcheck.sh uses ${WALLET_RPC_USERNAME:-wallet}); the
matched 'secret' is shell interpolation, not a hardcoded credential.
- shfmt: reindent wallet-entrypoint.sh + wallet-healthcheck.sh to the repo -i 4.
- wallet client: skip a transfer whose amount/height/timestamp is non-numeric or
out of signed-64 range instead of aborting the whole scan (would stall payout
confirmation by never advancing min_height); the 64-bit bound also keeps the
add_payouts INTEGER insert from OverflowError. + test for the skip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Adds optional on-chain payout confirmation (v1.5). Every earnings figure the dashboard shows is an estimate; nothing checked that a P2Pool payout actually landed. Now, when the operator sets
monero.view_key(the private view key for their payout address), the stack runs a view-onlymonero-wallet-rpcagainst the local node (scan-only — a view key cannot spend) and the dashboard confirms payouts from the chain (the coinbase outputs P2Pool pays you are the only ground truth).monero-wallet-rpcextracted from the already-verified CLI tarball (no new image/pin). Newwallet-rpccompose service on apayout_confirmprofile — gated on the view key being set + local mode, non-root (uid 1000), read-only rootfs + tmpfs, loopback-only publish127.0.0.1:18082, depends on monerod healthy. The view key reaches it via env + a tmpfs file, never the command line.monero.view_key(refused on a remote node; 64-hex checked) +monero.payout_scan_height; generates & preserves the wallet-rpc login; masksview_keyin the control config; keeps the secrets off the Stack config editor: change any setting from the dashboard, applied via pithead (incl. P2Pool mode hot-swap) #33 dashboard-editable allowlist (named deliberately in the comment).MoneroWalletClientpollsget_transferson the slow (~5 min) cadence; newpayoutstable keyed(chain, txid)so the Tari sibling Confirm Tari payouts on-chain with a view-only minotari wallet (sibling of #381) #462 reuses this exact table withchain="tari"(this feature setschain="monero"); confirmed 24h/7d/all-time totals beside the estimate; onepayout_confirmedalert per new payout (idempotent storage + height seeding ⇒ no replay on restart; coinbase recorded on confirmation, not maturity).configuration.mdentries; a "Payout confirmation" section indashboard.mdstating the full view-key security model; CHANGELOG under[Unreleased].Security model
config.json/.env. Handled exactly likenode_password: owner-only.env, never logged/echoed, off the dashboard config editor, never on a container command line (red-then-green no-leak test).monero.mode: remoteis refused).payouts table schema
payouts (chain TEXT, txid TEXT, height INTEGER, ts REAL, amount_atomic INTEGER, PRIMARY KEY (chain, txid))— shared with #462.Tests
make lint+make testgreen. Dashboard coverage 96.7% (≥80); patch coverage 97–98% (≥90). New: wallet client parsing + payouts round-trip/idempotency + alert edge (tier 1), fake wallet-rpc contract test (tier 2), config→.env render + view-key-never-leaked (tier-1 shell).Closes #381
🤖 Generated with Claude Code