Initial commit

This commit is contained in:
2026-07-20 21:24:31 +02:00
commit bae48c46dc
2 changed files with 122 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Language
The user communicates in Italian in chat — reply to them in Italian. Everything written to the repository (code, comments, commit messages, docs, this file) must be in English. Reasoning/thinking should also be done in English.
## Project status
This repository is at the **specification stage, not yet implemented**: it currently contains only [flowchart.mmd](flowchart.mmd), which is the source of truth for the project and describes the entire application flow. The tech stack is decided (see below) but no code, build system, or lint/test commands exist yet — once the project is scaffolded, this section must be updated with real commands (install, run, lint, test — including how to run a single test).
Before writing code, always read [flowchart.mmd](flowchart.mmd) in full: every node in the diagram corresponds to a behavior that must be implemented exactly as described, including the labels on the edges (conditions, retries, loops).
## Tech stack (MVP)
- **Backend language**: Python.
- **PLM node access**: Electrum protocol only (no full node/P2P). Bootstrap server for development: `santantonio.sytes.net:50002` (SSL).
- **Auth**: Argon2 password hashing + JWT sessions.
- **Secrets**: master xprv encrypted at rest with a symmetric scheme (AES-GCM/Fernet); the encryption key itself lives in an env var, never in the DB or in git.
- **Operational config** (fee/commission address, RBF fee-bump wallet, etc.): stored in a DB config table, not env vars — must be editable without a redeploy.
- **Round duration**: configurable via env var, default 10 minutes (not hardcoded).
## PLM network parameters
Source of truth: `PalladiumWallet` repo, [ChainProfiles.cs](../PalladiumWallet/src/Core/Chain/ChainProfiles.cs) and [PalladiumNetworks.cs](../PalladiumWallet/src/Core/Chain/PalladiumNetworks.cs) — always re-check that repo if a value is needed that isn't listed here, rather than guessing.
Mainnet:
- BIP44/84 coin type: `746` (i.e. HD path `m/84'/746'/0'/0/index`)
- Bech32 HRP: `plm`
- P2PKH address version byte: `55` (addresses start with `P`)
- P2SH address version byte: `5`
- WIF prefix: `0x80`
- Block time: 120s
- BIP32 extended key headers (Legacy/native-segwit `zprv`/`zpub` etc.): see `ExtKeyHeaders` in `ChainProfiles.cs`
## MVP business parameters
- Fixed bet cost: **10 PLM** per round.
- Prize split: 70% winner / 30% fees (fee address configurable in DB).
- Minimum deposit/withdrawal amount: **1 PLM** (business-friendly floor, above the network's technical dust limit).
- Confirmations required for all tx types (deposit, bet, payout, withdrawal): **1**.
## What is PLM Lottery
A periodic-round lottery system built on a Bitcoin-like coin (PLM, mainnet). Each user gets a dedicated P2WPKH address (server-side HD wallet); they deposit PLM to that address, place a fixed-cost bet to enter the current round, and when the round closes a winner is drawn who receives 70% of the prize pool (the remaining 30% goes to fees).
## Architecture (from the flowchart subgraphs)
The flow is organized into 5 phases, each a subgraph in [flowchart.mmd](flowchart.mmd):
- **REG (Registration)**: on signup the server derives a new P2WPKH address via BIP84 (`m/84'/coin'/0'/0/index`, one index per user) from a master xprv **encrypted at rest**. This address is permanent and serves as both the deposit address and the address that receives winnings and withdrawals.
- **DEP (Balance top-up)**: an ElectrumClient/SPV subscribes to the user's address scripthash. Internal balance (DB) is credited after **1 confirmation only** — the reorg risk at 1-conf is knowingly accepted in v1, with no rollback logic.
- **PLAY (Bet)**: fixed cost per round, **at most one active bet per user at a time** in v1. The server builds a PSBT user-address → pool-address for the fixed amount, with a **change output back to the same user address** (the user's balance must never exactly equal the bet amount). Fee minimized (~1 sat/vB), **deducted from the bet amount**. If the tx doesn't confirm within a timeout, fee-bump (RBF) and rebroadcast.
- **DRAW (Periodic draw)**: configurable timer (default 10 minutes). Round closing **waits for all already-broadcast bets to confirm** before proceeding (avoids losing bets at the round boundary). The **next round only opens once the previous round's payout tx is confirmed** — rounds never overlap in v1. v1 draw algorithm (deliberately simple, meant to be replaced later): wait for the first block confirmed after round closing, use its hash as seed, `index = seed mod participant_count` over the participant list ordered by **broadcast timestamp** (this is also the tie-break when two bets confirm in the same block). Every participant has **equal probability regardless of bet amount** (consistent with the fixed bet amount). The payout (70% winner / 30% fees) is signed with the pool address key; the **payout fee is deducted from the winner's 70%**, the 30% fee share stays intact. Same timeout → RBF → rebroadcast pattern here too.
- **WITHDRAW (Withdrawal)**: the only way to move funds out of the platform to an external address. PSBT user-address → external-address + change back to the user address, fee deducted from the withdrawn amount, same RBF retry pattern.
PLAY and WITHDRAW share a **per-user DB lock**: a user can never have a bet-build and a withdrawal-build in flight at the same time, since both would otherwise spend from the same UTXO set on the user's dedicated address.
## Non-obvious domain decisions
These choices were made explicitly during design (not derivable from reading a single file) and must be respected in any implementation:
- Private keys (xprv) are generated and held **server-side** — this is not a non-custodial system: the user never controls their own keys until they make an explicit withdrawal.
- The user's personal deposit address always doubles as the winnings-receiving address: there is no separate "winner address".
- 1 confirmation is the chosen threshold for all tx types (deposits, bets, payouts, withdrawals): don't introduce different thresholds (e.g. 3 or 6 confirmations) without an explicit decision.
- The draw algorithm (node R) is deliberately simple and should be treated as a replaceable/pluggable component, not the final design — don't architect around its current implementation.
+55
View File
@@ -0,0 +1,55 @@
flowchart TD
subgraph REG["Registration"]
A["User registers: username + password"] --> B["Server derives a new P2WPKH address\n(BIP84, path m/84'/746'/0'/0/index)\nmaster xprv encrypted at rest"]
B --> C["Address linked to the user profile in the DB"]
end
subgraph DEP["Balance top-up"]
C --> D["User sends PLM to their dedicated address"]
D --> E["ElectrumClient/SPV monitors the address\n(subscribe scripthash)"]
E --> F{"Tx confirmed\n(1 confirmation)?"}
F -- No --> E
F -- Yes --> G["User balance credited in the DB\n(balance = confirmed UTXOs on the address)"]
end
subgraph PLAY["Bet"]
G --> H{"User confirms bet purchase?\n(fixed cost: 10 PLM per round,\nmax 1 active bet at a time,\nacquires per-user DB lock shared with WITHDRAW)"}
H -- No --> G
H -- "Yes (balance >= bet cost)" --> I["Server builds PSBT:\nuser address -> pool address\n(bet cost) + change -> user address\nfee ~1 sat/vB deducted from the bet amount"]
I --> J["Server signs with the user's derived key"]
J --> K["Broadcast tx to the network"]
K --> L{"Tx confirmed\n(1 confirmation)?"}
L -- "No (timeout)" --> K2["Fee bump (RBF) and rebroadcast"]
K2 --> K
L -- Yes --> M["User registered as a participant\nin the current round (with bet amount)"]
end
subgraph DRAW["Periodic draw"]
N["Round timer: every X minutes (configurable, default 10)"] --> O{"Are there bets\nalready broadcast but not yet confirmed?"}
O -- Yes --> O
O -- No --> O2["Close current round"]
O2 --> P["List of round participants\n(user address + bet amount),\nordered by broadcast timestamp\n(tie-break for same-block confirmations)"]
P --> Q{"Are there participants?"}
Q -- No --> N
Q -- Yes --> R["Draw winner (simple v1 algorithm):\n1. wait for the first block confirmed after round closing\n2. seed = block hash (hex -> integer)\n3. index = seed mod participant_count\n4. winner = participants[index]\n(anyone can recompute and verify it;\nalgorithm replaceable in the future)"]
R --> S["Compute total round prize pool\n(sum of confirmed deposits to the pool address)"]
S --> T["70% of the prize pool - payout tx fee\n-> winner's deposit address"]
S --> U["30% of the prize pool (unchanged)\n-> fee address (configurable)"]
T --> V["Payout tx signed with\nthe pool address key"]
U --> V
V --> V2{"Tx confirmed\n(1 confirmation)?"}
V2 -- "No (timeout)" --> V3["Fee bump (RBF) and rebroadcast"]
V3 --> V2
V2 -- Yes --> W["Log round\n(winner, amount, txid) for audit"]
W --> N
end
subgraph WITHDRAW["Withdrawal (simple v1)"]
G --> X["User requests withdrawal:\nexternal address + amount <= balance\n(min 1 PLM, acquires per-user DB lock\nshared with PLAY)"]
X --> Y["Server builds and signs PSBT:\nuser address -> external address\n+ optional change -> user address\nfee deducted from the withdrawn amount"]
Y --> Z["Broadcast + wait for 1 confirmation\n(same RBF-on-timeout pattern)"]
Z --> G
end
M --> N