Files
dchain/docs/update-system.md
vsecoder 546d2c503f 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
2026-04-17 14:37:00 +03:00

202 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Система обновлений и версионирование
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
```