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
134 lines
5.7 KiB
Markdown
134 lines
5.7 KiB
Markdown
# 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** — никому не идёт |
|
||
| **Ограничения имени** | 4–32 символа, `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»).
|