Astraguard/Contracts
GitHub: Astraguard/Contracts
面向 Stellar 生态的 Soroban 智能合约集合,提供条件支付托管、保险资金池和链上欺诈标志注册表,解决链上交易信任与安全问题。
Stars: 0 | Forks: 0
# Astraguard 合约
[](https://github.com/Astraguard/Contracts/actions/workflows/contract-ci.yml)
Astraguard 的 Soroban 智能合约,Stellar 的信任与安全层:带有争议解决机制的条件支付托管、以真实承保为后盾支持已验证项目的保险资金池,以及将已确认的欺诈标志打上时间戳锚定在链上以供公众审计的注册表锚点。
本代码库**仅包含链上层**:托管、保险资金池和注册表锚点合约。链下验证、评分以及任何仪表板/前端均不在此范围内。
## 概述
Astraguard 的合约为 Stellar 生态系统提供了链上极少存在的安全原语:只有在条件实际满足(或仲裁员另有决定)时才释放的支付、为通过链下验证的项目提供的资金支持保险,以及任何钱包或协议在信任交易对手之前都可以检查的已确认欺诈的公开、不可变记录。
每一次特权写入——承保决策、欺诈标志——都受限于单一的 oracle 地址;这些合约中除了通过这一个授权账户外,不信任任何链下数据。
## 功能
- **条件托管**:资金在 `create` 时锁定,并在买家确认、超时通过或争议解决后释放给卖家
- **争议解决**:买家或卖家均可提出争议;托管的指定仲裁员作出具有约束力的决定——包括部分分割
- **保险资金池**:保费资助一个共享资金池;oracle 在偿付能力比例上限内向已验证的项目授予承保,理赔委员会批准或拒绝赔付
- **注册表锚点**:已确认的欺诈标志从账本时钟(而非调用者提供)获取时间戳并永久锚定,支持按主体查询
- **时间锁定的管理员交接**:每个合约的管理员只能通过 48 小时的提议 → 接受流程进行更改,因此被盗用或恶意的管理员密钥无法静默生效
- **TTL 管理的存储**:每次对持久化存储的写入都会增加该条目的 TTL,并且每次状态更改调用都会增加合约实例的 TTL,因此托管/理赔/标志不会在账本外静默过期
## 代码库结构
```
astraguard-contracts/
├── contracts/
│ ├── shared/ # astraguard-shared: access control, timelock, TTL helpers
│ │ └── src/{access,timelock,ttl}.rs
│ ├── escrow/ # Conditional payment escrow
│ │ └── src/{lib,test}.rs
│ ├── insurance-pool/ # Premiums, coverage, claims, payouts
│ │ └── src/{lib,test}.rs
│ └── registry-anchor/ # On-chain anchor of confirmed fraud flags
│ └── src/{lib,test}.rs
├── scripts/
│ ├── build.sh # cargo build + stellar contract optimize, all contracts
│ └── deploy.sh # deploy all three to a given network, record IDs
├── deployments/
│ ├── testnet.json # contract IDs per network, filled in by deploy.sh
│ └── mainnet.json
├── tests/README.md # scope for future cross-contract integration tests
└── Cargo.toml # Rust workspace
```
## 架构
```
graph TB
subgraph Users
BY[Buyer]
SE[Seller]
AR[Arbiter]
OR[Oracle — backend-controlled]
CM[Claims Committee]
VI[Victim / Claimant]
end
subgraph Contract["Smart Contracts (Soroban / Rust)"]
ESC[escrow — create / release / dispute / resolve]
POOL[insurance-pool — coverage, claims, payouts]
REG[registry-anchor — confirmed fraud flags]
SHR[shared — access control, timelock, TTL]
end
subgraph Stellar["Stellar Network"]
LEDGER[Ledger]
ASSET[Token Contract — e.g. USDC]
end
BY -->|create, funds locked| ESC
SE -->|release| ESC
BY -->|dispute| ESC
SE -->|dispute| ESC
AR -->|resolve| ESC
ESC -->|transfer| ASSET
SE -->|deposit_premium| POOL
OR -->|set_coverage| POOL
VI -->|file_claim| POOL
CM -->|approve_claim / reject_claim| POOL
POOL -->|payout| ASSET
OR -->|anchor_flag| REG
REG -->|timestamp from ledger| LEDGER
ESC -.-> SHR
POOL -.-> SHR
REG -.-> SHR
```
### 核心组件
- **`contracts/escrow`**:锁定买家资金,在确认/超时时释放给卖家,并在争议发生时执行仲裁员的决定
- **`contracts/insurance-pool`**:收取保费,在偿付能力比例上限内跟踪每个项目的承保情况,并在赔付前通过委员会审批处理理赔
- **`contracts/registry-anchor`**:仅限 oracle 追加已确认的欺诈标志,经过哈希处理并打上时间戳,可按主体查询
- **`contracts/shared`** (`astraguard-shared`):通用的管理员/oracle 访问控制(`access.rs`)、时间锁定的管理员交接流程(`timelock.rs`)以及这三个合约都使用的 TTL 增加辅助程序(`ttl.rs`)
### 托管分割模型
当仲裁员解决争议(`Decision`)时在链上强制执行的结果:
| 决策 | 卖家 | 买家 |
|------------------------|------------|------------|
| 无争议 (`release`) | 100% | 0% |
| `ReleaseToSeller` | 100% | 0% |
| `RefundToBuyer` | 0% | 100% |
| `Split(seller_bps)` | `seller_bps` / 10000 | 剩余部分 |
## 技术栈
| 组件 | 技术 | 用途 |
|-------------------|---------------------------|-------------------------------------------------------------------|
| 智能合约 | Rust + Soroban SDK 26 | 托管、保险资金池、注册表锚点 |
| 资产结算 | 任何 Soroban token 合约(例如 Stellar USDC) | 托管资金、保费、保险赔付 |
| 钱包 / 认证 | Freighter Wallet(通过前端代码库) | 签署买家/卖家/仲裁员/委员会交易 |
| 链下索引 | 合约事件 | 由后端索引器使用,不由本代码库使用 |
## 智能合约函数
### escrow
- `initialize(admin)` — 一次性设置
- `create(buyer, seller, arbiter, asset, amount, timeout, conditions)` — 锁定来自 `buyer` 的 `amount` 数量的 `asset`;`conditions` 是链下条款文档的哈希值
- `release(escrow_id)` — 随时由买家授权,或在 `timeout` 过去后无需许可
- `dispute(escrow_id, caller, reason)` — `caller` 必须是买家或卖家
- `resolve(escrow_id, decision)` — 仅限仲裁员,具有约束力
- `get_escrow(escrow_id)` — 完整记录和状态
- `propose_admin(candidate)` / `accept_admin()` — 时间锁定的管理员交接
### insurance-pool
- `initialize(admin, oracle, asset, coverage_ratio_bps, committee, approval_threshold)`
- `deposit_premium(from, project, amount)` — 流入资金池的资金
- `set_coverage(project, status, amount)` — **仅限 oracle**;如果会导致总有效承保额超过资金池余额的 `coverage_ratio_bps`,则会被拒绝
- `file_claim(project, victim, amount, evidence_hash)` — 要求项目具有 `Active` 承保
- `approve_claim(claim_id, member)` — 委员会成员投票;一旦 `approval_threshold` 个不同成员批准,理赔即变为可赔付状态
- `reject_claim(claim_id, member)` — 任何单个委员会成员都可以拒绝仍处于 `Filed` 状态的理赔
- `payout(claim_id)` — 从资金池资本中支付 `Approved` 的理赔
- `get_coverage(project)`, `get_claim(claim_id)`, `pool_balance()` — 查询
- `propose_admin(candidate)` / `accept_admin()`
### registry-anchor
- `initialize(admin, oracle)`
- `anchor_flag(subject, record_hash, category)` — **仅限 oracle**;时间戳取自账本时钟,而不是调用者提供的参数,因此标志不能被倒填日期
- `get_flag(flag_id)`, `get_flags_for_subject(subject)` — 查询
- `propose_admin(candidate)` / `accept_admin()`
## 托管生命周期 — 时序图
```
sequenceDiagram
actor Buyer
actor Seller
actor Arbiter
participant Contract as escrow
participant Asset as Token Contract
rect rgb(235, 245, 255)
Note over Buyer,Contract: Create — one signed transaction
Buyer->>Contract: create(seller, arbiter, asset, amount, timeout, conditions)
Contract->>Asset: transfer(buyer → contract, amount)
Contract-->>Buyer: escrow_id, status = Active
end
rect rgb(240, 255, 240)
Note over Seller,Contract: Happy path
Buyer->>Contract: release(escrow_id)
Contract->>Asset: transfer(contract → seller, amount)
Contract-->>Seller: status = Released
end
rect rgb(255, 235, 235)
Note over Buyer,Arbiter: Dispute path
Buyer->>Contract: dispute(escrow_id, buyer, reason)
Contract-->>Contract: status = Disputed
Arbiter->>Contract: resolve(escrow_id, decision)
Contract->>Asset: transfer(s) per decision
Contract-->>Buyer: status = Resolved
Contract-->>Seller: status = Resolved
end
```
## 托管生命周期 — 状态机
`EscrowStatus`,由 `contracts/escrow/src/lib.rs` 强制执行:
```
┌────────┐
│ Active │ ← create() locked funds; buyer or seller may act
└───┬────┘
│
├─────────────────────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Released │ │ Disputed │ ← buyer or seller called dispute()
└──────────┘ └────┬─────┘
(terminal) │
▼
┌──────────┐
│ Resolved │ ← arbiter called resolve() (terminal)
└──────────┘
```
### 有效状态转换
| 起点 | 目标 | 触发器 |
|-----------|-----------|------------------------------------------------------------------|
| Active | Released | 买家调用 `release`,或在 `timeout` 后由任何人调用 |
| Active | Disputed | 买家或卖家调用 `dispute` |
| Disputed | Resolved | 仲裁员调用 `resolve` — 资金根据 `Decision` 移动 |
## 保险理赔生命周期
`ClaimStatus`,由 `contracts/insurance-pool/src/lib.rs` 强制执行:
```
Filed ──approve_claim (≥ threshold)──▶ Approved ──payout──▶ PaidOut
│
└──reject_claim (any 1 committee member)──▶ Rejected (terminal)
```
## 安全特性
1. **托管资金隔离**:每个托管的资金和状态都是独立跟踪的——买家或卖家之间没有混合
2. **仲裁员/委员会门控操作**:`resolve` 需要特定托管的仲裁员;`set_coverage`/`anchor_flag` 需要 oracle;`approve_claim`/`reject_claim` 需要委员会成员资格——全部通过 `require_auth` 强制执行,而不仅仅是约定
3. **原子结算**:释放、争议分割和理赔赔付在与状态转换相同的交易中移动资金——没有部分赔付
4. **偿付能力防护**:`insurance-pool` 拒绝会使总风险敞口超过资金池实际 token 余额的 `coverage_ratio_bps` 的新有效承保
5. **不可变注册表**:已确认的锚定欺诈标志无法编辑或删除;时间戳来自账本时钟,而不是调用者
6. **时间锁定的管理员交接**:管理员更改需要在每个合约上执行 `propose_admin` → 48小时等待 → `accept_admin` 流程(`astraguard-shared::timelock`)
7. **TTL 延长**:每次持久写入和每次状态更改调用都会增加存储 TTL(`astraguard-shared::ttl`),因此实时数据不会在两次访问之间在账本外过期
8. **检查-效果-交互模式**:`escrow::release`/`resolve` 和 `insurance-pool::payout` 在调用 token 合约的 `transfer` 之前写入已结算状态(状态、TTL、承保总额),而不是之后——崩溃或恶意的 `asset` 合约无法重入以查看(或利用)陈旧的 `Active`/`Approved` 状态,并且失败的传输仍会在 Soroban 中回滚整个调用,因此这在成功路径上没有任何成本
**已知的差距,直接指出而不是隐藏起来:** `arbiter`、`oracle` 和每个委员会成员目前都是单个 Stellar 地址,而不是链上的 multisig 账户——在这些角色背后实现真正的 multisig/timelock 是迈向主网之前的实际工作。目前也没有暂停/熔断功能;既然没有实现,这里就不做声明。`ttl.rs` 中的 TTL 阈值/增加常量是合理的初始值,尚未针对特定网络的租金费用经济学进行调优。`env.events().publish(...)` 已被弃用,取而代之的是 `#[contractevent]` 宏(soroban-sdk 26);迁移会更改链上的事件编码,因此它被留作刻意的后续任务,而不是随手的重命名——请参阅路线图。
## 快速开始
需要带有 `wasm32v1-none` target 的 Rust 工具链(参见 `rust-toolchain.toml`)和 [`stellar` CLI](https://developers.stellar.org/docs/tools/stellar-cli)。
### 1. 构建和测试
```
cargo test --workspace
./scripts/build.sh
```
`scripts/build.sh` 为 `wasm32v1-none` 构建所有三个合约,并对每个合约运行 `stellar contract optimize`。
### 2. 部署
```
./scripts/deploy.sh testnet
```
这会部署 `escrow`、`insurance-pool` 和 `registry-anchor`,并将它们的合约 ID 写入 `deployments/testnet.json`。它**不会**调用 `initialize`——每个合约的构造函数参数(admin、oracle、asset、committee、...)取决于特定于您部署的地址。
### 3. 初始化每个合约
```
stellar contract invoke --id --source --network testnet \
-- initialize --admin
stellar contract invoke --id --source --network testnet \
-- initialize --admin --oracle --asset \
--coverage_ratio_bps 5000 --committee '[,]' --approval_threshold 2
stellar contract invoke --id --source --network testnet \
-- initialize --admin --oracle
```
## 工作原理
1. **托管** — 买家调用 `create`;资金在一笔已签名的交易中锁定。卖家(或任何人,在超时后)调用 `release` 走正常流程。任何一方都可以发起 `dispute`;仲裁员的 `resolve` 调用是最终决定,并根据其 `Decision` 移动资金。
2. **保险** — 保费通过 `deposit_premium` 累积。oracle(由后端的信任评分服务驱动)通过 `set_coverage` 授予 `Active` 承保,受偿付能力防护限制。受害者提出理赔;理赔委员会通过 `approve_claim` 达到 `approval_threshold`(或任何成员可以调用 `reject_claim`);已批准的理赔通过 `payout` 支付。
3. **注册表** — 一旦后端的双人审核确认了欺诈报告,oracle 就会调用 `anchor_flag`。哈希值和账本时间戳是永久的;任何人都可以在信任某个地址之前调用 `get_flags_for_subject`。
## 测试
```
cargo test --workspace
```
每个合约的当前单元测试覆盖率(`contracts//src/test.rs`):
- **escrow**:create + 正常流程的 release;dispute → 仲裁员分割解决;非当事方无法发起争议
- **insurance-pool**:premium deposit → coverage → claim → 委员会 approval → payout;超出偿付能力比例的 coverage 被拒绝;委员会成员对已提出 claim 的拒绝;非委员会成员无法批准或拒绝
- **registry-anchor**:按 ID 和主体 anchor + 查询 flag;每个主体累积多个 flag
尚未覆盖:跨合约集成场景(参见 `tests/README.md`),并且此测试套件尚未在此环境中的实时 Rust/Soroban 工具链上运行——在依赖它之前请使用 `cargo test --workspace` 进行验证。
## MVP 范围
最初的测试网 MVP 专注于单一的端到端流程:
1. 买家在特定条件下为卖家创建并提供托管资金
2. 卖家在 release 时收到资金,或者任意一方提出争议交由仲裁员解决
保险资金池和注册表锚点已搭建脚手架并进行了单元测试,但尚未接入真实的后端 oracle 或真实的 multisig 员会。
## 路线图
- [x] 条件托管(`create` / `release` / `dispute` / `resolve`),带有完整的状态机和单元测试
- [x] 保险资金池,带有偿付能力上限的承保、理赔批准/拒绝和赔付
- [x] 用于已确认欺诈标志的注册表锚点
- [x] 三个合约上的时间锁定管理员交接和持久化存储 TTL 管理
- [x] CI:每次 push/PR 时构建 + 测试
- [x] 所有转移资金的调用(`escrow::release`/`resolve`、`insurance-pool::payout`)上的检查-效果-交互顺序
- [ ] 将事件发射从 `env.events().publish(...)` 迁移到 `#[contractevent]` 宏
- [ ] 用真实的 multisig 账户替换单个地址的 `arbiter`/`oracle`/委员会成员
- [ ] 跨合约集成测试(参见 `tests/README.md`)
- [ ] 根据目标网络的租金经济学验证 TTL 阈值/增加常量
- [ ] 外部审计
- [ ] 主网启动
## 依赖项
- `soroban-sdk = "26.1.0"` — Soroban 智能合约 SDK(唯一的外部依赖;参见 `Cargo.toml`)
## 错误码
### escrow
| 代码 | 错误 | 原因 |
|------|-------|-------|
| 1 | `AlreadyInitialized` | `initialize` 被调用了两次 |
| 2 | `EscrowNotFound` | 无效或未知的 `escrow_id` |
| 3 | `InvalidAmount` | `create` 中的 `amount <= 0` |
| 4 | `InvalidTimeout` | `timeout` 不在未来 |
| 5 | `NotParty` | `dispute` 调用者既不是买家也不是卖家 |
| 6 | `AlreadySettled` | 在非 `Active` 的托管上调用了 `release`/`dispute` |
| 7 | `NotDisputed` | 在非 `Disputed` 的托管上调用了 `resolve` |
| 8 | `InvalidSplit` | `Decision::Split` 基点 > 10000 |
### insurance-pool
| 代码 | 错误 | 原因 |
|------|-------|-------|
| 1 | `AlreadyInitialized` | `initialize` 被调用了两次 |
| 2 | `ProjectNotCovered` | 对没有 `Active` 承保的项目调用 `file_claim` |
| 3 | `ClaimNotFound` | 无效或未知的 `claim_id` |
| 4 | `InvalidAmount` | `deposit_premium`/`file_claim` 中的金额非正,或 `set_coverage` 金额为负 |
| 5 | `CoverageExceedsSolvency` | 新的有效承保将超过资金池余额的 `coverage_ratio_bps` |
| 6 | `NotCommitteeMember` | `approve_claim`/`reject_claim` 调用者不在委员会中 |
| 7 | `ClaimNotApproved` | 对非 `Approved` 的理赔调用了 `payout` |
| 8 | `ClaimAlreadySettled` | 对非 `Filed` 的理赔调用了 `approve_claim`/`reject_claim` |
| 9 | `InsufficientPoolBalance` | `payout` 金额超过资金池当前的 token 余额 |
### registry-anchor
| 代码 | 错误 | 原因 |
|------|-------|-------|
| 1 | `AlreadyInitialized` | `initialize` 被调用了两次 |
| 2 | `FlagNotFound` | 无效或未知的 `flag_id` |
## 事件
| 合约 | 事件 | 触发时机 |
|----------|-------|---------------|
| escrow | `created` | `create` 锁定资金 |
| escrow | `released` | `release` 向卖家结算 |
| escrow | `disputed` | `dispute` 冻结活动托管 |
| escrow | `resolved` | `resolve` 执行仲裁员的决定 |
| insurance-pool | `premium` | `deposit_premium` 收到资金 |
| insurance-pool | `coverage` | `set_coverage` 更改项目的状态/金额 |
| insurance-pool | `claim_filed` | `file_claim` 注册新理赔 |
| insurance-pool | `claim_rejected` | `reject_claim` 拒绝已提出的理赔 |
| insurance-pool | `claim_paid` | `payout` 支付已批准的理赔 |
| registry-anchor | `flagged` | `anchor_flag` 锚定已确认的欺诈标志 |
| all three | `admin_proposed` | `propose_admin` 启动 48 小时时间锁 |
| all three | `admin_changed` | `accept_admin` 完成交接 |
## 许可证
MIT
## 支持
- GitHub Issues:[创建 issue](https://github.com/Astraguard/Contracts/issues)
- Stellar Discord:https://discord.gg/stellar
- Stellar 开发者:https://developers.stellar.org
## 贡献
欢迎贡献。在提交 PR 之前:
- `cargo test --workspace` 通过
- `cargo fmt --all -- --check` 和 `cargo clippy --workspace --all-targets` 干净无警告
- 新函数包含在相关 `src/test.rs` 中的单元测试
- 对托管分割逻辑、承保偿付能力数学计算或注册表 oracle 门控的更改需经过明确审查——这些是转移资金或作出公开信任声明的路径
**如果您运行 `cargo update`:** `soroban-env-host`(`soroban-sdk` 的一个传递性开发/测试依赖项)声明了 `ed25519-dalek = ">=2.0.0"` 且没有上限。`soroban-sdk` 本身固定使用 `^2`。如果发布了新的 `ed25519-dalek` 主要版本,不受限制的 `cargo update` 可能会将两个不兼容的主要版本解析到依赖树中,并因 `CryptoRng`/`rand_core` trait 绑定错误而构建失败。如果发生这种情况,请使用 `cargo update -p ed25519-dalek --precise ` 重新固定,而不是去追逐更新的主要版本。
标签:Soroban智能合约, Stellar, 争议解决, 保险池, 区块链, 可视化界面, 条件托管, 通知系统, 防欺诈记录