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

45
docs/api/README.md Normal file
View File

@@ -0,0 +1,45 @@
# REST API
DChain-нода предоставляет HTTP API на порту `--stats-addr` (по умолчанию `:8080`).
## Базовые URL
| Окружение | URL |
|---------|-----|
| Локально | `http://localhost:8081` |
| Docker node1 | `http://node1:8080` |
| Docker node2 | `http://node2:8080` |
| Docker node3 | `http://node3:8080` |
## Разделы API
| Документ | Эндпоинты |
|---------|----------|
| [Chain API](chain.md) | Блоки, транзакции, балансы, адреса, stats |
| [Contracts API](contracts.md) | Деплой, вызов, state, логи |
| [Relay API](relay.md) | Отправка сообщений, inbox, контакты |
## Формат ошибок
```json
{"error": "описание ошибки"}
```
HTTP-статус: 400 для клиентских ошибок, 500 для серверных.
## Аутентификация
REST API не требует аутентификации. Транзакции подписываются на стороне клиента (CLI-командами) и отправляются как подписанные JSON-объекты. API не имеет admin-эндпоинтов требующих токенов.
## Пример
```bash
# Статистика сети
curl http://localhost:8081/api/stats
# Баланс адреса
curl http://localhost:8081/api/balance/03a1b2c3...
# Последние блоки
curl http://localhost:8081/api/blocks?limit=10
```

314
docs/api/chain.md Normal file
View File

@@ -0,0 +1,314 @@
# Chain API
Эндпоинты для чтения блокчейна: блоки, транзакции, балансы, идентичности, валидаторы.
## Статистика сети
### `GET /api/netstats`
Агрегированная статистика сети.
```bash
curl http://localhost:8081/api/netstats
```
```json
{
"total_blocks": 1024,
"total_txs": 4821,
"validator_count": 3,
"relay_count": 1,
"total_supply_ut": 10000000000
}
```
---
## Блоки
### `GET /api/blocks?limit=N`
Последние `N` блоков (по умолчанию 20).
```bash
curl "http://localhost:8081/api/blocks?limit=10"
```
```json
[
{
"index": 1024,
"hash": "a1b2c3...",
"prev_hash": "...",
"timestamp": 1710000000,
"validator": "03abcd...",
"tx_count": 3,
"total_fees_ut": 15000
},
...
]
```
---
### `GET /api/block/{index}`
Детали блока по высоте.
```bash
curl http://localhost:8081/api/block/1024
```
```json
{
"index": 1024,
"hash": "a1b2c3...",
"prev_hash": "...",
"timestamp": 1710000000,
"validator": "03abcd...",
"transactions": [
{
"id": "tx-abc123",
"type": "TRANSFER",
"from": "03...",
"to": "04...",
"amount_ut": 1000000,
"fee_ut": 1000
}
]
}
```
---
## Транзакции
### `GET /api/txs/recent?limit=N`
Последние транзакции (по умолчанию 20).
```bash
curl "http://localhost:8081/api/txs/recent?limit=5"
```
---
### `GET /api/tx/{txid}`
Транзакция по ID.
```bash
curl http://localhost:8081/api/tx/tx-abc123def456
```
```json
{
"id": "tx-abc123",
"block_index": 1024,
"type": "CALL_CONTRACT",
"from": "03abcd...",
"timestamp": 1710000000,
"payload": { ... },
"signature": "..."
}
```
---
### `POST /api/tx`
Отправить подписанную транзакцию.
```bash
curl -X POST http://localhost:8081/api/tx \
-H "Content-Type: application/json" \
-d '{"type":"TRANSFER","from":"03...","payload":{...},"signature":"..."}'
```
```json
{"id": "tx-abc123", "status": "accepted"}
```
---
## Chain API v2
Расширенный API для работы с транзакциями.
### `GET /v2/chain/transactions/{tx_id}`
```bash
curl http://localhost:8081/v2/chain/transactions/tx-abc123
```
```json
{
"id": "tx-abc123",
"block_index": 1024,
"payload": { ... },
"payload_hex": "...",
"signature_hex": "..."
}
```
---
### `GET /v2/chain/accounts/{account_id}/transactions`
Транзакции для аккаунта с пагинацией.
**Query параметры:**
| Параметр | По умолчанию | Описание |
|---------|------------|---------|
| `limit` | 100 | Максимум (max 1000) |
| `after_block` | — | Только блоки после N |
| `before_block` | — | Только блоки до N |
| `order` | `desc` | `asc` или `desc` |
```bash
curl "http://localhost:8081/v2/chain/accounts/03abcd.../transactions?limit=20&order=desc"
```
---
### `POST /v2/chain/transactions/draft`
Создать черновик транзакции для подписания.
```bash
curl -X POST http://localhost:8081/v2/chain/transactions/draft \
-H "Content-Type: application/json" \
-d '{"from":"03...","to":"04...","amount_ut":1000000}'
```
```json
{
"tx": { ... },
"sign_bytes_hex": "...",
"sign_bytes_base64": "..."
}
```
---
### `POST /v2/chain/transactions`
Отправить подписанную транзакцию (расширенный формат).
```bash
curl -X POST http://localhost:8081/v2/chain/transactions \
-H "Content-Type: application/json" \
-d '{"tx":{...},"signature":"..."}'
```
---
## Адреса
### `GET /api/address/{addr}?limit=N&offset=N`
Информация об адресе (DC-адрес или hex pubkey).
```bash
curl "http://localhost:8081/api/address/DCabc123?limit=20&offset=0"
```
```json
{
"address": "DCabc123...",
"pub_key": "03abcd...",
"balance_ut": 9500000,
"tx_count": 12,
"transactions": [...],
"has_more": false
}
```
---
## Идентичности и валидаторы
### `GET /api/identity/{pubkey|addr}`
Информация об идентичности.
```bash
curl http://localhost:8081/api/identity/03abcd...
```
```json
{
"pub_key": "03abcd...",
"address": "DCabc123...",
"nick": "alice",
"x25519_pub": "...",
"registered_at": 100,
"stake_ut": 1000000000
}
```
---
### `GET /api/validators`
Список активных валидаторов.
```bash
curl http://localhost:8081/api/validators
```
```json
[
{"pub_key": "03...", "stake_ut": 1000000000},
{"pub_key": "04...", "stake_ut": 1000000000}
]
```
---
### `GET /api/node/{pubkey|addr}?window=N`
Информация об узле (статистика, репутация).
```bash
curl http://localhost:8081/api/node/03abcd...
```
---
### `GET /api/relays`
Зарегистрированные relay-провайдеры.
```bash
curl http://localhost:8081/api/relays
```
```json
[
{
"pub_key": "03...",
"relay_pub": "...",
"fee_ut": 100,
"endpoint": ""
}
]
```
---
## Live Events
### `GET /api/events`
Server-Sent Events поток новых блоков и транзакций.
```bash
curl -N http://localhost:8081/api/events
```
```
data: {"type":"block","index":1025,"hash":"...","tx_count":2}
data: {"type":"tx","id":"tx-abc","block":1025,"from":"03..."}
```

285
docs/api/contracts.md Normal file
View File

@@ -0,0 +1,285 @@
# Contracts API
REST API для работы с WASM-контрактами: деплой, вызов, чтение state и логов.
> Деплой и вызов контрактов выполняются через CLI-команды `client deploy-contract` и `client call-contract`, которые формируют и отправляют подписанные транзакции через `POST /api/tx`. Прямой HTTP-деплой не поддерживается.
## Список контрактов
### `GET /api/contracts`
Все задеплоенные контракты.
```bash
curl http://localhost:8081/api/contracts
```
```json
{
"count": 4,
"contracts": [
{
"contract_id": "a1b2c3d4e5f60001",
"deployer_pub": "03abcd...",
"deployed_at": 100,
"wasm_size": 1842,
"abi": {
"contract": "governance",
"version": "1.0.0",
"methods": [...]
}
},
...
]
}
```
---
## Детали контракта
### `GET /api/contracts/{contractID}`
```bash
curl http://localhost:8081/api/contracts/a1b2c3d4e5f60001
```
```json
{
"contract_id": "a1b2c3d4e5f60001",
"deployer_pub": "03abcd...",
"deployed_at": 100,
"wasm_size": 1842,
"abi_json": "{...}"
}
```
| Поле | Тип | Описание |
|------|-----|---------|
| `contract_id` | string | 16-символьный hex ID |
| `deployer_pub` | string | Pubkey деплоера |
| `deployed_at` | uint64 | Высота блока деплоя |
| `wasm_size` | int | Размер WASM в байтах |
| `abi_json` | string | JSON ABI контракта |
---
## State контракта
### `GET /api/contracts/{contractID}/state/{key}`
Читает значение из state контракта по ключу.
```bash
# Строковое значение
curl http://localhost:8081/api/contracts/a1b2c3d4e5f60001/state/admin
# Вложенный ключ
curl "http://localhost:8081/api/contracts/a1b2c3d4e5f60001/state/param:gas_price"
# Ключ эскроу
curl "http://localhost:8081/api/contracts/.../state/e:deal-001:b"
```
```json
{
"key": "admin",
"value_b64": "MDNhYmNk...",
"value_hex": "30336162636...",
"value_u64": null
}
```
| Поле | Тип | Описание |
|------|-----|---------|
| `value_b64` | string | Base64-encoded raw bytes |
| `value_hex` | string | Hex-encoded raw bytes |
| `value_u64` | uint64\|null | Если значение ровно 8 байт big-endian |
**Примеры чтения state контрактов:**
```bash
# Governance: live параметр
curl "http://localhost:8081/api/contracts/$GOV_ID/state/param:gas_price"
# Governance: pending предложение
curl "http://localhost:8081/api/contracts/$GOV_ID/state/prop:gas_price"
# Username registry: pubkey по имени
curl "http://localhost:8081/api/contracts/$REG_ID/state/name:alice"
# value_hex = hex(pubkey_bytes) → hex.DecodeString(value_hex) = pubkey
# Escrow: статус сделки
curl "http://localhost:8081/api/contracts/$ESC_ID/state/e:deal-001:x"
# value_hex: 61='a' (active), 64='d' (disputed), 72='r' (released), 66='f' (refunded)
```
---
## Логи контракта
### `GET /api/contracts/{contractID}/logs?limit=N`
Последние `N` лог-записей (по умолчанию 50, максимум 100).
```bash
curl "http://localhost:8081/api/contracts/a1b2c3d4e5f60001/logs?limit=20"
```
```json
{
"contract_id": "a1b2c3d4e5f60001",
"count": 5,
"logs": [
{
"tx_id": "tx-abc123",
"block_index": 1024,
"timestamp": 1710000000,
"message": "registered: alice"
},
{
"tx_id": "tx-def456",
"block_index": 1020,
"timestamp": 1709999800,
"message": "initialized"
}
]
}
```
Логи отсортированы от новейших к старейшим.
---
## Деплой контракта (через CLI)
```bash
client deploy-contract \
--key key.json \
--wasm mycontract.wasm \
--abi mycontract_abi.json \
--node http://localhost:8081
```
Успешный деплой печатает:
```
contract_id: a1b2c3d4e5f60001
```
В Docker:
```bash
docker exec node1 client deploy-contract \
--key /keys/node1.json \
--wasm /contracts/mycontract.wasm \
--abi /contracts/mycontract_abi.json \
--node http://node1:8080
```
---
## Вызов контракта (через CLI)
```bash
# Без аргументов
client call-contract \
--contract a1b2c3d4e5f60001 \
--method increment \
--gas 5000 \
--key key.json --node http://localhost:8081
# Строковый аргумент
client call-contract \
--contract $REG_ID \
--method register \
--arg alice \
--gas 30000 \
--key key.json --node http://localhost:8081
# Числовой аргумент (uint64)
client call-contract \
--contract $ESC_ID \
--method create \
--arg "deal-001" \
--arg "$SELLER_PUB" \
--arg64 5000000 \
--gas 30000 \
--key key.json --node http://localhost:8081
# Несколько аргументов в JSON
client call-contract \
--contract $AUCTION_ID \
--method create \
--args '["Rare item #1", 1000000, 100]' \
--gas 20000 \
--key key.json --node http://localhost:8081
```
### Флаги call-contract
| Флаг | Тип | Описание |
|------|-----|---------|
| `--contract` | string | ID контракта (16 hex) |
| `--method` | string | Имя метода |
| `--arg` | string | Добавить строковый аргумент (повторяемый) |
| `--arg64` | uint64 | Добавить числовой аргумент (повторяемый) |
| `--args` | JSON | Массив всех аргументов `["str", 42, ...]` |
| `--gas` | uint64 | Gas лимит |
| `--key` | path | Файл ключа |
| `--node` | URL | Node URL |
---
## Токены и NFT
### `GET /api/tokens`
```bash
curl http://localhost:8081/api/tokens
```
```json
{"count": 2, "tokens": [{...}]}
```
### `GET /api/tokens/{id}`
```bash
curl http://localhost:8081/api/tokens/abc123
```
### `GET /api/tokens/{id}/balance/{pubkey}`
```bash
curl http://localhost:8081/api/tokens/abc123/balance/03abcd...
```
```json
{
"token_id": "abc123",
"pub_key": "03abcd...",
"address": "DCabc...",
"balance": 1000
}
```
### `GET /api/nfts`
```bash
curl http://localhost:8081/api/nfts
```
### `GET /api/nfts/{id}`
```bash
curl http://localhost:8081/api/nfts/nft-abc123
```
### `GET /api/nfts/owner/{pubkey}`
```bash
curl http://localhost:8081/api/nfts/owner/03abcd...
```
```json
{"count": 3, "nfts": [{...}]}
```

246
docs/api/relay.md Normal file
View File

@@ -0,0 +1,246 @@
# Relay API
REST API для работы с шифрованными сообщениями через relay-сеть.
Сообщения шифруются E2E с использованием NaCl (X25519 + XSalsa20-Poly1305). Relay хранит зашифрованные конверты и доставляет их получателям.
## Отправить сообщение
### `POST /relay/send`
Зашифровать и отправить сообщение получателю.
**Request body:**
```json
{
"recipient_pub": "<x25519_hex>",
"msg_b64": "<base64_encoded_message>"
}
```
| Поле | Тип | Описание |
|------|-----|---------|
| `recipient_pub` | string | X25519 public key получателя (hex) |
| `msg_b64` | string | Сообщение в base64 |
```bash
MSG=$(echo -n "Hello, Bob!" | base64)
curl -X POST http://localhost:8081/relay/send \
-H "Content-Type: application/json" \
-d "{\"recipient_pub\":\"$BOB_X25519\",\"msg_b64\":\"$MSG\"}"
```
**Response:**
```json
{
"id": "env-abc123",
"recipient_pub": "...",
"status": "sent"
}
```
---
## Broadcast конверта
### `POST /relay/broadcast`
Опубликовать pre-sealed конверт (для light-клиентов, которые шифруют на своей стороне).
**Request body:**
```json
{
"envelope": {
"id": "...",
"recipient_pub": "...",
"sender_pub": "...",
"payload_b64": "...",
"timestamp": 1710000000,
"fee_ut": 100
}
}
```
```bash
curl -X POST http://localhost:8081/relay/broadcast \
-H "Content-Type: application/json" \
-d '{"envelope": {...}}'
```
**Response:**
```json
{"id": "env-abc123", "status": "broadcast"}
```
---
## Inbox
### `GET /relay/inbox?pub=<x25519hex>&since=<ts>&limit=N`
Получить сообщения из inbox.
**Query параметры:**
| Параметр | Обязательный | Описание |
|---------|------------|---------|
| `pub` | Да | X25519 pubkey получателя (hex) |
| `since` | Нет | Unix timestamp — только сообщения новее |
| `limit` | Нет | Максимум (по умолчанию 50) |
```bash
# Получить все сообщения
curl "http://localhost:8081/relay/inbox?pub=$MY_X25519"
# Только новые (после timestamp)
curl "http://localhost:8081/relay/inbox?pub=$MY_X25519&since=1710000000&limit=20"
```
**Response:**
```json
{
"pub": "...",
"count": 2,
"has_more": false,
"items": [
{
"id": "env-abc123",
"sender_pub": "...",
"payload_b64": "...",
"timestamp": 1710000000,
"fee_ut": 100
}
]
}
```
> `payload_b64` содержит зашифрованное сообщение. Расшифровка выполняется на стороне клиента с помощью X25519 private key.
---
### `GET /relay/inbox/count?pub=<hex>`
Количество сообщений в inbox.
```bash
curl "http://localhost:8081/relay/inbox/count?pub=$MY_X25519"
```
**Response:**
```json
{"pub": "...", "count": 3}
```
---
### `DELETE /relay/inbox/{envID}?pub=<hex>`
Удалить сообщение из inbox.
```bash
curl -X DELETE "http://localhost:8081/relay/inbox/env-abc123?pub=$MY_X25519"
```
**Response:**
```json
{"id": "env-abc123", "status": "deleted"}
```
---
## Контакты
### `GET /relay/contacts?pub=<ed25519hex>`
Входящие запросы на контакт.
> Используйте **ed25519** pubkey (не X25519).
```bash
curl "http://localhost:8081/relay/contacts?pub=$MY_ED25519"
```
**Response:**
```json
{
"pub": "...",
"count": 1,
"contacts": [
{
"from_pub": "03abcd...",
"from_nick": "alice",
"intro": "Hi! Let's connect.",
"fee_ut": 1000,
"timestamp": 1710000000
}
]
}
```
---
## CLI команды для relay
Прямой вызов relay API через CLI:
```bash
# Отправить сообщение (автоматически ищет X25519 ключ в registry)
client send-msg \
--to $RECIPIENT_PUB \
--msg "Hello!" \
--key key.json \
--node http://localhost:8081
# Отправить через @username
client send-msg \
--to @alice \
--msg "Hello Alice!" \
--registry $REGISTRY_ID \
--key key.json \
--node http://localhost:8081
# Получить сообщения из inbox
client inbox \
--key key.json \
--node http://localhost:8081 \
--limit 20
# Удалить прочитанные
client inbox \
--key key.json \
--node http://localhost:8081 \
--delete
# Запросить контакт
client request-contact \
--to $RECIPIENT_PUB \
--fee 1000 \
--intro "Hi, I want to connect" \
--key key.json \
--node http://localhost:8081
```
---
## Архитектура relay
```
Отправитель Relay Node Получатель
│ │ │
│── POST /relay/send ───────────────▶│ │
│ {recipient_pub, msg_b64} │ Encrypt (NaCl box) │
│ │ Broadcast via gossipsub │
│ │◀── gossip ─────────────────▶│
│ │ │
│ │ Store in mailbox │
│ │ │
│ │◀── GET /relay/inbox?pub=... ─│
│ │ │
│ │─── {items:[{payload_b64}]} ▶│
│ │ │
│ │ Submit RELAY_PROOF tx │
│ │ (claim fee from sender) │
```
**Gossipsub топик:** `dchain/relay/v1`
**Fee:** Relay берёт fee (задаётся при регистрации `--relay-fee`). Sender должен иметь достаточный баланс. Fee списывается при доставке через RELAY_PROOF транзакцию.