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
5.7 KiB
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.
Проверки (в порядке):
nameвалиден (длина, символы, не reserved)tx.Amount == 10 000(ошибкаregister requires tx.amount = 10000 µT)- Имя не занято
tx.Fromне владеет другим именем- Баланс отправителя достаточен для
amount + fee + gas
Успех:
- Списывает 10 000 µT с баланса (burn)
cstate[name:<name>] = tx.Fromcstate[addr:<tx.From>] = name- Лог
registered: <name> → <pubkey>
CLI-пример:
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_registryresolveUsername(contractId, name)иreverseResolve(contractId, addr)вclient-app/lib/api.tsделают HTTP-запрос + hex-decode- Settings экран покупает никнейм через
buildCallContractTx({amount: 10_000, ...})+ автоматическийreverseResolvepolling после 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»).