chore(release): clean up repo for v0.0.1 release
Excluded from release bundle:
- CONTEXT.md, CHANGELOG.md (agent/project working notes)
- client-app/ (React Native messenger — tracked separately)
- contracts/hello_go/ (unused standalone example)
Kept contracts/counter/ and contracts/name_registry/ as vm-test fixtures
(referenced by vm/vm_test.go; NOT production contracts).
Docs refactor:
- docs/README.md — new top-level index with cross-references
- docs/quickstart.md — rewrite around single-node as primary path
- docs/node/README.md — full rewrite, all CLI flags, schema table
- docs/api/README.md — add /api/well-known-version, /api/update-check
- docs/contracts/README.md — split native (Go) vs WASM (user-deployable)
- docs/update-system.md — new, full 5-layer update system design
- README.md — link into docs/, drop CHANGELOG/client-app references
Build-time version system (inherited from earlier commits this branch):
- node --version / client --version with ldflags-injected metadata
- /api/well-known-version with {build, protocol_version, features[]}
- Peer-version gossip on dchain/version/v1
- /api/update-check against Gitea release API
- deploy/single/update.sh with semver guard + 15-min systemd jitter
This commit is contained in:
34
docs/README.md
Normal file
34
docs/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# DChain documentation
|
||||
|
||||
Справочник по блокчейн-ноде DChain. Читается в любом порядке, но если в первый
|
||||
раз — идите по разделам сверху вниз.
|
||||
|
||||
## Оглавление
|
||||
|
||||
| Документ | О чём |
|
||||
|----------|-------|
|
||||
| [quickstart.md](quickstart.md) | Поднять ноду локально за 5 минут |
|
||||
| [architecture.md](architecture.md) | 4 слоя стека: network / chain / transport / app |
|
||||
| [node/README.md](node/README.md) | Запуск ноды (docker, native, deployment) |
|
||||
| [update-system.md](update-system.md) | Версионирование, `/api/update-check`, auto-update от Gitea |
|
||||
| [api/README.md](api/README.md) | REST + WebSocket API endpoints |
|
||||
| [cli/README.md](cli/README.md) | CLI `client` — команды, флаги, примеры |
|
||||
| [contracts/README.md](contracts/README.md) | Системные контракты (native + WASM) |
|
||||
| [development/README.md](development/README.md) | SDK для написания своих контрактов (TinyGo) |
|
||||
| [node/governance.md](node/governance.md) | On-chain governance, голосование параметров |
|
||||
| [node/multi-server.md](node/multi-server.md) | Multi-validator federation deploy |
|
||||
|
||||
## Внешние ссылки из репо
|
||||
|
||||
- [README.md](../README.md) — обзор проекта
|
||||
- [deploy/single/README.md](../deploy/single/README.md) — operator runbook для single-node
|
||||
- [deploy/prod/README.md](../deploy/prod/README.md) — operator runbook для multi-validator
|
||||
- [deploy/UPDATE_STRATEGY.md](../deploy/UPDATE_STRATEGY.md) — дизайн forward-compat обновлений (4 слоя)
|
||||
|
||||
## Соглашения
|
||||
|
||||
- **Единицы:** микро-токены (`µT`). 1 T = 1,000,000 µT. MinFee = 1000 µT = 0.001 T.
|
||||
- **Pubkey:** 32-байтный Ed25519 (hex, 64 символа).
|
||||
- **Address:** `DC` + первые 24 hex-символа SHA-256(pubkey).
|
||||
- **ChainID:** `dchain-` + первые 12 hex-символов SHA-256 genesis-блока.
|
||||
- **Времена:** RFC 3339 (UTC) везде, кроме internal-счётчиков в Prometheus (unix seconds).
|
||||
@@ -1,45 +1,72 @@
|
||||
# REST API
|
||||
# REST + WebSocket API
|
||||
|
||||
DChain-нода предоставляет HTTP API на порту `--stats-addr` (по умолчанию `:8080`).
|
||||
DChain-нода обслуживает HTTP + WebSocket на `--stats-addr` (по умолчанию `:8080`).
|
||||
JSON везде; ошибки в формате `{"error": "описание"}`, с 4xx для клиентских
|
||||
проблем и 5xx для серверных.
|
||||
|
||||
## Базовые URL
|
||||
Полная OpenAPI-спека живёт в бинаре (`/swagger/openapi.json`) и рендерится
|
||||
Swagger UI на `/swagger`. Эти два эндпоинта можно выключить через
|
||||
`DCHAIN_DISABLE_SWAGGER=true` — см. [deploy/single/README.md](../../deploy/single/README.md).
|
||||
|
||||
| Окружение | 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, контакты |
|
||||
| [chain.md](chain.md) | Блоки, транзакции, балансы, адреса, netstats, validators |
|
||||
| [contracts.md](contracts.md) | Деплой, вызов, state, логи контрактов |
|
||||
| [relay.md](relay.md) | Relay-mailbox: отправка/приём encrypted envelopes |
|
||||
|
||||
## Формат ошибок
|
||||
## Discovery / metadata (always on)
|
||||
|
||||
```json
|
||||
{"error": "описание ошибки"}
|
||||
```
|
||||
| Endpoint | Возврат |
|
||||
|----------|---------|
|
||||
| `GET /api/netstats` | height, total_txs, supply, validator_count |
|
||||
| `GET /api/network-info` | chain_id, genesis_hash, peers[], validators[], contracts |
|
||||
| `GET /api/well-known-version` | node_version, build{}, protocol_version, features[], chain_id |
|
||||
| `GET /api/well-known-contracts` | canonical contract_id → name mapping |
|
||||
| `GET /api/update-check` | comparison with upstream Gitea release (см. [../update-system.md](../update-system.md)) |
|
||||
| `GET /api/peers` | connected peer IDs + их версии (из gossip `dchain/version/v1`) |
|
||||
| `GET /api/validators` | active validator set |
|
||||
| `GET /metrics` | Prometheus exposition |
|
||||
|
||||
HTTP-статус: 400 для клиентских ошибок, 500 для серверных.
|
||||
## Real-time (push)
|
||||
|
||||
- **`GET /api/ws`** — WebSocket, рекомендованный транспорт. Поддерживает
|
||||
`submit_tx`, `subscribe`, `unsubscribe`, `typing`, `ping`. Push-события:
|
||||
`block`, `tx`, `inbox`, `typing`, `submit_ack`.
|
||||
- `GET /api/events` — SSE, legacy одностороннее streaming (для старых клиентов).
|
||||
|
||||
Protocol reference — `node/ws.go` + [../architecture.md](../architecture.md).
|
||||
|
||||
## Аутентификация
|
||||
|
||||
REST API не требует аутентификации. Транзакции подписываются на стороне клиента (CLI-командами) и отправляются как подписанные JSON-объекты. API не имеет admin-эндпоинтов требующих токенов.
|
||||
По умолчанию API **публичен**. Три режима access-control настраиваются через
|
||||
env:
|
||||
|
||||
## Пример
|
||||
| Режим | `DCHAIN_API_TOKEN` | `DCHAIN_API_PRIVATE` | Эффект |
|
||||
|-------|:------------------:|:--------------------:|--------|
|
||||
| Public | не задан | — | Все могут читать и submit'ить tx |
|
||||
| Token writes | задан | `false` | Читать — любой; `POST /api/tx` и WS `submit_tx` — только с `Authorization: Bearer <token>` |
|
||||
| Fully private | задан | `true` | Все эндпоинты требуют token (включая `/api/netstats`) |
|
||||
|
||||
WebSocket: токен передаётся query-параметром `?token=<...>`.
|
||||
|
||||
## Примеры
|
||||
|
||||
```bash
|
||||
# Статистика сети
|
||||
curl http://localhost:8081/api/stats
|
||||
# Базовый health
|
||||
curl -s http://localhost:8080/api/netstats | jq .
|
||||
|
||||
# Баланс адреса
|
||||
curl http://localhost:8081/api/balance/03a1b2c3...
|
||||
# Версия ноды
|
||||
curl -s http://localhost:8080/api/well-known-version | jq '{tag: .build.tag, features: .features}'
|
||||
|
||||
# Последние блоки
|
||||
curl http://localhost:8081/api/blocks?limit=10
|
||||
# Список пиров с их версиями
|
||||
curl -s http://localhost:8080/api/peers | jq '.peers[].version'
|
||||
|
||||
# С токеном (private-режим)
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/netstats
|
||||
|
||||
# Submit tx (public-режим)
|
||||
curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d @tx.json http://localhost:8080/api/tx
|
||||
```
|
||||
|
||||
@@ -1,59 +1,85 @@
|
||||
# Production Smart Contracts
|
||||
# Smart contracts
|
||||
|
||||
DChain поставляется с четырьмя production-контрактами, задеплоенными из genesis-кошелька.
|
||||
DChain поддерживает **два типа** контрактов — native Go (запечены в
|
||||
бинарь ноды) и WASM (деплоятся on-chain через `DEPLOY_CONTRACT` tx).
|
||||
|
||||
## Обзор
|
||||
## Native (Go, скомпилированы в бинарь)
|
||||
|
||||
| Контракт | Назначение | Ключевые фичи |
|
||||
|---------|-----------|--------------|
|
||||
| [username_registry](username_registry.md) | Username ↔ адрес | Тарифная сетка, treasury fees, reverse lookup |
|
||||
Живут в `blockchain/native*.go`. Видны как `native:<name>`, работают на
|
||||
каждой ноде идентично (тот же Go-код — нет WASM-drift'а).
|
||||
|
||||
| Контракт | ID | Назначение |
|
||||
|----------|----|-----------|
|
||||
| [username_registry](username_registry.md) | `native:username_registry` | `@username` ↔ pubkey, плата 10_000 µT, min 4 символа |
|
||||
|
||||
Клиент находит `native:username_registry` автоматически через
|
||||
`/api/well-known-contracts` — без конфигурирования.
|
||||
|
||||
## WASM (user-deployable)
|
||||
|
||||
Деплоятся любым обладателем баланса через `client deploy-contract`.
|
||||
Исполняются в wazero runtime с gas limit и set'ом host-функций.
|
||||
|
||||
| Контракт | Назначение | Key features |
|
||||
|----------|------------|--------------|
|
||||
| [governance](governance.md) | On-chain параметры | Propose/approve workflow, admin role |
|
||||
| [auction](auction.md) | English auction | Token escrow, автоматический refund, settle |
|
||||
| [escrow](escrow.md) | Двусторонний escrow | Dispute/resolve, admin arbitration |
|
||||
|
||||
## Деплой
|
||||
Исходники + WASM-артефакты — в `contracts/<name>/`. SDK для написания
|
||||
новых контрактов — [../development/README.md](../development/README.md).
|
||||
|
||||
## Деплой WASM
|
||||
|
||||
Контракты **не деплоятся автоматически** — это выбор оператора.
|
||||
Скрипт-хелпер:
|
||||
|
||||
```bash
|
||||
docker compose --profile deploy run --rm deploy
|
||||
./scripts/deploy_contracts.sh # деплоит auction + escrow + governance из CWD
|
||||
```
|
||||
|
||||
Все 4 контракта деплоятся автоматически. ID сохраняются в `/tmp/contracts.env`.
|
||||
|
||||
## Вызов контракта
|
||||
Или вручную:
|
||||
|
||||
```bash
|
||||
docker exec node1 client call-contract \
|
||||
--key /keys/node1.json \
|
||||
--contract <CONTRACT_ID> \
|
||||
--method <METHOD> \
|
||||
--arg <STRING_ARG> # строковый аргумент (можно несколько)
|
||||
--arg64 <UINT64_ARG> # числовой аргумент uint64
|
||||
--gas <GAS_LIMIT> # рекомендуется 20000 для записи, 5000 для чтения
|
||||
--node http://node1:8080
|
||||
client deploy-contract \
|
||||
--key node.json \
|
||||
--wasm contracts/auction/auction.wasm \
|
||||
--abi contracts/auction/auction_abi.json \
|
||||
--node http://localhost:8080
|
||||
```
|
||||
|
||||
## Contract Treasury
|
||||
После деплоя `contract_id` печатается в stdout — сохраните его.
|
||||
|
||||
У каждого контракта есть **ownerless treasury address** — `hex(sha256(contractID + ":treasury"))`.
|
||||
Это эскроу-адрес без private key. Только сам контракт может снять с него деньги через host function `transfer`.
|
||||
|
||||
Используется в `auction` и `escrow` для хранения заблокированных токенов.
|
||||
|
||||
## Просмотр состояния
|
||||
## Вызов контракта (любой типа)
|
||||
|
||||
```bash
|
||||
# Через REST API
|
||||
curl http://localhost:8081/api/contracts/<ID>/state/<key>
|
||||
|
||||
# Через Explorer
|
||||
open http://localhost:8081/contract?id=<ID>
|
||||
client call-contract \
|
||||
--key node.json \
|
||||
--contract <contract_id_or_native:name> \
|
||||
--method <method> \
|
||||
--arg <string_arg> # можно повторять
|
||||
--arg64 <uint64_arg> # числовые аргументы
|
||||
--gas 20000 # reasonable default; read-only методы: 5000
|
||||
--amount 0 # tx.Amount (для методов с платой — см. username_registry)
|
||||
--node http://localhost:8080
|
||||
```
|
||||
|
||||
## Логи контракта
|
||||
## Contract treasury
|
||||
|
||||
У каждого WASM-контракта есть **ownerless treasury** по адресу
|
||||
`hex(sha256(contractID + ":treasury"))`. Private-key не существует,
|
||||
списывать можно только через host function `transfer` из самого
|
||||
контракта. Используется `auction` (escrow bids) и `escrow` (held amount).
|
||||
|
||||
## Просмотр state / логов
|
||||
|
||||
```bash
|
||||
# REST
|
||||
curl "http://localhost:8081/api/contracts/<ID>/logs?limit=20"
|
||||
# Конкретный ключ state
|
||||
curl -s http://localhost:8080/api/contracts/<id>/state/<key>
|
||||
|
||||
# Explorer → вкладка Logs
|
||||
# Последние N логов контракта
|
||||
curl -s "http://localhost:8080/api/contracts/<id>/logs?limit=20" | jq .
|
||||
|
||||
# Или через Explorer UI
|
||||
open http://localhost:8080/contract?id=<id>
|
||||
```
|
||||
|
||||
@@ -85,19 +85,20 @@ contracts/
|
||||
## Деплой контракта
|
||||
|
||||
```bash
|
||||
# Локально
|
||||
client deploy-contract \
|
||||
--key key.json \
|
||||
--key node.json \
|
||||
--wasm mycontract.wasm \
|
||||
--abi mycontract_abi.json \
|
||||
--node http://localhost:8081
|
||||
--node http://localhost:8080
|
||||
```
|
||||
|
||||
# В Docker
|
||||
docker exec node1 client deploy-contract \
|
||||
--key /keys/node1.json \
|
||||
В Docker (single-node):
|
||||
```bash
|
||||
docker exec dchain /usr/local/bin/client deploy-contract \
|
||||
--key /keys/node.json \
|
||||
--wasm /path/to/mycontract.wasm \
|
||||
--abi /path/to/mycontract_abi.json \
|
||||
--node http://node1:8080
|
||||
--node http://localhost:8080
|
||||
```
|
||||
|
||||
Успешный деплой логирует `contract_id: <hex16>`.
|
||||
|
||||
@@ -1,219 +1,182 @@
|
||||
# Запуск ноды
|
||||
|
||||
## Быстрый старт (Docker)
|
||||
Три основных сценария — по возрастанию сложности.
|
||||
|
||||
| Сценарий | Где подробно |
|
||||
|----------|--------------|
|
||||
| **Одна нода локально (dev, genesis)** | [../quickstart.md](../quickstart.md) → Путь 1 |
|
||||
| **Одна нода с TLS и доменом (production personal)** | [../../deploy/single/README.md](../../deploy/single/README.md) |
|
||||
| **Multi-validator federation (3+ нод, PBFT quorum)** | [../../deploy/prod/README.md](../../deploy/prod/README.md) + [multi-server.md](multi-server.md) |
|
||||
|
||||
Этот документ — про native запуск (не через docker) и обзор всех флагов.
|
||||
|
||||
## Native build
|
||||
|
||||
Требования:
|
||||
- Go 1.24+
|
||||
- BadgerDB + libp2p встроены в бинарь (no-cgo, `CGO_ENABLED=0`)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your/go-blockchain
|
||||
cd go-blockchain
|
||||
docker compose up -d
|
||||
go build -ldflags "-s -w \
|
||||
-X go-blockchain/node/version.Tag=$(git describe --tags --always --dirty) \
|
||||
-X go-blockchain/node/version.Commit=$(git rev-parse HEAD) \
|
||||
-X go-blockchain/node/version.Date=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
|
||||
-X go-blockchain/node/version.Dirty=$(git diff --quiet HEAD -- && echo false || echo true)" \
|
||||
-o node ./cmd/node
|
||||
|
||||
./node --version
|
||||
# dchain-node v0.0.1 (commit=abc1234 date=… dirty=false)
|
||||
```
|
||||
|
||||
Запускает 3 ноды: `node1` (8081), `node2` (8082), `node3` (8083).
|
||||
Ldflags не обязательны — без них версия будет "dev". Makefile в корне
|
||||
инкапсулирует это: `make build`.
|
||||
|
||||
Деплой контрактов:
|
||||
```bash
|
||||
docker exec node1 /scripts/deploy_contracts.sh
|
||||
```
|
||||
## Single node (genesis)
|
||||
|
||||
Explorer: http://localhost:8081
|
||||
|
||||
---
|
||||
|
||||
## Запуск вручную
|
||||
|
||||
### Требования
|
||||
|
||||
- Go 1.21+
|
||||
- BadgerDB (встроен)
|
||||
- libp2p (встроен)
|
||||
|
||||
### Сборка
|
||||
Сгенерировать ключ и поднять блок 0 с собой как единственным валидатором:
|
||||
|
||||
```bash
|
||||
cd go-blockchain
|
||||
go build ./cmd/node/
|
||||
go build ./cmd/client/
|
||||
```
|
||||
./node --version # проверка
|
||||
./client keygen --out node.json
|
||||
|
||||
### Genesis нода
|
||||
|
||||
```bash
|
||||
# Первый запуск — создать genesis
|
||||
./node \
|
||||
--key node1.json \
|
||||
--key node.json \
|
||||
--db ./data \
|
||||
--genesis \
|
||||
--validators "$(cat node1.json | jq -r .pub_key),$(cat node2.json | jq -r .pub_key),$(cat node3.json | jq -r .pub_key)" \
|
||||
--stats-addr :8081 \
|
||||
--listen /ip4/0.0.0.0/tcp/4001 \
|
||||
--db ./data/node1
|
||||
--stats-addr :8080
|
||||
```
|
||||
|
||||
### Peer нода
|
||||
После первого успешного запуска удалите `--genesis` (no-op, но шумит в логах).
|
||||
|
||||
## Join existing network
|
||||
|
||||
Зная URL хотя бы одной живой ноды:
|
||||
|
||||
```bash
|
||||
./node \
|
||||
--key node2.json \
|
||||
--peers /ip4/127.0.0.1/tcp/4001/p2p/<node1-peer-id> \
|
||||
--stats-addr :8082 \
|
||||
--listen /ip4/0.0.0.0/tcp/4002 \
|
||||
--db ./data/node2
|
||||
--key node.json \
|
||||
--db ./data \
|
||||
--join http://seed1.example.com:8080,http://seed2.example.com:8080 \
|
||||
--listen /ip4/0.0.0.0/tcp/4001 \
|
||||
--stats-addr :8080
|
||||
```
|
||||
|
||||
---
|
||||
Нода скачает `/api/network-info`, подхватит chain_id, genesis_hash, список
|
||||
валидаторов и peer multiaddrs. По умолчанию запускается как **observer**
|
||||
(применяет блоки, принимает tx, но не голосует). Чтобы стать валидатором,
|
||||
существующий валидатор должен подать `ADD_VALIDATOR` tx с multi-sig.
|
||||
|
||||
## Флаги командной строки
|
||||
## Все флаги
|
||||
|
||||
| Флаг | По умолчанию | Описание |
|
||||
|------|------------|---------|
|
||||
| `--key` | `node.json` | Файл Ed25519 + X25519 идентичности |
|
||||
| `--db` | `chaindata` | Директория BadgerDB |
|
||||
| `--listen` | `/ip4/0.0.0.0/tcp/4001` | libp2p адрес |
|
||||
| `--peers` | — | Bootstrap peer multiaddrs (через запятую) |
|
||||
| `--validators` | — | Pubkeys валидаторов (через запятую, только для `--genesis`) |
|
||||
| `--genesis` | false | Создать genesis блок при первом старте |
|
||||
| `--stats-addr` | `:8080` | HTTP API/Explorer порт |
|
||||
| `--wallet` | — | Payout кошелёк (hex pubkey) |
|
||||
| `--wallet-pass` | — | Пароль к кошельку |
|
||||
| `--heartbeat` | false | Отправлять heartbeat каждые 60 минут |
|
||||
| `--register-relay` | false | Зарегистрироваться как relay-провайдер |
|
||||
| `--relay-fee` | 0 | Fee за relay сообщение в µT |
|
||||
| `--relay-key` | `relay.json` | X25519 ключ для relay шифрования |
|
||||
| `--mailbox-db` | — | Директория для relay mailbox |
|
||||
| `--governance-contract` | — | ID governance контракта для динамических параметров |
|
||||
Каждый флаг имеет env fallback (`DCHAIN_<FLAG_NAME>` в UPPERCASE с
|
||||
заменой `-` на `_`).
|
||||
|
||||
---
|
||||
### Идентичность
|
||||
| Флаг | env | По умолчанию | Описание |
|
||||
|------|-----|--------------|----------|
|
||||
| `--key` | `DCHAIN_KEY` | `node.json` | Ed25519 + X25519 identity file |
|
||||
| `--db` | `DCHAIN_DB` | `chaindata` | BadgerDB directory |
|
||||
|
||||
## Docker Compose
|
||||
### Сеть
|
||||
| Флаг | env | По умолчанию | Описание |
|
||||
|------|-----|--------------|----------|
|
||||
| `--listen` | `DCHAIN_LISTEN` | `/ip4/0.0.0.0/tcp/4001` | libp2p listen multiaddr |
|
||||
| `--announce` | `DCHAIN_ANNOUNCE` | auto | Что анонсировать пирам (публичный IP!) |
|
||||
| `--peers` | `DCHAIN_PEERS` | — | Bootstrap peers (comma-sep multiaddrs) |
|
||||
| `--join` | `DCHAIN_JOIN` | — | Seed HTTP URLs для onboarding |
|
||||
|
||||
### docker-compose.yml структура
|
||||
### Консенсус
|
||||
| Флаг | env | По умолчанию | Описание |
|
||||
|------|-----|--------------|----------|
|
||||
| `--genesis` | `DCHAIN_GENESIS` | false | Создать block 0 при первом старте |
|
||||
| `--validators` | `DCHAIN_VALIDATORS` | — | Initial validator set (для `--genesis`) |
|
||||
| `--observer` | `DCHAIN_OBSERVER` | false | Observer mode (не голосовать) |
|
||||
| `--allow-genesis-mismatch` | — | false | Пропустить safety check (опасно) |
|
||||
|
||||
```yaml
|
||||
services:
|
||||
node1:
|
||||
build: .
|
||||
ports:
|
||||
- "8081:8080" # HTTP API
|
||||
- "4001:4001" # libp2p
|
||||
volumes:
|
||||
- node1_data:/chaindata
|
||||
- ./keys:/keys:ro
|
||||
command: >
|
||||
node
|
||||
--key /keys/node1.json
|
||||
--genesis
|
||||
--validators "$VALIDATORS"
|
||||
--stats-addr :8080
|
||||
--listen /ip4/0.0.0.0/tcp/4001
|
||||
--governance-contract "$GOV_ID"
|
||||
environment:
|
||||
- VALIDATORS=03...,04...,05...
|
||||
### Relay
|
||||
| Флаг | env | По умолчанию | Описание |
|
||||
|------|-----|--------------|----------|
|
||||
| `--register-relay` | `DCHAIN_REGISTER_RELAY` | false | Подать REGISTER_RELAY tx при старте |
|
||||
| `--relay-fee` | — | 1000 | Fee за relay envelope, µT |
|
||||
| `--relay-key` | — | `relay.json` | X25519 key для E2E шифрования envelope |
|
||||
| `--mailbox-db` | — | — | Badger dir для offline-получателей |
|
||||
|
||||
node2:
|
||||
build: .
|
||||
ports:
|
||||
- "8082:8080"
|
||||
command: >
|
||||
node
|
||||
--key /keys/node2.json
|
||||
--peers /dns4/node1/tcp/4001/p2p/$NODE1_PEER_ID
|
||||
--stats-addr :8080
|
||||
--listen /ip4/0.0.0.0/tcp/4001
|
||||
```
|
||||
### Governance / misc
|
||||
| Флаг | env | По умолчанию | Описание |
|
||||
|------|-----|--------------|----------|
|
||||
| `--governance-contract` | `DCHAIN_GOVERNANCE_CONTRACT` | — | ID governance контракта |
|
||||
| `--heartbeat` | — | false | Слать heartbeat tx каждые 60 мин |
|
||||
| `--log-format` | `DCHAIN_LOG_FORMAT` | `text` | `text` или `json` |
|
||||
|
||||
### Управление
|
||||
### HTTP / WebSocket / UI
|
||||
| Флаг | env | По умолчанию | Описание |
|
||||
|------|-----|--------------|----------|
|
||||
| `--stats-addr` | — | `:8080` | HTTP + WS listen address |
|
||||
| `--disable-ui` | `DCHAIN_DISABLE_UI` | false | Выключить блок-эксплорер HTML-страницы |
|
||||
| `--disable-swagger` | `DCHAIN_DISABLE_SWAGGER` | false | Выключить `/swagger` + `/swagger/openapi.json` |
|
||||
| `--api-token` | `DCHAIN_API_TOKEN` | — | Bearer token для submit (пусто = public) |
|
||||
| `--api-private` | `DCHAIN_API_PRIVATE` | false | Требовать token и для чтения |
|
||||
|
||||
```bash
|
||||
# Запустить
|
||||
docker compose up -d
|
||||
|
||||
# Логи
|
||||
docker compose logs -f node1
|
||||
|
||||
# Остановить
|
||||
docker compose down
|
||||
|
||||
# Сбросить данные
|
||||
docker compose down -v
|
||||
```
|
||||
|
||||
---
|
||||
### Update system
|
||||
| Флаг | env | По умолчанию | Описание |
|
||||
|------|-----|--------------|----------|
|
||||
| `--update-source-url` | `DCHAIN_UPDATE_SOURCE_URL` | — | Gitea `/api/v1/.../releases/latest` URL |
|
||||
| `--update-source-token` | `DCHAIN_UPDATE_SOURCE_TOKEN` | — | PAT для приватных repo |
|
||||
|
||||
## Файл ключа
|
||||
|
||||
Каждая нода использует один файл с обоими ключами:
|
||||
|
||||
```json
|
||||
{
|
||||
"pub_key": "03abcd...", // Ed25519 pubkey (hex, 66 символов)
|
||||
"priv_key": "...", // Ed25519 privkey (hex)
|
||||
"x25519_pub": "...", // X25519 pubkey для E2E relay (hex, 64 символа)
|
||||
"x25519_priv": "..." // X25519 privkey
|
||||
"pub_key": "26018d40...", // Ed25519 public (64 hex chars)
|
||||
"priv_key": "16aba1d2...", // Ed25519 private (128 hex chars, priv||pub)
|
||||
"x25519_pub": "baada10a...", // X25519 public для relay E2E
|
||||
"x25519_priv":"a814c191..." // X25519 private
|
||||
}
|
||||
```
|
||||
|
||||
Генерация:
|
||||
```bash
|
||||
client keygen --out node1.json
|
||||
`client keygen --out node.json` — создаёт валидный файл.
|
||||
|
||||
## Схема ключей BadgerDB
|
||||
|
||||
```
|
||||
height → uint64 (tip)
|
||||
block:<index> → JSON Block
|
||||
tx:<txid> → JSON TxRecord
|
||||
txchron:<block20d>:<seq04d> → tx_id (recent-tx index)
|
||||
balance:<pubkey> → uint64 (µT)
|
||||
stake:<pubkey> → uint64 (µT)
|
||||
id:<pubkey> → JSON RegisterKeyPayload
|
||||
chan:<channelID> → JSON CreateChannelPayload
|
||||
chan-member:<ch>:<pub> → ""
|
||||
contract:<contractID> → JSON ContractRecord
|
||||
cstate:<contractID>:<key> → bytes
|
||||
clog:<ct>:<block>:<seq> → JSON ContractLogEntry
|
||||
relay:<pubkey> → JSON RegisterRelayPayload
|
||||
validator:<pubkey> → "" (presence = active)
|
||||
schema:ver → uint32 (migration version)
|
||||
```
|
||||
|
||||
---
|
||||
## Метрики + healthcheck
|
||||
|
||||
## Мониторинг
|
||||
- **Healthcheck:** `curl http://localhost:8080/api/netstats` возвращает
|
||||
200 с JSON, если нода живая.
|
||||
- **Prometheus:** `GET /metrics` — см. [../api/README.md](../api/README.md).
|
||||
- **Время блока:** норма ~5 сек (константа в `consensus/pbft.go`).
|
||||
|
||||
### HTTP healthcheck
|
||||
## Сброс данных
|
||||
|
||||
```bash
|
||||
curl http://localhost:8081/api/netstats
|
||||
```
|
||||
# Native
|
||||
rm -rf ./data ./mailbox
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
# Docker
|
||||
docker compose logs -f node1 | grep -E "block|error|warn"
|
||||
|
||||
# Прямой запуск
|
||||
./node ... 2>&1 | tee node.log
|
||||
```
|
||||
|
||||
### Метрики производительности
|
||||
|
||||
| Показатель | Норма |
|
||||
|-----------|------|
|
||||
| Время блока | ~3 с |
|
||||
| Блоков в минуту | ~20 |
|
||||
| PBFT фазы | prepare → commit → finalize |
|
||||
|
||||
---
|
||||
|
||||
## Структура данных BadgerDB
|
||||
|
||||
```
|
||||
balance:<pubkey> → uint64 (µT)
|
||||
identity:<pubkey> → JSON RegisterKeyPayload
|
||||
stake:<pubkey> → uint64 (µT)
|
||||
block:<index> → JSON Block
|
||||
tx:<txid> → JSON TxRecord
|
||||
txidx:<pubkey>:<block>:<seq> → txid (индекс по адресу)
|
||||
contract:<id> → JSON ContractRecord
|
||||
cstate:<id>:<key> → []byte (state контракта)
|
||||
clog:<id>:<seq> → JSON ContractLogEntry
|
||||
relay:<pubkey> → JSON RegisteredRelayInfo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Сброс и восстановление
|
||||
|
||||
```bash
|
||||
# Полный сброс
|
||||
docker compose down -v
|
||||
docker compose up -d
|
||||
|
||||
# Только данные одной ноды
|
||||
docker compose stop node1
|
||||
docker volume rm go-blockchain_node1_data
|
||||
docker compose up -d node1
|
||||
# Single-node container
|
||||
docker stop dchain && docker rm dchain
|
||||
docker volume rm dchain_data
|
||||
```
|
||||
|
||||
После сброса нужно заново задеплоить контракты и залинковать governance.
|
||||
После сброса нода начнёт с пустого стейта; c `--genesis` — создаст новый
|
||||
chain_id, с `--join` — синкается с сетью.
|
||||
|
||||
@@ -1,177 +1,85 @@
|
||||
# Быстрый старт
|
||||
# Quickstart
|
||||
|
||||
Три пути в зависимости от того, что вам нужно.
|
||||
|
||||
## Путь 1: локальная single-node за 5 минут
|
||||
|
||||
Самый быстрый способ потрогать блокчейн — одна нода в Docker, без TLS, без
|
||||
федерации, с HTTP-API на `localhost:8080`.
|
||||
|
||||
```bash
|
||||
git clone https://git.vsecoder.vodka/vsecoder/dchain.git
|
||||
cd dchain
|
||||
|
||||
# 1. Собираем образ
|
||||
docker build -t dchain-node-slim -f deploy/prod/Dockerfile.slim .
|
||||
|
||||
# 2. Генерим ключ ноды (один раз)
|
||||
mkdir -p keys
|
||||
docker run --rm --entrypoint /usr/local/bin/client \
|
||||
-v "$PWD/keys:/out" dchain-node-slim \
|
||||
keygen --out /out/node.json
|
||||
|
||||
# 3. Запускаем как genesis (блок 0 = эта нода — единственный валидатор)
|
||||
docker run -d --name dchain --restart unless-stopped \
|
||||
-p 4001:4001 -p 8080:8080 \
|
||||
-v dchain_data:/data \
|
||||
-v "$PWD/keys:/keys:ro" \
|
||||
-e DCHAIN_GENESIS=true \
|
||||
-e DCHAIN_ANNOUNCE=/ip4/127.0.0.1/tcp/4001 \
|
||||
dchain-node-slim \
|
||||
--db=/data/chain --mailbox-db=/data/mailbox --key=/keys/node.json \
|
||||
--relay-key=/data/relay.json --listen=/ip4/0.0.0.0/tcp/4001 --stats-addr=:8080
|
||||
|
||||
# 4. Проверяем
|
||||
curl -s http://localhost:8080/api/netstats
|
||||
curl -s http://localhost:8080/api/well-known-version
|
||||
open http://localhost:8080/ # Explorer UI
|
||||
open http://localhost:8080/swagger # Swagger UI
|
||||
```
|
||||
|
||||
Блоки коммитятся каждые ~5 секунд. Что дальше:
|
||||
|
||||
- Первая отправка tx — [cli/README.md](cli/README.md)
|
||||
- API endpoint reference — [api/README.md](api/README.md)
|
||||
- Выключить UI / Swagger — см. `DCHAIN_DISABLE_UI` / `DCHAIN_DISABLE_SWAGGER` в [deploy/single/README.md](../deploy/single/README.md)
|
||||
|
||||
## Путь 2: single-node с TLS и доменом
|
||||
|
||||
Полный operator runbook — [deploy/single/README.md](../deploy/single/README.md).
|
||||
Там 6 готовых сценариев: публичная с UI, headless API, полностью приватная,
|
||||
genesis, joiner, auto-update от Gitea.
|
||||
|
||||
## Путь 3: multi-validator federation
|
||||
|
||||
3 ноды в PBFT quorum, Caddy LB с ip_hash для WS — [deploy/prod/README.md](../deploy/prod/README.md).
|
||||
|
||||
## Требования
|
||||
|
||||
- Docker Desktop (или Docker Engine + Compose v2)
|
||||
- 4 GB RAM, 2 CPU
|
||||
- Docker Desktop / Docker Engine + Compose v2
|
||||
- 2 GB RAM (одна нода), 4 GB (3-node dev cluster)
|
||||
- 1 CPU
|
||||
|
||||
Для разработки контрактов дополнительно:
|
||||
- Go 1.21+
|
||||
- TinyGo 0.30+ (только для TinyGo-контрактов)
|
||||
Для разработки из исходников:
|
||||
|
||||
---
|
||||
- Go 1.24+ (модуль использует новые фичи)
|
||||
- TinyGo 0.30+ — только если собираете WASM-контракты вручную
|
||||
|
||||
## 1. Запустить сеть
|
||||
## Проверка жизни
|
||||
|
||||
```bash
|
||||
git clone <repo>
|
||||
cd go-blockchain
|
||||
# высота тип
|
||||
curl -s http://localhost:8080/api/netstats | jq '.total_blocks'
|
||||
|
||||
docker compose up --build -d
|
||||
# версия бинаря
|
||||
docker exec dchain /usr/local/bin/node --version
|
||||
# → dchain-node v0.5.0-dev (commit=abc1234 date=… dirty=false)
|
||||
|
||||
# /api/well-known-version — те же данные + features[]
|
||||
curl -s http://localhost:8080/api/well-known-version | jq .
|
||||
|
||||
# лист возможностей
|
||||
curl -s http://localhost:8080/api/well-known-version | jq '.features'
|
||||
```
|
||||
|
||||
Запускается три ноды:
|
||||
|
||||
| Контейнер | Роль | Explorer |
|
||||
|-----------|------|---------|
|
||||
| node1 | genesis + validator + relay | http://localhost:8081 |
|
||||
| node2 | validator + relay | http://localhost:8082 |
|
||||
| node3 | relay-only observer | http://localhost:8083 |
|
||||
|
||||
Дождитесь пока в Explorer появятся блоки (~10 секунд).
|
||||
|
||||
Swagger: http://localhost:8081/swagger
|
||||
|
||||
---
|
||||
|
||||
## 2. Задеплоить контракты
|
||||
|
||||
```bash
|
||||
docker compose --profile deploy run --rm deploy
|
||||
```
|
||||
|
||||
Скрипт:
|
||||
1. Ждёт готовности node1
|
||||
2. Деплоит 4 контракта из genesis-ключа `/keys/node1.json`
|
||||
3. Вызывает `init` на governance и escrow
|
||||
4. Привязывает governance к нодам через `/api/governance/link`
|
||||
5. Выводит contract ID и сохраняет в `/tmp/contracts.env`
|
||||
|
||||
Пример вывода:
|
||||
```
|
||||
══════════════════════════════════════════════════
|
||||
DChain — деплой production-контрактов
|
||||
══════════════════════════════════════════════════
|
||||
|
||||
▶ Деплой username_registry
|
||||
✓ username_registry contract_id: a1b2c3d4e5f60718
|
||||
|
||||
▶ Деплой governance
|
||||
✓ governance contract_id: 9f8e7d6c5b4a3210
|
||||
|
||||
▶ Деплой auction
|
||||
✓ auction contract_id: 1a2b3c4d5e6f7089
|
||||
|
||||
▶ Деплой escrow
|
||||
✓ escrow contract_id: fedcba9876543210
|
||||
|
||||
✓ username_registry : a1b2c3d4e5f60718
|
||||
✓ governance : 9f8e7d6c5b4a3210
|
||||
✓ auction : 1a2b3c4d5e6f7089
|
||||
✓ escrow : fedcba9876543210
|
||||
```
|
||||
|
||||
Сохраните ID для последующего использования:
|
||||
```bash
|
||||
# Запомнить ID
|
||||
export UR_ID=a1b2c3d4e5f60718
|
||||
export GOV_ID=9f8e7d6c5b4a3210
|
||||
export AUC_ID=1a2b3c4d5e6f7089
|
||||
export ESC_ID=fedcba9876543210
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Первые операции
|
||||
|
||||
### Проверить баланс genesis-кошелька
|
||||
|
||||
```bash
|
||||
docker exec node1 client balance \
|
||||
--key /keys/node1.json \
|
||||
--node http://node1:8080
|
||||
```
|
||||
|
||||
### Создать новый кошелёк
|
||||
|
||||
```bash
|
||||
docker exec node1 wallet keygen --out /tmp/alice.json
|
||||
docker exec node1 client balance \
|
||||
--key /tmp/alice.json \
|
||||
--node http://node1:8080
|
||||
```
|
||||
|
||||
### Перевести токены
|
||||
|
||||
```bash
|
||||
# Получить pubkey Alice
|
||||
ALICE_PUB=$(docker exec node1 sh -c 'cat /tmp/alice.json | grep pub_key' | grep -oP '"pub_key":\s*"\K[^"]+')
|
||||
|
||||
docker exec node1 client transfer \
|
||||
--key /keys/node1.json \
|
||||
--to $ALICE_PUB \
|
||||
--amount 1000000 \
|
||||
--node http://node1:8080
|
||||
```
|
||||
|
||||
### Зарегистрировать username
|
||||
|
||||
```bash
|
||||
docker exec node1 client call-contract \
|
||||
--key /keys/node1.json \
|
||||
--contract $UR_ID \
|
||||
--method register \
|
||||
--arg alice \
|
||||
--gas 20000 \
|
||||
--node http://node1:8080
|
||||
```
|
||||
|
||||
### Отправить сообщение (по username)
|
||||
|
||||
```bash
|
||||
docker exec node1 client send-msg \
|
||||
--key /keys/node1.json \
|
||||
--to @alice \
|
||||
--registry $UR_ID \
|
||||
--msg "Привет!" \
|
||||
--node http://node1:8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Explorer
|
||||
|
||||
После деплоя контракты видны в Explorer:
|
||||
|
||||
```
|
||||
http://localhost:8081/contracts — все контракты
|
||||
http://localhost:8081/contract?id=$UR_ID — username_registry
|
||||
http://localhost:8081/contract?id=$GOV_ID — governance
|
||||
http://localhost:8081/contract?id=$AUC_ID — auction
|
||||
http://localhost:8081/contract?id=$ESC_ID — escrow
|
||||
```
|
||||
|
||||
Вкладки в Explorer на странице контракта:
|
||||
- **Overview** — метаданные, ABI-методы
|
||||
- **State** — query state по ключу
|
||||
- **Logs** — history вызовов с логами
|
||||
- **Raw** — сырой JSON ContractRecord
|
||||
|
||||
---
|
||||
|
||||
## 5. Полный сброс
|
||||
|
||||
```bash
|
||||
docker compose down -v && docker compose up --build -d
|
||||
```
|
||||
|
||||
Флаг `-v` удаляет тома BadgerDB. После пересборки сеть стартует с чистого genesis.
|
||||
|
||||
---
|
||||
|
||||
## Следующие шаги
|
||||
|
||||
- [Контракты](contracts/README.md) — использование всех 4 контрактов
|
||||
- [Разработка контрактов](development/README.md) — написать свой контракт
|
||||
- [CLI](cli/README.md) — все команды клиента
|
||||
- [API](api/README.md) — REST-интерфейс
|
||||
Если `total_blocks` растёт каждые ~5 сек — всё ОК.
|
||||
|
||||
201
docs/update-system.md
Normal file
201
docs/update-system.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Система обновлений и версионирование
|
||||
|
||||
DChain поставляется с полноценной системой update-detection, которая
|
||||
включает **пять слоёв**: build-time версия в бинаре, HTTP-эндпоинты для
|
||||
обнаружения, peer-gossip версий соседей, `update-check` от Gitea, и
|
||||
rolling-restart скрипт. Ниже — как это работает и как использовать.
|
||||
|
||||
## Слой 1. Build-time версия
|
||||
|
||||
Бинарь хранит 4 поля, инжектимые через `-ldflags -X`:
|
||||
|
||||
- `Tag` — человекочитаемый тег, обычно `git describe --tags --always --dirty`
|
||||
- `Commit` — полный 40-символьный SHA коммита
|
||||
- `Date` — RFC 3339 timestamp сборки (UTC)
|
||||
- `Dirty` — `"true"` если сборка была из грязного worktree
|
||||
|
||||
Печать:
|
||||
|
||||
```bash
|
||||
node --version
|
||||
# dchain-node v0.0.1 (commit=abc1234 date=2026-04-17T10:00:00Z dirty=false)
|
||||
|
||||
client --version
|
||||
# тот же формат
|
||||
```
|
||||
|
||||
Все 4 образа (`Dockerfile` и `deploy/prod/Dockerfile.slim`) принимают эти
|
||||
значения через `--build-arg VERSION_TAG=…` и т.д. `update.sh`
|
||||
вычисляет их автоматически перед ребилдом.
|
||||
|
||||
## Слой 2. `/api/well-known-version`
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8080/api/well-known-version | jq .
|
||||
```
|
||||
```json
|
||||
{
|
||||
"node_version": "v0.0.1",
|
||||
"build": {
|
||||
"tag": "v0.0.1",
|
||||
"commit": "abc1234…",
|
||||
"date": "2026-04-17T10:00:00Z",
|
||||
"dirty": "false"
|
||||
},
|
||||
"protocol_version": 1,
|
||||
"features": [
|
||||
"access_token", "chain_id", "channels_v1", "contract_logs",
|
||||
"fan_out", "identity_registry", "native_username_registry",
|
||||
"onboarding_api", "payment_channels", "relay_mailbox",
|
||||
"ws_submit_tx"
|
||||
],
|
||||
"chain_id": "dchain-ddb9a7e37fc8"
|
||||
}
|
||||
```
|
||||
|
||||
Клиент использует это для feature-detection: зная `features[]`, он знает
|
||||
какие экраны/флоу рендерить. Пример в `client-app/lib/api.ts` —
|
||||
функция `checkNodeVersion()`.
|
||||
|
||||
**Protocol version** — отдельная ось от `node_version`. Меняется только
|
||||
при несовместимых изменениях wire-протокола (новый формат PBFT-message,
|
||||
breaking change в tx encoding). Клиент, который ожидает `protocol_version: 1`,
|
||||
не должен работать с нодой `protocol_version: 2`.
|
||||
|
||||
## Слой 3. Peer-gossip версий
|
||||
|
||||
Каждая нода публикует своё `{peer_id, tag, commit, protocol_version,
|
||||
timestamp}` в gossipsub-топик `dchain/version/v1` раз в 60 секунд. Другие
|
||||
ноды эту информацию получают, хранят 15 минут, и отдают через
|
||||
`/api/peers`:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8080/api/peers | jq .
|
||||
```
|
||||
```json
|
||||
{
|
||||
"peers": [
|
||||
{
|
||||
"id": "12D3KooW…",
|
||||
"addrs": ["/ip4/…/tcp/4001/p2p/12D3KooW…"],
|
||||
"version": {
|
||||
"tag": "v0.0.1",
|
||||
"commit": "abc1234…",
|
||||
"protocol_version": 1,
|
||||
"timestamp": 1745000000,
|
||||
"received_at": "2026-04-17T10:01:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Зачем это:
|
||||
|
||||
- Operator видит «какая версия у моих соседей» одним запросом.
|
||||
- Client видит «стоит ли переключиться на другую ноду».
|
||||
- Feature-flag activation (например, `EventChannelBan`) можно запускать
|
||||
только когда ≥80% сети на новой версии.
|
||||
|
||||
## Слой 4. `/api/update-check`
|
||||
|
||||
Оператор настраивает `DCHAIN_UPDATE_SOURCE_URL` на ссылку вида:
|
||||
|
||||
```
|
||||
https://<your-gitea>/api/v1/repos/<owner>/<repo>/releases/latest
|
||||
```
|
||||
|
||||
Нода опрашивает этот URL (15 мин cache, 5 сек timeout) и возвращает:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8080/api/update-check | jq .
|
||||
```
|
||||
```json
|
||||
{
|
||||
"current": { "tag": "v0.0.1", "commit": "…", "date": "…", "dirty": "false" },
|
||||
"latest": { "tag": "v0.0.2", "commit": "…", "url": "https://…", "published_at": "…" },
|
||||
"update_available": true,
|
||||
"checked_at": "2026-04-17T11:00:00Z",
|
||||
"source": "https://git.vsecoder.vodka/api/v1/repos/vsecoder/dchain/releases/latest"
|
||||
}
|
||||
```
|
||||
|
||||
- 503 — не настроен `DCHAIN_UPDATE_SOURCE_URL`.
|
||||
- 502 — upstream (Gitea) недоступен или ответил non-2xx.
|
||||
- `update_available: false` — либо HEAD совпадает, либо Gitea вернула
|
||||
draft/prerelease (оба игнорируются).
|
||||
|
||||
Токен для приватного репо: `DCHAIN_UPDATE_SOURCE_TOKEN=<Gitea PAT>`
|
||||
(scope: `read:repository` достаточно).
|
||||
|
||||
## Слой 5. `update.sh` + systemd timer
|
||||
|
||||
Скрипт в `deploy/single/update.sh`. Флоу:
|
||||
|
||||
1. Если `DCHAIN_UPDATE_SOURCE_URL` задан — сначала спрашивает
|
||||
`/api/update-check`. Если `update_available: false` — выход с кодом 0.
|
||||
2. `git fetch --tags`.
|
||||
3. **Semver guard:** если `UPDATE_ALLOW_MAJOR != true` и major-версия
|
||||
меняется (v1.x → v2.y) — блокирует обновление с exit code 4.
|
||||
4. `git checkout <tag>` (в detached HEAD) или ff-merge на `origin/main`.
|
||||
5. Ребилд образа с правильными `--build-arg VERSION_*`.
|
||||
6. Smoke-test: `docker run --rm … node --version` — должен напечатать
|
||||
новую версию без ошибок.
|
||||
7. `docker compose up -d --force-recreate node`.
|
||||
8. Polling `/api/netstats` до 60 сек — если не ожил, `exit 1`.
|
||||
|
||||
Systemd-интеграция — в `deploy/single/systemd/`:
|
||||
|
||||
```bash
|
||||
sudo cp deploy/single/systemd/dchain-update.{service,timer} /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now dchain-update.timer
|
||||
```
|
||||
|
||||
Таймер: `OnUnitActiveSec=1h` + `RandomizedDelaySec=15min` — чтобы
|
||||
федерация не рестартовала вся одновременно и не уронила PBFT quorum.
|
||||
|
||||
## Schema migrations (BadgerDB)
|
||||
|
||||
Отдельный слой, относящийся к on-disk формату данных. См.
|
||||
`blockchain/schema_migrations.go`:
|
||||
|
||||
- `CurrentSchemaVersion` — const в Go-коде, bumpается с каждой
|
||||
миграцией.
|
||||
- `schemaMetaKey = "schema:ver"` — ключ в BadgerDB хранит фактическую
|
||||
версию данных.
|
||||
- `runMigrations(db)` вызывается при `NewChain()`, применяет каждый
|
||||
шаг форвард-миграций атомарно (data rewrite + version bump в одной
|
||||
badger.Update транзакции).
|
||||
- Если stored version > CurrentSchemaVersion — ошибка (запускаете
|
||||
старый бинарь на новом DB). Fix: обновите бинарь или восстановите
|
||||
из бэкапа.
|
||||
|
||||
На текущий релиз `CurrentSchemaVersion = 0`, миграций нет — scaffold
|
||||
живёт и готов к первому реальному изменению формата.
|
||||
|
||||
## Forward-compat для EventType
|
||||
|
||||
В `blockchain/chain.go` → `applyTx()` добавлен `default:` case для
|
||||
неизвестных `EventType`:
|
||||
|
||||
- Fee дебитуется с отправителя (не спам-вектор).
|
||||
- Tx применяется как **no-op**.
|
||||
- В логе warning «unknown event type … — binary is older than this tx».
|
||||
|
||||
Это значит: новую `EventType` можно подать в сеть, она будет
|
||||
обработана на новых нодах и проигнорирована на старых, без split'а
|
||||
консенсуса. Full design — `deploy/UPDATE_STRATEGY.md` → §5.1.
|
||||
|
||||
## Checklist при релизе
|
||||
|
||||
1. В Git `git tag -a vX.Y.Z -m "release notes"` + `git push origin vX.Y.Z`.
|
||||
2. В Gitea UI: `Releases → New Release → Tag: vX.Y.Z → Publish`.
|
||||
3. На всех нодах с настроенным `update.sh`:
|
||||
- systemd-таймер подхватит через ~1 час (± 15 мин jitter).
|
||||
- Operator может форсить: `sudo systemctl start dchain-update.service`.
|
||||
4. Проверка:
|
||||
```bash
|
||||
curl -s https://<node>/api/well-known-version | jq .build.tag
|
||||
# должно вернуть vX.Y.Z
|
||||
```
|
||||
Reference in New Issue
Block a user