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

182
scripts/deploy_contracts.sh Normal file
View File

@@ -0,0 +1,182 @@
#!/bin/sh
# deploy_contracts.sh — деплой всех 4 production-контрактов из genesis-кошелька.
#
# Запуск:
# docker compose --profile deploy run --rm deploy
#
# После деплоя contract_id'ы выводятся в stdout и сохраняются в /tmp/contracts.env.
# Контракты видны в Explorer: http://localhost:8081/contracts
set -e
# ── Конфигурация нод (через backbone IP, без DNS) ─────────────────────────────
NODE1_URL="${NODE1_URL:-http://172.30.0.11:8080}"
NODE2_URL="${NODE2_URL:-http://172.30.0.12:8080}"
NODE3_URL="${NODE3_URL:-http://172.30.0.13:8080}"
# Основная нода для деплоя (genesis-кошелёк на node1)
NODE="$NODE1_URL"
GENESIS_KEY="/keys/node1.json"
BOLD='\033[1m'
CYAN='\033[1;36m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
RESET='\033[0m'
step() { printf "\n${CYAN}▶ %s${RESET}\n" "$*"; }
ok() { printf " ${GREEN}${RESET} %s\n" "$*"; }
fail() { printf " ${RED}${RESET} %s\n" "$*"; exit 1; }
info() { printf " %s\n" "$*"; }
# ── Ожидание готовности ноды ──────────────────────────────────────────────────
wait_node() {
local name="$1" url="$2"
step "Ожидание готовности $name ($url)..."
i=0
while [ $i -lt 90 ]; do
if wget -qO /dev/null "$url/api/netstats" 2>/dev/null; then
ok "$name доступен"; break
fi
printf "."; sleep 1; i=$((i+1))
done
[ $i -lt 90 ] || fail "$name недоступен после 90 секунд"
}
wait_node "node1" "$NODE1_URL"
wait_node "node2" "$NODE2_URL"
wait_node "node3" "$NODE3_URL"
printf " Ждём genesis block на node1"
i=0
while [ $i -lt 60 ]; do
if wget -qO- "$NODE/api/netstats" 2>/dev/null | grep -q '"total_blocks": *[1-9]'; then
printf " — готово.\n"; break
fi
printf "."; sleep 1; i=$((i+1))
done
[ $i -lt 60 ] || fail "Genesis block не появился"
# ── Хелпер: деплой одного контракта ──────────────────────────────────────────
deploy_contract() {
local name="$1" wasm="$2" abi="$3"
step "Деплой $name"
OUT=$(client deploy-contract \
--key "$GENESIS_KEY" \
--wasm "$wasm" \
--abi "$abi" \
--node "$NODE" 2>&1)
printf '%s\n' "$OUT"
CID=$(printf '%s' "$OUT" | grep 'contract_id:' | awk '{print $NF}')
[ -n "$CID" ] || fail "Не удалось получить contract_id для $name"
# Ждём подтверждения on-chain
printf " Ждём подтверждения"
i=0
while [ $i -lt 40 ]; do
if wget -qO- "$NODE/api/contracts/$CID" 2>/dev/null | grep -q '"contract_id"'; then
printf " — задеплоен.\n"; break
fi
printf "."; sleep 1; i=$((i+1))
done
[ $i -lt 40 ] || fail "Timeout ожидания контракта $name"
ok "$name contract_id: $CID"
echo "$CID"
}
# ── Инициализация контракта (вызов init) ──────────────────────────────────────
init_contract() {
local name="$1" cid="$2"
step "Инициализация $name (вызов init)"
client call-contract \
--key "$GENESIS_KEY" \
--contract "$cid" \
--method init \
--gas 50000 \
--node "$NODE" 2>&1 | grep -v '^$' || true
sleep 2
ok "$name инициализирован"
}
# ── Деплой всех контрактов ────────────────────────────────────────────────────
printf "\n${YELLOW}══════════════════════════════════════════════════${RESET}\n"
printf "${BOLD} DChain — деплой production-контрактов${RESET}\n"
printf "${YELLOW}══════════════════════════════════════════════════${RESET}\n"
UR_ID=$(deploy_contract "username_registry" \
"/keys/username_registry.wasm" \
"/keys/username_registry_abi.json")
GOV_ID=$(deploy_contract "governance" \
"/keys/governance.wasm" \
"/keys/governance_abi.json")
AUC_ID=$(deploy_contract "auction" \
"/keys/auction.wasm" \
"/keys/auction_abi.json")
ESC_ID=$(deploy_contract "escrow" \
"/keys/escrow.wasm" \
"/keys/escrow_abi.json")
# Инициализируем контракты с admin-ролью
init_contract "governance" "$GOV_ID"
init_contract "escrow" "$ESC_ID"
# ── Линковка governance со всеми нодами (runtime, без перезапуска) ────────────
link_governance() {
local name="$1" url="$2"
step "Линковка governance с $name ($url)"
RESP=$(wget -qO- --post-data="{\"governance\":\"$GOV_ID\"}" \
--header="Content-Type: application/json" \
"$url/api/governance/link" 2>/dev/null || true)
if printf '%s' "$RESP" | grep -q '"ok"'; then
ok "governance привязан — gas_price управляется on-chain"
else
info "нода недоступна или уже настроена: $RESP"
fi
}
link_governance "node1" "$NODE1_URL"
link_governance "node2" "$NODE2_URL"
link_governance "node3" "$NODE3_URL"
# ── Итоговый вывод ────────────────────────────────────────────────────────────
printf "\n${YELLOW}══════════════════════════════════════════════════${RESET}\n"
printf "${BOLD} Все контракты задеплоены!${RESET}\n"
printf "${YELLOW}══════════════════════════════════════════════════${RESET}\n\n"
ok "username_registry : $UR_ID"
ok "governance : $GOV_ID"
ok "auction : $AUC_ID"
ok "escrow : $ESC_ID"
printf "\n${BOLD}Explorer:${RESET}\n"
info "node1 → http://localhost:8081/contracts"
info "node2 → http://localhost:8082/contracts"
info "node3 → http://localhost:8083/contracts"
printf "\n"
info "http://localhost:8081/contract?id=$UR_ID (username_registry)"
info "http://localhost:8081/contract?id=$GOV_ID (governance)"
info "http://localhost:8081/contract?id=$AUC_ID (auction)"
info "http://localhost:8081/contract?id=$ESC_ID (escrow)"
# Сохраняем ID для последующего использования
cat > /tmp/contracts.env << EOF
USERNAME_REGISTRY=$UR_ID
GOVERNANCE=$GOV_ID
AUCTION=$AUC_ID
ESCROW=$ESC_ID
EOF
printf "\n"
ok "ID сохранены в /tmp/contracts.env"
echo