First deposit integration slice

This commit is contained in:
2026-10-21 20:07:27 -04:00
parent e3b2261da3
commit af646dcc32
10 changed files with 223 additions and 46 deletions
+3 -6
View File
@@ -15,13 +15,10 @@ The operator has authorized proceeding with implementation instead of the previo
- [Custody UI](../src/CustodyPanel.tsx): marketplace “Custody preview” tab with reviewed signatures, private/paged balances and activity, test funding, listing/reservation and withdrawal-hold cancellation. A lost response retains the original signed envelope for replay rather than creating a new debit nonce.
- HTTP endpoints `/api/custody` and `/api/custody/command` are off by default; explicit `WIKIDECK_CUSTODY_MODE=sandbox` enables testnet only. All other enabled modes and mainnet sandbox are rejected before node resources open. Requests use the node's origin/admission/body limits, per-route mutation throttling and sanitized errors. No observer/admin settlement or balance-credit endpoint exists.
- [Stopped-node recovery](../server/recovery.ts) includes the optional custody sandbox database and its identity while continuing to accept existing two-database backups. Financial receipts are not pruned. Restore/startup checks do not substitute for production reconciliation.
- Offline SQLite fixture methods exercise fee allocation and prepared-withdrawal identity/retry/cancellation boundaries. They never access Monero and are not callable through HTTP. Public reservations stop at `awaiting-lock`; there is **no automatic NFT delivery or settled sale** in this increment. No real-address withdrawal, deposit subaddress, wallet RPC, signer, author payout or reorg compensation is implemented yet.
- Offline SQLite fixture methods exercise fee allocation and prepared-withdrawal identity/retry/cancellation boundaries. They never access Monero and are not callable through HTTP. Public reservations stop at `awaiting-lock`; there is **no automatic NFT delivery or settled sale** in this increment. Monero address allocation and read-only observation groundwork now exist, but no deposit scan worker, customer credit, live withdrawal, signer, author payout or reorg compensation is wired or enabled.
- **Financial storage update:** [ADR 003](postgres-financial-storage.md) selects PostgreSQL and records the integrated asynchronous custody backend, restricted runtime/schema roles, deferred accounting constraints, explicit SQLite freeze/import, and full dump/restore rehearsal. SQLite remains an opt-in-compatible local sandbox default, not an automatic fallback. Real custody and production operating acceptance remain blocked.
**Financial storage update:** [ADR 003](postgres-financial-storage.md) selects PostgreSQL and records the integrated asynchronous custody backend, restricted runtime/schema roles, deferred accounting constraints, explicit SQLite freeze/import, and full dump/restore rehearsal. SQLite remains an opt-in-compatible local sandbox default, not an automatic fallback. Real custody and production operating acceptance remain blocked.
**Monero observation and deposit-address allocation groundwork:** [server/market/monero-observer.ts](../server/market/monero-observer.ts) keeps the existing observer client strictly read-only: mainnet wallet validation and bounded incoming scans, authenticated requests, strict HTTPS/literal-loopback-only HTTP, no redirects, bounded response/deadline, JSON-RPC ID validation and lossless integer handling. A separate, narrow `createMoneroDepositAddressRpc()` capability verifies a mainnet primary address with `get_address` plus `validate_address`, then derives a domain-separated fingerprint. It can inspect one indexed subaddress or request exactly one labeled subaddress; it exposes no transfer, relay, sweep or withdrawal operation. Actual testing against the verified Monero v0.18.5.4 Wallet RPC found that `get_wallet_info` returns JSON-RPC “Method not found” despite appearing in current docs; the implementation therefore does not depend on it. The allocator [server/market/monero-deposit-allocation-store.ts](../server/market/monero-deposit-allocation-store.ts) combines PostgreSQL v3 beneficiary/index reservations with this capability: it reserves first, serializes cooperating allocators per wallet/account, checks that the live wallet fingerprint matches the durable generation, checks the exact reserved index, and recovers a lost `create_address` response by looking up the deterministic allocation label before retrying creation. Actual v0.18.5.4 testing also confirmed that an unused address index returns RPC error `-15` (“address index is out of bound”), rather than an empty `addresses` array; the client recognizes only that exact error as “not created” and fails closed for other errors. Address registration is immutable and performed only after the wallet returns the expected index/address/label. The installed v0.18.5.4 interface does not provide a supported `get_wallet_info` query to attest the wallet type, so watch-only provisioning is an operator/deployment requirement, not a property this RPC client can prove. If another wallet client consumes indexes outside this allocator, the operation fails for reconciliation rather than reassigning an address; the wallet RPC must therefore be dedicated to the allocator. The configured wallet-generation UUID still requires controlled operator enrollment and backup. Neither client nor orchestration is wired to startup, routes, UI, deposit scans or financial posting. The registry remains inaccessible to both custody API and observer roles. Observation storage continues to record reorg/reappearance evidence only; no credit, debit, reversal or journal can be posted. Schema v1→v3 is explicit and additive. The test-target build succeeded; **187/187** offline tests and **35/35** disposable PostgreSQL tests passed, including rejection of wallet identity mismatch and simulated create-success/response-loss/recovery without duplicate address creation or journal posting. The mTLS gateway method allowlist and config guard have unit coverage; an end-to-end certificate handshake remains open.
**Security claim:** operator-custodied XMR, seller-authorized and host-attested NFT sale completion, with durable accounting and recoverable coordination. Not trustless, not an atomic Monero/NFT swap, not insured, and not an independently consensus-verified Monero payment. The operator can steal, censor, become insolvent or lose keys; segregation and review reduce but do not remove that risk.
**Monero observation and deposit-address allocation groundwork:** [server/market/monero-observer.ts](../server/market/monero-observer.ts) keeps the observer strictly read-only, with bounded requests, strict HTTPS/literal-loopback-only HTTP, no redirects, bounded responses/deadlines, JSON-RPC ID validation and lossless integer handling. A separate, narrow `createMoneroDepositAddressRpc()` capability verifies a mainnet primary address with `get_address` plus `validate_address`, then derives a domain-separated fingerprint. It can inspect one indexed subaddress or request exactly one labeled subaddress; it exposes no transfer, relay, sweep or withdrawal operation. The allocator [server/market/monero-deposit-allocation-store.ts](../server/market/monero-deposit-allocation-store.ts) uses PostgreSQL v3 beneficiary/index reservations: it reserves first, serializes cooperating allocators per wallet/account, checks the live wallet fingerprint and exact reserved index, and recovers a lost `create_address` response by looking up the deterministic label before retrying. Actual v0.18.5.4 testing confirmed `get_wallet_info` is unavailable and an unused address index returns RPC error `-15` (“address index is out of bound”); the client recognizes only that precise unused-index response. Watch-only provisioning remains a deployment requirement. PostgreSQL v4 now records a per-output public key and global index while preserving historical observations without a creditable output identity. The observer joins transaction-level `get_transfers` metadata to bounded `incoming_transfers` output records and identifies each output as `txid:pubkey`; the gateway allows only bounded read parameters. The source-contract parser and fixtures were updated, but this output-level method has not yet been tested against the actual Stagenet wallet binary. The app does not start the gateway, observer, or allocator; no output assessment/credit journal, customer deposit API, withdrawal worker or sale-settlement path is wired. No financial journal can be posted by the Monero observer or allocator. This is integration groundwork, not a live payment path.
## 2. Identity and trust boundaries
+1 -1
View File
@@ -8,7 +8,7 @@ Use **PostgreSQL** for financial state. Exact arithmetic, transactional constrai
This is an integrated backend, not a second disconnected balance demo: [custody storage selection](../server/market/custody-storage.ts) supplies [the existing HTTP custody routes](../server/testnet/http.ts) with [PostgreSQL custody](../server/market/postgres-custody.ts). Signed command domains, user identities, amounts and UI stay unchanged. SQLite remains the default disposable/local sandbox for compatibility; choose PostgreSQL explicitly. Chain DAG/canonical indexes and legacy mock marketplace remain in their original SQLite databases. Moving those independent concerns is not needed to use PostgreSQL for customer liabilities.
**All balances still represent fictitious sandbox XMR.** No deposit observer/subaddress allocation, Monero signer, real-address withdrawal, NFT sale witness or production payout is introduced. The PostgreSQL backend supports public listing/reservation/cancellation/withdrawal-hold commands; old prepared fixture withdrawals are preserved by import but cannot be cancelled or paid through HTTP. Old SQLite fixture settlement methods are not production PostgreSQL payment workers.
**All balances still represent fictitious sandbox XMR.** Monero observer and subaddress allocation groundwork are implemented separately in additive custody migrations v2-v4, but remain unwired, non-posting and unavailable to public custody routes. No deposit credit journal, live signer, real-address withdrawal, NFT sale witness or production payout is introduced. The PostgreSQL backend supports sandbox listing/reservation/cancellation/withdrawal-hold commands; old prepared fixture withdrawals are preserved by import but cannot be cancelled or paid through HTTP. Old SQLite fixture settlement methods are not production PostgreSQL payment workers.
## Storage and transaction guarantees
+2
View File
@@ -26,6 +26,8 @@ Status: active engineering plan; **not launch or payment authorization**. Update
**Actual Wallet RPC + mTLS gateway rehearsal completed (2026-10-21):** the test image obtains Monero CLI v0.18.5.4 only from the official downloads host, verifies the `binaryFate` fingerprint `81AC 591F E9C4 B65C 5806 AFC3 F0AF 4D46 2A0B DF92`, verifies the GPG-signed canonical hashes file and checks the Linux x64 archive digest `ba2928962c101b609434bf8c1b43ad59ebe96c5525977284ca599d8bf0f02d10` before extraction. Its rehearsal creates ephemeral CA/server/approved-client/untrusted-client certificates, then runs `monero-wallet-rpc` in Docker `--network none`, `--stagenet --offline`, loopback-only, with login disabled behind the tested TLS 1.3 mTLS gateway. The real wallet was verified watch-only by the `query_key(spend_key)` denial. The configured client cert successfully read the primary address and created one subaddress; the gateway rejected a transfer request (HTTP 400 before upstream), a CA-signed but unapproved client certificate, and a no-certificate request. Wallet files were stopped and deleted with the ephemeral container. No `monerod`, peer, network, live credential, mainnet or funds were involved. This proves protocol interoperability in that isolated single-container topology; it does **not** prove Kubernetes/apartment firewall isolation, certificate issuance/rotation, deployment configuration, hard power-loss durability, deposits/credits/withdrawals, or production readiness. The current app still does not start this gateway or observer; HTTP auth with Monero's own `--rpc-login` remains intentionally unused. Full offline suite after these changes: **187/187 passed**; disposable PostgreSQL suite: **35/35 passed**; test image build succeeded.
**Output failure/reorg test results (2026-10-21):** offline coverage now rejects missing outputs, duplicate public keys, subaddress misattribution, and malformed output records. PostgreSQL coverage follows a stable output identity through not-seen, reappeared, and changed-height re-inclusion states; it rejects output-key reuse across transaction IDs and confirms zero journal postings throughout. The image build passed; offline suite **189/189 passed** and disposable PostgreSQL suite **35/35 passed**. This exercises persistence and review signals only—no customer credit or automatic reversal exists. The updated method has not yet been tested against the actual Stagenet wallet binary.
## 1A. MVP release boundary — proposed safe first production-hosted release
The apartment/single-operator constraint changes the rollout and availability claims, not the safety evidence required to hold customer funds or valuable assets. Recommended MVP: **a public, clearly labeled non-monetary beta on the existing testnet**, with real XMR completely disabled, no custodial balances/deposits/withdrawals, and marketplace checkout/delivery disabled for valuable assets. Keep mining, browsing and wallet use only if they are explicitly described as experimental and users accept that data/ownership may be lost. This would be a production-hosted beta, **not mainnet production**, a custody MVP, or an independently secured chain.