chore: initial commit for v0.0.1

DChain single-node blockchain + React Native messenger client.

Core:
- PBFT consensus with multi-sig validator admission + equivocation slashing
- BadgerDB + schema migration scaffold (CurrentSchemaVersion=0)
- libp2p gossipsub (tx/v1, blocks/v1, relay/v1, version/v1)
- Native Go contracts (username_registry) alongside WASM (wazero)
- WebSocket gateway with topic-based fanout + Ed25519-nonce auth
- Relay mailbox with NaCl envelope encryption (X25519 + Ed25519)
- Prometheus /metrics, per-IP rate limit, body-size cap

Deployment:
- Single-node compose (deploy/single/) with Caddy TLS + optional Prometheus
- 3-node dev compose (docker-compose.yml) with mocked internet topology
- 3-validator prod compose (deploy/prod/) for federation
- Auto-update from Gitea via /api/update-check + systemd timer
- Build-time version injection (ldflags → node --version)
- UI / Swagger toggle flags (DCHAIN_DISABLE_UI, DCHAIN_DISABLE_SWAGGER)

Client (client-app/):
- Expo / React Native / NativeWind
- E2E NaCl encryption, typing indicator, contact requests
- Auto-discovery of canonical contracts, chain_id aware, WS reconnect on node switch

Documentation:
- README.md, CHANGELOG.md, CONTEXT.md
- deploy/single/README.md with 6 operator scenarios
- deploy/UPDATE_STRATEGY.md with 4-layer forward-compat design
- docs/contracts/*.md per contract
This commit is contained in:
vsecoder
2026-04-17 14:16:44 +03:00
commit 7e7393e4f8
196 changed files with 55947 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
# username_registry
Привязка читаемых `@username` к адресам (Ed25519 pubkey). Forward
(`name → addr`) и reverse (`addr → name`) lookup, передача ownership,
освобождение имени.
**Реализация: native Go** (`blockchain/native_username.go`). Работает
без WASM VM — нулевая латентность, не может повесить AddBlock. Старая
WASM-версия заменена; клиенты получают канонический ID через
`/api/well-known-contracts`.
## Ключевые свойства
| | |
|---|---|
| **Contract ID** | `native:username_registry` |
| **Version** | `2.1.0-native` |
| **Регистрация** | Flat fee 10 000 µT (0.01 T), **burn** — никому не идёт |
| **Ограничения имени** | 432 символа, `a-z 0-9 _ -`, первый символ — буква |
| **Зарезервированные** | `system`, `admin`, `root`, `dchain`, `null`, `none` |
| **Один адрес — одно имя** | Чтобы сменить, нужно `release` + `register` |
Комиссия 10 000 µT выбрана как баланс: достаточно дёшево для реальных
пользователей, достаточно дорого чтобы один ID-squatter не забил тысячу
имён из скучающего бота.
## Оплата
Плата передаётся в `tx.Amount` (видна в истории транзакций), контракт
проверяет её точное значение и вычитает из баланса отправителя. Не
направляется никуда — токены сгорают.
Дополнительно платится `tx.Fee = 1 000 µT` за саму транзакцию — это
network fee валидатору, стандартный `MinFee` для любой tx.
Итого: `register(name)` стоит **11 000 µT ≈ 0.011 T**.
## Методы
### `register(name)` — payable 10000
Зарегистрировать имя для `tx.From`.
**Проверки** (в порядке):
1. `name` валиден (длина, символы, не reserved)
2. `tx.Amount == 10 000` (ошибка `register requires tx.amount = 10000 µT`)
3. Имя не занято
4. `tx.From` не владеет другим именем
5. Баланс отправителя достаточен для `amount + fee + gas`
**Успех:**
- Списывает 10 000 µT с баланса (burn)
- `cstate[name:<name>] = tx.From`
- `cstate[addr:<tx.From>] = name`
- Лог `registered: <name> → <pubkey>`
**CLI-пример:**
```bash
client call-contract \
--key my.json \
--contract native:username_registry \
--method register \
--args '["alice"]' \
--amount 10000 \
--node http://localhost:8081
```
**Клиент-приложение** делает это автоматически через Settings →
«Купить никнейм».
### `resolve(name)` — free
Прочитать владельца имени.
Логирует `owner: <pubkey>` или `not found: <name>`. Клиент в HTTP-API
обычно использует прямое чтение state:
```
GET /api/contracts/native:username_registry/state/name:alice
→ { value_hex: "<hex of owner pubkey ASCII>" }
```
### `lookup(address)` — free
Обратный lookup. Логирует `name: <name>` или `no name: <address>`.
Клиент использует `GET .../state/addr:<pubkey>`.
### `transfer(name, new_owner)` — free
Передать `name` другому адресу. Только текущий владелец может вызвать.
Новый владелец не должен уже иметь имя.
### `release(name)` — free
Удалить привязку. Только текущий владелец. Освобождает `name` и
`addr:<caller>` для будущих регистраций.
## State layout
Все ключи записываются в общий namespace `cstate:native:username_registry:`:
| Key | Value |
|-----|-------|
| `name:<name>` | Hex pubkey владельца (64 символа) |
| `addr:<pubkey>` | ASCII-строка с именем |
Значения хранятся как байты; HTTP endpoint
`/api/contracts/:id/state/:key` возвращает их в `value_hex`, клиент
делает `hex → UTF-8` перед показом.
## Клиентская интеграция
- `useWellKnownContracts` хук авто-синхронизирует `settings.contractId =
native:username_registry`
- `resolveUsername(contractId, name)` и `reverseResolve(contractId, addr)`
в `client-app/lib/api.ts` делают HTTP-запрос + hex-decode
- Settings экран покупает никнейм через `buildCallContractTx({amount:
10_000, ...})` + автоматический `reverseResolve` polling после submit
для показа `@name` в профиле за ~1 блок
## Отличия от предыдущей WASM-версии
| | WASM v1 | Native v2 |
|---|---------|-----------|
| **Fee** | 10^(7-len), tiered | Flat 10 000 µT |
| **Fee destination** | Contract treasury | Burn |
| **Min length** | 1 | **4** |
| **Payment point** | Debited inside contract, invisible in tx | `tx.Amount`, visible in history |
| **Latency** | ~10 ms (wazero startup) | ~50 µs (direct Go call) |
| **VM hang risk** | Да (требует timeout) | Нет |
Зарегистрированные в старой WASM-версии имена не мигрируются
автоматически — операторам нужно пере-зарегистрировать после reset
chain (см. `CHANGELOG.md` «Compatibility notes»).