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,193 @@
# Gas и Treasury
## Gas модель
Каждый вызов контракта (`CALL_CONTRACT` транзакция) тратит gas. Gas конвертируется в µT и списывается со счёта отправителя.
### Формула
```
fee = gas_used × gas_price
```
| Параметр | Значение | Откуда |
|---------|---------|-------|
| `gas_used` | ≤ `--gas` лимит | считается VM |
| `gas_price` | 1 µT/gas по умолчанию | governance или константа |
### Лимит gas
Задаётся в транзакции флагом `--gas`:
```bash
client call-contract --method increment \
--gas 5000 \
--contract $CONTRACT_ID --key key.json --node http://localhost:8081
```
Если VM израсходует весь лимит до конца исполнения — транзакция откатывается, gas не возвращается.
### Что стоит gas
| Операция | Стоимость |
|---------|---------|
| Итерация цикла (`gas_tick`) | 1 unit |
| Внешний вызов (`call_contract`) | gas подвызова |
| Прочие инструкции | 0 (не инструментированы) |
> TinyGo автоматически вставляет `gas_tick` в циклы. Бинарные WASM-контракты вызывают `gas_tick` вручную.
### Межконтрактный gas
При вызове `dc.CallContract(...)`, подвызов получает budget = `Remaining()` родителя.
После возврата, `gasUsed` подвызова списывается с родительского счётчика.
```
Родитель: limit=5000, used=200
→ подвызов budget = 4800
→ подвызов использовал 300
Родитель: used = 200 + 300 = 500
```
## Treasury
Каждый контракт имеет **treasury** — специальный баланс, привязанный к контракту.
### Получить адрес treasury
```go
treasury := dc.Treasury() // hex pubkey, 64 символа
```
### Переводы через treasury
Treasury используется как промежуточный эскроу:
```go
// Принять деньги от caller → treasury
dc.Transfer(dc.Caller(), dc.Treasury(), amount)
// Отправить деньги из treasury → получателю
dc.Transfer(dc.Treasury(), recipient, amount)
```
**Ограничение:** в `dc.Transfer(from, ...)`, `from` может быть только:
1. `dc.Caller()` — списать со счёта вызывающего
2. `dc.Treasury()` — списать с treasury контракта
Контракт **не может** списывать деньги с произвольных адресов.
### Проверить баланс treasury
```go
treasuryBal := dc.Balance(dc.Treasury())
```
### Паттерны использования treasury
**1. Fee collector:**
```go
const fee = 1000 // µT
dc.Transfer(dc.Caller(), dc.Treasury(), fee)
// Деньги остаются в treasury навсегда (или до явного вывода)
```
**2. Эскроу (lock → release):**
```go
// lock: buyer → treasury
dc.Transfer(buyer, dc.Treasury(), amount)
// release: treasury → seller
dc.Transfer(dc.Treasury(), seller, amount)
// refund: treasury → buyer
dc.Transfer(dc.Treasury(), buyer, amount)
```
**3. Prize pool (аукцион):**
```go
// Принять ставку
dc.Transfer(bidder, dc.Treasury(), bid)
// Возврат предыдущей ставки
dc.Transfer(dc.Treasury(), prevBidder, prevBid)
// Финал: перевод победителю
dc.Transfer(dc.Treasury(), seller, topBid)
```
## Governance и динамический gas_price
По умолчанию `gas_price = 1 µT/gas`. Это значение можно изменить через governance-контракт.
### Как нода читает gas_price
```go
// blockchain/chain.go
func (c *Chain) GetEffectiveGasPrice() uint64 {
val, ok := c.GetGovParam("gas_price")
if !ok { return GasPrice } // константа по умолчанию
price, err := strconv.ParseUint(val, 10, 64)
if err != nil { return GasPrice }
return price
}
```
`GetGovParam` читает `cstate:<govID>:param:gas_price` напрямую из BadgerDB без VM-вызова.
### Изменить gas_price через governance
```bash
# Предложить новое значение (5 µT/gas)
client call-contract --method propose \
--arg gas_price --arg 5 \
--contract $GOV_ID --key /keys/node1.json \
--gas 10000 --node http://node1:8080
# Admin утверждает
client call-contract --method approve \
--arg gas_price \
--contract $GOV_ID --key /keys/node1.json \
--gas 10000 --node http://node1:8080
```
После approve все последующие `CALL_CONTRACT` транзакции будут использовать новую цену.
### Проверить текущий gas_price
```bash
# Через контракт
client call-contract --method get \
--arg gas_price \
--contract $GOV_ID --key key.json \
--gas 3000 --node http://localhost:8081
# Через REST (без транзакции)
curl http://localhost:8081/api/contracts/$GOV_ID/state/param:gas_price
```
## Константы
```go
// blockchain/chain.go
const (
GasPrice = uint64(1) // µT за 1 gas unit (default)
)
// vm/host.go
const (
maxContractCallDepth = 8
)
```
## Рекомендации по выбору --gas
| Операция | Рекомендуемый gas |
|---------|-----------------|
| Простое чтение/запись state | 3 000 |
| Регистрация / короткий метод | 5 000 |
| Метод с несколькими state операциями | 10 000 |
| Создание эскроу/аукциона | 20 000 30 000 |
| Метод с межконтрактным вызовом | 30 000 50 000 |
Gas, который не был потрачён, **не возвращается**. Завышенный лимит безопасен, но лишних денег не списывается больше `gas_used × gas_price`.