Files
dchain/docs/development/inter-contract.md
vsecoder 7e7393e4f8 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
2026-04-17 14:16:44 +03:00

130 lines
5.2 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 поддерживает вызовы одного контракта из другого через host-функцию `call_contract`. Все вызовы в цепочке исполняются в рамках одной транзакции и одного `badger.Txn`.
## Синтаксис (TinyGo SDK)
```go
import dc "go-blockchain/contracts/sdk"
// Вызвать метод с аргументами
ok := dc.CallContract(contractID, "method_name", `["arg1","arg2"]`)
// Без аргументов
ok := dc.CallContract(contractID, "get_price", "[]")
// С числовым аргументом
ok := dc.CallContract(contractID, "increment", `[42]`)
```
`contractID` — 16-символьный hex-ID контракта (как в Explorer).
## Как это работает
```
tx CALL_CONTRACT → Contract A
│ call_contract(B, "method", args)
Contract B
│ call_contract(C, "method", args)
Contract C
└── [возврат gasUsed]
[gasUsed заряжается на B]
[возврат gasUsed]
[gasUsed заряжается на A]
```
### Атомарность
Все изменения состояния в цепочке вызовов используют **одну** `badger.Txn`. Если родительский вызов завершился ошибкой, изменения всех подвызовов откатываются вместе с ним. Если подвызов вернул 1 (ошибку), родитель должен сам решить: паниковать или продолжать.
### Gas
Gas-бюджет подвызова = `gc.Remaining()` родителя (оставшийся gas на момент вызова). После возврата, `gasUsed` подвызова списывается с родительского счётчика. Таким образом, весь gas всей цепочки вычитается из единственного `--gas` лимита транзакции.
### Caller
В подвызове `dc.Caller()` возвращает contractID **вызывающего контракта** (не исходного пользователя).
```
user → Contract A → Contract B
dc.Caller() == Contract A's ID
```
### Глубина
Максимальная глубина вложенности: **8**.
`A → B → C → ... → H` (8 уровней) — допустимо.
Попытка вызвать 9-й уровень вернёт ошибку, транзакция откатится.
## Пример: price oracle
```go
// contracts/mycontract/main.go
package main
import dc "go-blockchain/contracts/sdk"
const priceOracleID = "abcd1234abcd1234" // ID oracle-контракта
//export buy
func buy() {
amount := dc.ArgU64(0)
// Узнать цену у другого контракта
if !dc.CallContract(priceOracleID, "get_price", "[]") {
dc.Log("oracle unavailable")
return
}
// После вызова цена может быть записана в shared state под известным ключом,
// или oracle мог сделать transfer напрямую.
dc.Log("buy executed")
}
func main() {}
```
## Пример: ping (hello_go)
```go
//export ping
func ping() {
target := dc.ArgStr(0, 64)
if target == "" {
dc.Log("no target")
return
}
ok := dc.CallContract(target, "get", "[]")
if ok {
dc.Log("ping ok")
} else {
dc.Log("ping failed")
}
}
```
## Ограничения
| Параметр | Значение |
|---------|---------|
| Максимальная глубина | 8 |
| Разделяемый state | одна `badger.Txn` |
| Caller в подвызове | contractID родителя |
| Gas бюджет подвызова | `Remaining()` родителя |
| Возврат данных | только через state (нет return value) |
> **Нет возврата значений.** `call_contract` возвращает только 0/1 (успех/ошибка). Для передачи данных из подвызова обратно: запишите в state под известным ключом или используйте промежуточный общий контракт.
## Безопасность
- Не доверяйте `dc.Caller()` из подвызванного контракта — он будет contractID инициатора, который может быть любым задеплоенным контрактом.
- Проверяйте contractID перед вызовом, если вам важно с кем вы говорите.
- Помните что подвызов может изменять общий state — не вызывайте непроверенные контракты.