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

@@ -51,9 +51,37 @@ _Avoid_: reputation, rating, rank
### Payments & fraud ### Payments & fraud
**Stake**: **Stake**:
Tokens a node locks as collateral that can be slashed for fraud. Stake protects the network economically, but route selection is not based on a node's token balance. Collateral a node stands to lose for fraud. In the current design the node's Pending Balance serves as stake — no upfront deposit is required. An optional USDT/TAI deposit may return later for routing priority.
_Avoid_: deposit, bond, escrow _Avoid_: deposit, bond, escrow
**Treasury**:
The single project-owned Solana wallet that custodially holds client deposits, pays node payouts, and accumulates the Protocol Cut. Its keypair is loaded only on settlement-capable trackers.
_Avoid_: escrow, vault, hot wallet
**Pending Balance**:
A node's accrued, not-yet-paid USDT earnings on the tracker ledger. Doubles as the node's fraud collateral: it is forfeited in full when a validator catches a divergent output.
_Avoid_: unpaid rewards, accrual, balance due
**Settlement Period**:
The dynamic interval driving on-chain payouts: a node is paid when its Pending Balance exceeds the Payout Threshold or the period elapses, whichever comes first. Short in development (seconds), long in production (daily), configurable to grow with volume.
_Avoid_: epoch, payout cycle, billing cycle
**Payout Threshold**:
The minimum Pending Balance that triggers an immediate payout before the Settlement Period elapses. Includes a dust floor so payouts are never smaller than they are worth.
_Avoid_: minimum payout, dust limit
**Protocol Cut**:
The 10% of inference fees retained by the project for infrastructure; the remaining 90% goes to the nodes that served the request. Accumulates in the Treasury as the future TAI liquidity reserve.
_Avoid_: spread, commission, house fee
**Deposit Watcher**:
The tracker component that observes the Treasury's on-chain USDT deposits and credits the sending client's API-key ledger balance.
_Avoid_: payment listener, chain scanner
**Mock USDT**:
The self-created 6-decimal SPL mint that stands in for USDT on devnet, where real USDT does not exist. The mint address is configuration, so mainnet cutover is a config change.
_Avoid_: test token, fake USDT, devnet dollar
**Tax**: **Tax**:
The share of caller payments distributed to compute nodes as rewards. Taxes are weighted by completed work and historical node speed so faster, larger nodes earn proportionally more. The share of caller payments distributed to compute nodes as rewards. Taxes are weighted by completed work and historical node speed so faster, larger nodes earn proportionally more.
_Avoid_: fee, toll, commission _Avoid_: fee, toll, commission
@@ -67,8 +95,8 @@ Work a compute node performs without earning immediate rewards, usually during p
_Avoid_: unpaid labor, warmup request _Avoid_: unpaid labor, warmup request
**Slash**: **Slash**:
The act of reducing a node's stake as a penalty for a proven fraud incident. The penalty for a proven fraud incident: the node's entire Pending Balance is forfeited to the Treasury and a Strike is recorded.
_Avoid_: penalize, burn, fine _Avoid_: penalize, burn, fine, forfeit
**Strike**: **Strike**:
A fraud incident recorded on-chain against a node. Enough strikes result in a ban. A fraud incident recorded on-chain against a node. Enough strikes result in a ban.
@@ -83,7 +111,7 @@ The first N jobs a new wallet must complete without earning, to raise the cost o
_Avoid_: trial period, warmup, grace period _Avoid_: trial period, warmup, grace period
**Token**: **Token**:
Our native Solana L2 token. Used by nodes for staking and received as inference rewards. Clients never need to hold it. TAI, our native Solana SPL token. Deferred (ADR-0015): nodes are currently paid directly in USDT; TAI returns as the reward/upside layer once volume exists, funded by the accumulated Protocol Cut. Clients never need to hold it.
_Avoid_: coin, reward token, native token _Avoid_: coin, reward token, native token
**Contract Boundary**: **Contract Boundary**:
@@ -105,7 +133,7 @@ _Avoid_: accusation, report, claim
### Client-facing ### Client-facing
**Client**: **Client**:
Any application or user that sends inference requests to the gateway. Pays in SOL or USDC. Any application or user that sends inference requests to the gateway. Prepays USDT into the Treasury; each request is metered against the resulting ledger balance at a per-1K-tokens price set per model.
_Avoid_: user, caller, consumer _Avoid_: user, caller, consumer
**Model Preset**: **Model Preset**:

View File

@@ -0,0 +1,33 @@
# USDT-direct custodial settlement with pending-balance collateral
Nodes are paid directly in USDT — 90% of inference fees, with 10% retained as the protocol cut. TAI (ADR-0002) is **deferred, not cancelled**: the reward token, revenue-backed floor, and halving curve remain the roadmap once volume exists, and the accumulated protocol cut is the future TAI liquidity source. Nothing in this design blocks minting TAI later (an SPL mint is a one-command operation); this ADR amends ADR-0002's settlement mechanics only.
## The design
**Custodial treasury.** A single project-owned Solana wallet holds all funds. Clients deposit USDT into it; a deposit watcher credits their API-key ledger balance off-chain. Node payouts are batched SPL transfers from the treasury. No Anchor/Rust programs are required — the entire on-chain surface is plain SPL token transfers, extending ADR-0007's contract-boundary approach with a real Solana adapter behind the same interface.
**Off-chain ledger, periodic on-chain settlement.** Trackers meter every request against the client's ledger balance and accrue node earnings as a pending balance. Settlement follows 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. Dev defaults: period 60s, threshold ≈ 0 (so every run is verifiable on-chain). Prod defaults: period 24h, threshold a few USDT. Both are dynamic config so the period can grow with volume to avoid chain overhead.
**Raft leader settles.** In the tracker hive, only the current Raft leader runs the settlement loop; followers replicate the ledger. The treasury keypair is loaded only on settlement-capable trackers (operator-designated). Third-party trackers can join the mesh for routing but never hold the key.
**Pricing.** Clients are charged a per-1K-tokens USDT price set per model in tracker config. Revenue per request is split among the nodes that served it proportional to work units (layers × tokens), reusing existing attribution.
**Penalty = pending-balance forfeiture (amends ADR-0003).** The validator still re-runs ~5% of jobs. A caught cheater forfeits their entire pending balance to the treasury and receives a strike; three strikes bans the wallet. Because settlement is periodic, the pending balance itself is the collateral — no upfront stake deposit, preserving zero-friction node onboarding. With daily settlement, pending ≈ a day's earnings ≫ 20× the per-job gain at a 5% check rate, so cheating has negative expected value. The probationary period (ADR-0003 / issue 08) is retained as the anti-sybil re-entry cost.
**Cluster.** Development targets **devnet** (supersedes the "testnet, never devnet" note in issue 06 — both are free; devnet is the ecosystem standard for app development with reliable faucets). Real USDT exists only on mainnet, so devnet uses a self-created mock-USDT SPL mint (6 decimals); the mint address is config, making mainnet cutover a config change. CI uses `solana-test-validator` or the local deterministic adapter.
## Considered options
- **Anchor escrow program now**: trustless, but requires the Rust/Anchor toolchain and can't ship today; the custodial trust assumption is acceptable pre-mainnet — deferred.
- **Any tracker settles via lease**: most decentralized, but every mesh member would need the treasury key — rejected while custodial.
- **USDT stake deposits for nodes**: stronger collateral, but adds onboarding friction that contradicts the mining-style UX; may return later as optional stake-for-priority — deferred.
- **Keep TAI settlement (ADR-0002 as written)**: backing-price calc and buyback endpoint are too much machinery before there is volume — deferred.
- **USDT-direct custodial settlement with pending-balance collateral**: shippable immediately, verifiable on-chain end-to-end on devnet — **chosen**.
## Consequences
- The entire payment system works with zero smart contracts; the first Anchor program can replace the custodial treasury later behind the same `packages/contracts` boundary.
- Clients and nodes must trust the treasury key until then; this is explicit and acceptable pre-mainnet.
- Node collateral scales with the settlement period: shortening the period in prod weakens the fraud deterrent, so period changes must consider both chain overhead and collateral size.
- A node caught cheating immediately after a payout has little pending balance to forfeit — the strike/ban system covers this window.
- The 10% protocol cut accumulates in the treasury as the future TAI liquidity reserve.

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`

View File

@@ -710,6 +710,103 @@
"US-014", "US-014",
"US-019" "US-019"
] ]
},
{
"id": "US-031",
"title": "31 — Billing ledger: per-token pricing, 90/10 split, pending balances",
"description": "Tracker-side off-chain billing per ADR-0015. Each model preset has a USDT price per 1K tokens in tracker config. On request completion the tracker debits the client's API-key ledger balance and credits 90% to the serving nodes' pending balances proportional to work_units (layers × tokens), 10% to the protocol cut. Requests from API keys with insufficient balance are rejected with 402. Ledger persists in the existing tracker SQLite and replicates across the tracker hive.",
"acceptanceCriteria": [
"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 (no free work)",
"Balances survive tracker restart (SQLite) and replicate to hive followers",
"Unit tests: single-node route, 3-node split, exhausted balance, restart persistence"
],
"priority": 31,
"status": "open",
"notes": "Pure off-chain — no Solana calls in this story. Reuses ComputeAttribution/work_units from packages/contracts.",
"dependsOn": [
"US-023",
"US-025"
]
},
{
"id": "US-032",
"title": "32 — Devnet custodial treasury: mock-USDT mint + deposit watcher",
"description": "Real Solana adapter behind the packages/contracts boundary, custodial model per ADR-0015. A setup script creates the mock-USDT SPL mint (6 decimals) and treasury token account on devnet. Clients register a wallet pubkey against their API key; the deposit watcher polls the treasury token account and credits the sender's API-key ledger balance on confirmed USDT transfers. Mint address, RPC URL, and treasury keypair path are config (.env.devnet).",
"acceptanceCriteria": [
"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 are credited exactly once (signature dedupe)",
"Local solana-test-validator integration test covers mint→deposit→credit flow",
"Deterministic local adapter still works for CI without any validator"
],
"priority": 32,
"status": "open",
"notes": "Supersedes 'testnet never devnet' note in issue 06. solana-py + spl-token client libs.",
"dependsOn": [
"US-031"
]
},
{
"id": "US-033",
"title": "33 — Settlement loop: leader-only batched USDT payouts",
"description": "The Raft leader runs the settlement loop per ADR-0015: pay a node when pending ≥ payout_threshold OR time since its last payout ≥ max_period, whichever fires first, with a dust floor. Payouts are batched SPL transfers treasury→node wallet on devnet. Dev defaults: period 60s, threshold ~0; prod defaults: period 24h, threshold few USDT — all dynamic config. Settlement history (tx signature, node, amount, epoch) persists and replicates.",
"acceptanceCriteria": [
"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 → observe node wallet USDT balance increase"
],
"priority": 33,
"status": "open",
"notes": "Mining-pool standard: threshold + max-interval, dust floor. Treasury key only on settlement-capable trackers.",
"dependsOn": [
"US-019",
"US-032"
]
},
{
"id": "US-034",
"title": "34 — Hardened proof-of-work: pending-balance forfeiture penalty",
"description": "Wire the validator's ~5% sampling to the new penalty per ADR-0015: on confirmed output divergence the node's entire pending balance is forfeited to the protocol cut, a strike is recorded, and three strikes ban the wallet (tracker rejects registration and excludes from routes). Probationary period (first N jobs unpaid) is retained as the re-entry cost. Penalty math documented: at 5% sampling, forfeiting ~a day's pending earnings ≫ 20× per-job cheat gain.",
"acceptanceCriteria": [
"Validator divergence → pending balance forfeited to protocol_cut in the same ledger transaction as the strike",
"3rd strike bans wallet: registration rejected, excluded from all routes",
"Banned wallet's unpaid pending balance is not paid out 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, gets banned within 60 requests",
"Slash/forfeiture events visible in tracker logs and settlement history"
],
"priority": 34,
"status": "open",
"notes": "No stake deposit — pending balance IS the collateral. Amends ADR-0003 penalty; sampling mechanics unchanged.",
"dependsOn": [
"US-031"
]
},
{
"id": "US-035",
"title": "35 — Tracker web dashboard",
"description": "Web dashboard served by the tracker (single-page, no build step — plain HTML/JS polling tracker HTTP endpoints). Shows: hive membership and Raft leader, node registry with health/scores/coverage map, client ledger balances, node pending balances, settlement history with devnet explorer links, strikes/bans, and rolling RPM stats. Read-only in this story; every tracker in the mesh can serve it from its replicated state.",
"acceptanceCriteria": [
"GET /dashboard serves the UI from any tracker (leader or follower)",
"Panels: hive/leader, nodes+coverage, client balances, pending payouts, settlement history (with tx links), strikes/bans, RPM stats",
"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"
],
"priority": 35,
"status": "open",
"notes": "CLI dashboard already exists from US-016; this is the web counterpart. Write ops (price config, manual settle) deferred.",
"dependsOn": [
"US-031",
"US-033"
]
} }
], ],
"metadata": { "metadata": {