refine blockchain integration and protocols

This commit is contained in:
Dobromir Popov
2026-07-02 21:14:15 +02:00
parent c3d22c895e
commit 416ceba0f6
8 changed files with 306 additions and 6 deletions

View File

@@ -0,0 +1,25 @@
Status: ready-for-agent
# 31 — Billing ledger: per-token pricing, 90/10 split, pending balances
## What to build
Tracker-side off-chain billing per ADR-0015. Each model preset gets a USDT `price_per_1k_tokens` in tracker config. When a request completes, the tracker debits the client's API-key ledger balance (`price × total_tokens / 1000`) and credits 90% to the serving nodes' pending balances proportional to work units (layers × tokens, reusing the existing `ComputeAttribution`), with 10% accruing to the protocol cut. API keys with insufficient balance are rejected with HTTP 402 before any routing happens — no free work.
The ledger persists in the existing tracker SQLite store and replicates across the tracker hive so any follower can serve balance reads.
This story is pure off-chain accounting — no Solana calls.
## Acceptance criteria
- [ ] Per-model `price_per_1k_tokens` in tracker config with sane defaults
- [ ] Completed request debits client ledger: `price × total_tokens / 1000`
- [ ] 90% split across serving nodes by work_units; 10% accrues to `protocol_cut`
- [ ] Insufficient balance → HTTP 402 before routing
- [ ] Balances survive tracker restart (SQLite) and replicate to hive followers
- [ ] Unit tests: single-node route, 3-node split, exhausted balance, restart persistence
## Blocked by
- `23-heartbeat-stats.md`
- `25-rolling-rpm-stats.md`

View File

@@ -0,0 +1,28 @@
Status: ready-for-agent
# 32 — Devnet custodial treasury: mock-USDT mint + deposit watcher
## What to build
The real Solana adapter behind the `packages/contracts` boundary, custodial model per ADR-0015. All on-chain surface is plain SPL transfers — no Anchor programs.
**Setup script** (`scripts/devnet_setup.py`): creates the mock-USDT SPL mint (6 decimals, matching real USDT) and the treasury token account on devnet, airdropping SOL for fees, and prints the `.env.devnet` values (mint address, RPC URL, treasury keypair path).
**Wallet binding**: `POST /v1/wallet/register` binds a client wallet pubkey to an API key.
**Deposit watcher**: polls the treasury token account for confirmed incoming USDT transfers and credits the sending wallet's bound API-key ledger balance. Transaction signatures are deduplicated so replayed/re-observed transfers credit exactly once.
Note: this supersedes the "testnet, never devnet" note in issue 06 (see ADR-0015) — devnet is the ecosystem standard for app development and real USDT exists on neither cluster.
## Acceptance criteria
- [ ] `scripts/devnet_setup.py` creates mint + treasury ATA and prints `.env.devnet` values
- [ ] `POST /v1/wallet/register` binds client wallet pubkey to API key
- [ ] Deposit watcher credits ledger within one poll interval of a confirmed transfer
- [ ] Duplicate/replayed transactions credit exactly once (signature dedupe)
- [ ] Local `solana-test-validator` integration test covers mint → deposit → credit
- [ ] Deterministic local adapter still works for CI without any validator
## Blocked by
- `31-billing-ledger.md`

View File

@@ -0,0 +1,27 @@
Status: ready-for-agent
# 33 — Settlement loop: leader-only batched USDT payouts
## What to build
The on-chain settlement loop per ADR-0015, following the mining-pool standard: pay a node when `pending ≥ payout_threshold` OR `time_since_last_payout ≥ max_period`, whichever fires first, with a dust floor so no payout is smaller than it is worth.
Only the current Raft leader runs the loop; followers replicate the ledger but never sign. The treasury keypair is loaded only on settlement-capable trackers. Payouts are batched SPL transfers treasury → node wallets on devnet.
Config (all dynamic, so the period can grow with volume): dev defaults `period=60s, threshold≈0` so every run is verifiable on-chain; prod defaults `period=24h, threshold=a few USDT`.
Settlement history (settlement id, tx signature, node wallet, amount, timestamp) persists in SQLite, replicates across the hive, and is queryable over HTTP. Retries are idempotent by settlement id — a failed or timed-out transaction never double-pays.
## Acceptance criteria
- [ ] Only the Raft leader settles; followers never sign (asserted in a 3-tracker test)
- [ ] Trigger: `pending ≥ threshold` OR `elapsed ≥ max_period`; dust floor respected
- [ ] Batched SPL transfers land on devnet; pending balances zeroed atomically with recorded tx signature
- [ ] Failed/timeout transactions retry without double-pay (idempotent by settlement id)
- [ ] Settlement history queryable via tracker HTTP endpoint
- [ ] End-to-end devnet test: fund client → run inference → node wallet USDT balance increases
## Blocked by
- `19-binary-data-plane-and-peer-weight-transfer.md`
- `32-devnet-treasury-deposits.md`

View File

@@ -0,0 +1,28 @@
Status: ready-for-agent
# 34 — Hardened proof-of-work: pending-balance forfeiture penalty
## What to build
Wire the validator's ~5% sampling (issue 07) to the new penalty per ADR-0015. On confirmed output divergence:
1. The node's **entire pending balance is forfeited** to the protocol cut, in the same ledger transaction as the strike (no window where a payout can race the penalty).
2. A **strike** is recorded; the third strike **bans** the wallet — registration rejected, excluded from all routes, and any unpaid pending balance is never paid out.
The probationary period (first N jobs unpaid, default 50) is retained as the anti-sybil re-entry cost, and the node CLI shows remaining probation jobs.
Why this deters cheating: settlement is periodic, so the pending balance itself is the collateral — no upfront stake deposit, zero onboarding friction. At a 5% check rate a cheater is caught once per ~20 fraudulent jobs, so the penalty must exceed ~20× the per-job gain; with daily settlement, pending ≈ a full day's earnings, well above that bar. Document this math in the validator README.
## Acceptance criteria
- [ ] Divergence → pending balance forfeited to `protocol_cut` atomically with the strike
- [ ] 3rd strike bans wallet: registration rejected, excluded from all routes
- [ ] Banned wallet's unpaid pending balance is not paid at next settlement
- [ ] Probation: first N jobs (default 50) accrue no pending balance; node CLI shows remaining
- [ ] Integration test: deliberately-bad node loses pending, accrues strikes, banned within 60 requests
- [ ] Forfeiture events visible in tracker logs and settlement history
## Blocked by
- `07-fraud-detection-slash.md`
- `31-billing-ledger.md`

View File

@@ -0,0 +1,34 @@
Status: ready-for-agent
# 35 — Tracker web dashboard
## What to build
A read-only web dashboard served by the tracker itself at `GET /dashboard` — single page, plain HTML/JS polling the tracker's HTTP endpoints, static assets embedded in the tracker package. No new build toolchain.
Because the tracker hive replicates ledger and registry state, **any tracker in the mesh — leader or follower — can serve the dashboard** from its own state.
Panels:
- Hive membership and current Raft leader
- Node registry: health, scores, coverage map per model
- Client ledger balances
- Node pending balances and next-payout ETA
- Settlement history with devnet explorer links (tx signatures)
- Strikes / bans / forfeiture events
- Rolling RPM stats per model
Write operations (editing prices, manual settlement trigger) are deferred to a later story.
## Acceptance criteria
- [ ] `GET /dashboard` serves the UI from any tracker (leader or follower)
- [ ] All seven panels render with live data
- [ ] Auto-refresh ≤5s without page reload
- [ ] No new build toolchain — static assets embedded in the tracker package
- [ ] Works against a 3-tracker hive in the two-machine LAN test setup
## Blocked by
- `31-billing-ledger.md`
- `33-settlement-loop.md`