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

View File

@@ -0,0 +1,249 @@
# Бинарный WASM
Альтернативный способ написания контрактов — генерация WASM-байткода вручную. Используется для системных/минимальных контрактов где критичен размер (~5002000 байт вместо 30100 KB TinyGo).
> **Рекомендуется TinyGo** для новых контрактов. Бинарный WASM используется для встроенных контрактов в этом проекте.
## Структура генератора
```
contracts/
mycontract/
gen/
main.go # Go-программа, печатает WASM байты
mycontract.wasm # Результат: go run gen/main.go > mycontract.wasm
mycontract_abi.json
```
Генератор запускается стандартным Go (`go run gen/main.go`) и выводит бинарный WASM в stdout.
## Анатомия WASM модуля
```
Magic + Version : \0asm\x01\x00\x00\x00
Секции (по порядку):
1. Type — сигнатуры функций
2. Import — импортируемые host-функции ("env" модуль)
3. Function — индексы типов для локальных функций
4. Export — экспортируемые функции (методы контракта)
5. Code — тела функций
6. Data — статические строки в памяти
7. Memory — объявление памяти (мин. 1 страница = 64 KB)
```
## LEB128 кодирование
Целые числа в WASM кодируются в LEB128 (variable-length encoding).
```go
// Unsigned LEB128
func u(v uint64) []byte {
var out []byte
for {
b := byte(v & 0x7f)
v >>= 7
if v != 0 {
b |= 0x80
}
out = append(out, b)
if v == 0 {
break
}
}
return out
}
// Signed LEB128 (для i32/i64 константы)
func s(v int64) []byte {
var out []byte
for {
b := byte(v & 0x7f)
v >>= 7
if (v == 0 && b&0x40 == 0) || (v == -1 && b&0x40 != 0) {
out = append(out, b)
break
}
out = append(out, b|0x80)
}
return out
}
```
## Вспомогательные функции
```go
// Секция с длиной-префиксом
func section(id byte, content []byte) []byte {
return append(append([]byte{id}, u(uint64(len(content)))...), content...)
}
// Вектор (count + элементы)
func vec(items ...[]byte) []byte {
out := u(uint64(len(items)))
for _, item := range items {
out = append(out, item...)
}
return out
}
// Строка с длиной-префиксом
func str(s string) []byte {
return append(u(uint64(len(s))), []byte(s)...)
}
```
## WASM инструкции
Наиболее используемые опкоды:
| Инструкция | Байт | Описание |
|-----------|------|---------|
| `local.get` | `0x20 + leb(idx)` | Прочитать локальную переменную |
| `local.set` | `0x21 + leb(idx)` | Записать локальную переменную |
| `local.tee` | `0x22 + leb(idx)` | Записать и оставить на стеке |
| `i32.const` | `0x41 + sleb(val)` | Константа i32 |
| `i64.const` | `0x42 + sleb(val)` | Константа i64 |
| `i32.load` | `0x28 0x02 leb(offset)` | Загрузить 4 байта |
| `i32.store` | `0x36 0x02 leb(offset)` | Сохранить 4 байта |
| `i64.load` | `0x29 0x03 leb(offset)` | Загрузить 8 байт |
| `i64.store` | `0x37 0x03 leb(offset)` | Сохранить 8 байт |
| `i32.add` | `0x6A` | Сложение i32 |
| `i32.sub` | `0x6B` | Вычитание i32 |
| `i32.eq` | `0x46` | Равенство i32 |
| `i64.eq` | `0x51` | Равенство i64 |
| `if` | `0x04 0x40` | Ветвление (void) |
| `else` | `0x05` | — |
| `end` | `0x0B` | Конец блока/функции |
| `return` | `0x0F` | Возврат из функции |
| `call` | `0x10 + leb(funcIdx)` | Вызов функции |
| `drop` | `0x1A` | Убрать верхний элемент стека |
## Шаблон генератора
```go
package main
import (
"os"
)
// Импорты host-функций
const (
fnGetArgStr = 0 // индекс в таблице импортов
fnSetState = 1
fnGetState = 2
fnGetStateLen = 3
fnGetCaller = 4
fnLog = 5
)
// Смещения в памяти
const (
pKey = 0 // буфер для ключей state
pVal = 128 // буфер для значений
pCaller = 256 // буфер для caller
pArg0 = 320 // буфер для аргумента 0
pStatic = 400 // статические строки (из Data секции)
)
func main() {
// 1. Magic + version
wasm := []byte{0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00}
// 2. Type секция — сигнатуры функций
// ...
// 3. Import секция — host функции
// ...
// 4. Function + Memory + Export + Code + Data
// ...
os.Stdout.Write(wasm)
}
```
## Пример: функция increment
Аналог Go-кода:
```go
func increment() {
count := getU64("counter")
putU64("counter", count+1)
log("incremented")
}
```
В бинарном WASM:
```go
// В Data секции: "counter\x00" @ offset 0, "incremented\x00" @ offset 8
// Функция increment:
func buildIncrement() []byte {
// locals: none
body := []byte{0x00} // local decl count = 0
// load key "counter" (ptr=0, len=7) → call get_u64 → result on stack
body = append(body, 0x41) // i32.const
body = append(body, s(0)...) // ptr = 0 (offset of "counter" in data)
body = append(body, 0x41)
body = append(body, s(7)...) // len = 7
body = append(body, 0x10) // call
body = append(body, u(fnGetU64)...)
// add 1
body = append(body, 0x42, 0x01) // i64.const 1
body = append(body, 0x7C) // i64.add
// put_u64("counter", result)
body = append(body, 0x21, 0x00) // local.set 0 (tmp)
body = append(body, 0x41)
body = append(body, s(0)...)
body = append(body, 0x41)
body = append(body, s(7)...)
body = append(body, 0x20, 0x00) // local.get 0
body = append(body, 0x10)
body = append(body, u(fnPutU64)...)
// log("incremented")
body = append(body, 0x41)
body = append(body, s(8)...) // ptr = 8 (offset of "incremented")
body = append(body, 0x41)
body = append(body, s(11)...) // len = 11
body = append(body, 0x10)
body = append(body, u(fnLog)...)
body = append(body, 0x0B) // end
return append(u(uint64(len(body))), body...)
}
```
## Сборка и деплой
```bash
# Генерация
go run contracts/mycontract/gen/main.go > contracts/mycontract/mycontract.wasm
# Проверка (wasm-objdump из wabt)
wasm-objdump -d contracts/mycontract/mycontract.wasm
# Деплой
client deploy-contract \
--key key.json \
--wasm contracts/mycontract/mycontract.wasm \
--abi contracts/mycontract/mycontract_abi.json \
--node http://localhost:8081
```
## Встроенные контракты в проекте
| Контракт | Генератор | Размер .wasm |
|---------|----------|-------------|
| counter | contracts/counter/gen/main.go | ~500 байт |
| governance | contracts/governance/gen/main.go | ~800 байт |
| name_registry | contracts/name_registry/gen/main.go | ~1.2 KB |
| username_registry | contracts/username_registry/gen/main.go | ~1.5 KB |
| escrow | contracts/escrow/gen/main.go | ~2 KB |
| auction | contracts/auction/gen/main.go | ~2.5 KB |
Все генераторы используют одни и те же LEB128-утилиты и паттерн `vec/section/str`.