nada171/SurgiConsent
GitHub: nada171/SurgiConsent
一个基于 Solana 和 Anchor 构建的智能合约,为手术知情同意流程提供防篡改的链上审计追踪。
Stars: 0 | Forks: 0
# SurgiConsent — 链上审计程序
这是一个 Solana 智能合约,为手术知情同意工作流提供防篡改、不可变的审计追踪。基于 [Anchor](https://www.anchor-lang.com/) 0.31.1 构建。
## 概述
SurgiConsent 将医院、医生和患者之间的手术知情同意流程数字化并进行安全保护 —— 将每一个步骤锚定到 Solana 区块链上,使得任何一方事后都无法对已达成协议的内容进行 disputes 或更改。
**实际运行机制:**
1. **医院注册自身信息**,并建立一个授权医生名单,允许这些医生代表医院创建知情同意书。
2. **医院或白名单医生创建知情同意记录**,针对特定的患者和手术程序 —— 这包括实际知情同意文件(存储在链下,例如医院数据库中)的哈希值,该文件描述了手术程序及其风险。
3. **患者(或其法定代理人,如果是未成年人)在链下审阅文件并在链上签名** —— 此操作会附带时间戳被永久记录,证明患者在手术前已知晓并同意相关风险。
4. **医生批准知情同意**,在患者签名后的 24 小时窗口期内完成,最终使其具有法律约束力。
5. 如果文件中的任何内容发生更改,或者如果在紧急情况下需要撤回或覆盖知情同意,**每一次更改都会记录在链上** —— 创建一个关于谁在何时同意了什么内容的永久、防篡改审计追踪。
由于文件的哈希值在患者签名的那一刻就被锁定,**任何事后篡改知情同意书的企图都会被立即发现** —— 存储的哈希值将与修改后的文件不匹配。这既保护了患者(他们的知情同意不会被悄然更改),也保护了医院(他们拥有患者已知晓并同意的密码学证明)。
该程序强制执行:
- **访问控制** —— 只有已注册的医院或其白名单医生才能创建知情同意书;只有医院才能更新或覆盖它们。
- **状态机完整性** —— 知情同意状态转换在链上经过验证,无法被绕过。
- **紧急访问** —— 专门的 override 指令允许医院在紧急情况下采取行动,同时仍会留下可验证的链上记录。
## 程序 ID
```
6GQdMpD72WwzxsXm6q52ED3UDYFU4i7tH8pLVoHgSBcY
```
## 架构
```
ConsentConfig (singleton PDA)
└── hospital: Pubkey
└── whitelisted_doctors: Vec ← max 5
PatientAccount (one per patient, hospital-created)
└── name, age, gender, patient_hash
└── surrogate: Option ← required if age < 18
└── hospital, bump
ConsentAccount (one per consent document)
└── consent_id, procedure_template_id
└── document_hash, status
└── timestamp, version, hospital
```
`ConsentConfig` 是一个由任意一方初始化一次的单例 PDA。它定义了医院权限以及授权与该协议进行交互的医生集合。所有的 `ConsentAccount` 和 `PatientAccount` PDA 在创建时都会从该配置中继承 `hospital` 字段,确保权限始终可以追溯到已注册的医院。
## 账户
### `ConsentConfig`
**PDA seeds:** `["consent_config"]`
协议级别的配置。仅初始化一次;`init` 约束可防止重复初始化。
| 字段 | 类型 | 描述 |
|---|---|---|
| `hospital` | `Pubkey` | 医院 wallet —— 所有知情同意书的最终权限方 |
| `whitelisted_doctors` | `Vec` | 授权创建知情同意书的医生(最多 5 名) |
| `bump` | `u8` | PDA bump seed |
### `PatientAccount`
**PDA seeds:** `["patient", patient_wallet]`
每位患者对应一个账户。包含 PHI —— 必须在链下限制对其的访问。
| 字段 | 类型 | 描述 |
|---|---|---|
| `patient` | `Pubkey` | 患者 wallet —— 显式存储以便账户具有自描述性 |
| `name` | `String` | 患者全名 |
| `age` | `u8` | 患者年龄 |
| `gender` | `Gender` | `Male`、`Female` 或 `Other` |
| `patient_hash` | `[u8; 32]` | 用于患者身份标识的 SHA-256,用于链下关联 |
| `surrogate` | `Option` | 代理人 wallet;当年龄 < 18 岁时必填 |
| `hospital` | `Pubkey` | 登记该患者的医院 |
| `bump` | `u8` | PDA bump seed |
### `ConsentAccount`
**PDA seeds:** `["consent", patient_wallet, consent_id.to_le_bytes()]`
每份知情同意文件对应一个账户。`consent_id` 是一个由链下系统分配的 `u64`,被编码为 8 个小端字节 —— 无需进行哈希处理。
| 字段 | 类型 | 描述 |
|---|---|---|
| `consent_id` | `u64` | 知情同意文件的数字标识符 |
| `procedure_template_id` | `u64` | 数字手术程序模板标识符 |
| `document_hash` | `[u8; 32]` | 知情同意文件的 SHA-256 |
| `status` | `ConsentStatus` | 当前生命周期状态 |
| `patient_signed` | `bool` | 当患者或代理人签名后为 True;同时开启 24 小时窗口 |
| `signed_at` | `i64` | 患者签名时的 Unix 时间戳 |
| `timestamp` | `i64` | 最后一次更新时的 Unix 时间戳 |
| `version` | `u64` | 单调递增的更新计数器 |
| `patient` | `Pubkey` | 患者 wallet —— 显式存储以便账户具有自描述性 |
| `hospital` | `Pubkey` | 创建时的医院地址 |
| `bump` | `u8` | PDA bump seed |
## 指令
### `initialize_config(hospital: Pubkey)`
设置 `ConsentConfig` 单例。可由任何账户调用一次(`init` 约束防止重复执行)。建立医院地址和一个空的医生白名单。
### `manage_doctor(doctor: Pubkey, add: bool)`
在白名单中添加或删除医生。
- **权限:** 仅限医院
- **限制:** 任何时候最多只能有 5 名白名单医生
- 传入 `add: true` 添加至白名单,传入 `add: false` 将其移除
### `create_patient(name, age, gender, patient_hash, surrogate)`
登记患者并创建 `PatientAccount`。
- **权限:** 仅限医院
- 如果 `age < 18`,则必须提供 `surrogate` —— 这是一个成年人的 wallet,将代表该患者签署知情同意书
### `create_consent(consent_id: u64, procedure_template_id: u64, document_hash)`
创建一个新的 `ConsentAccount`,初始状态为 `Pending`。要求患者必须已拥有注册过的 `PatientAccount`。`hospital` 字段从 `ConsentConfig` 中获取并填充,而不是来自调用者。
- **权限:** 医院或任何白名单医生
### `sign_consent()`
患者或其代理人签署知情同意书。状态保持为 `Pending` —— 这会启动 **24 小时批准窗口**。
- **权限:** 患者本人的 wallet(年龄 ≥ 18 岁)或已登记的代理人(年龄 < 18 岁)
- 触发 `PatientSigned` 事件并附带 `signed_by` 以供审计
### `approve_consent()`
白名单医生在 24 小时窗口期内批准该知情同意书。立即将状态转换为 `Signed`。
- **权限:** 仅限白名单医生
- **限制:** 要求患者必须已先签署;如果窗口已关闭则被阻止(改为调用 `expire_consent`)
- 触发 `ConsentSigned` 事件并附带 `approved_by` 以供审计
### `expire_consent()`
正式将知情同意书标记为 `Expired`。无需权限 —— 一旦超过 24 小时窗口期且双方未均批准,任何人都可以调用此方法。Solana 没有计时器;过期必须被显式触发。
- **限制:** 要求患者必须已签署;要求时间窗口已过
### `update_document_hash(document_hash)`
更新知情同意文件的 SHA-256 哈希值,以反映条款的变更。
- **权限:** 仅限白名单医生
- **限制:** 一旦患者签署即被阻止 —— 从那一刻起文件即不可变
### `withdraw_consent()`
撤回知情同意书。仅限医院操作。
- **有效转换:** `Pending → Withdrawn`,`Signed → Withdrawn`
### `emergency_override(reason_hash, document_hash)`
强制将知情同意书从任何状态转换为 `Overridden` 状态。在链上记录 `reason_hash` 以保证可审计性。
- **权限:** 仅限医院
## 知情同意生命周期
```
┌─────────┐
│ Pending │ ← created by hospital / doctor
└────┬────┘
│ sign_consent() (patient or surrogate)
│ document hash locked from this point
▼
┌─────────┐ 24-hour window opens
│ Pending │ waiting for doctor approval
└────┬────┘
│
┌───────┴───────────────┐
│ approve_consent() │ window closes (24h) without doctor approval
│ (whitelisted doctor) │
▼ ▼
┌────────┐ ┌─────────┐
│ Signed │ │ Expired │ (expire_consent, permissionless)
└───┬────┘ └─────────┘
│ withdraw_consent() (hospital only)
▼
┌───────────┐
│ Withdrawn │◄── Pending (hospital can cancel before signing too)
└───────────┘
Any state ──► Overridden (emergency_override, hospital only)
```
## 事件
所有状态更改都会触发链上事件,提供可审计的日志,而无需为历史数据消耗账户存储空间。
| 事件 | 触发方 | 关键字段 |
|---|---|---|
| `PatientCreated` | `create_patient` | patient_hash, age, hospital |
| `ConsentCreated` | `create_consent` | consent_id, procedure_template_id, document_hash, status |
| `PatientSigned` | `sign_consent` | consent_id, signed_by, timestamp |
| `ConsentSigned` | `approve_consent` | consent_id, approved_by, timestamp, version |
| `ConsentExpired` | `expire_consent` | consent_id, timestamp, version |
| `DocumentHashUpdated` | `update_document_hash` | consent_id, document_hash, status, version |
| `StatusUpdated` | `withdraw_consent` | consent_id, document_hash, status, version |
| `EmergencyOverrideEvent` | `emergency_override` | consent_id, reason_hash, document_hash, version |
## 访问控制
| 指令 | 允许的调用者 | 备注 |
|---|---|---|
| `initialize_config` | 任何人(仅一次) | |
| `manage_doctor` | 医院 | |
| `create_patient` | 医院 | |
| `create_consent` | 医院或白名单医生 | |
| `sign_consent` | 患者(年龄 ≥ 18 岁)或代理人(年龄 < 18 岁) | 开启 24 小时窗口 |
| `approve_consent` | 白名单医生 | 必须在患者签署后的 24 小时内批准 |
| `expire_consent` | 任何人 | 只能在 24 小时窗口期后调用 |
| `update_document_hash` | 白名单医生 | 在患者签署后被阻止 |
| `withdraw_consent` | 医院 | |
| `emergency_override` | 医院 | |
## 错误码
| 代码 | 描述 |
|---|---|
| `InvalidTransition` | 尝试的状态转换不被状态机允许 |
| `Unauthorized` | 签名者与所需的权限方不匹配 |
| `NotHospitalOrDoctor` | 调用者既不是医院也不是白名单医生 |
| `NotWhitelistedDoctor` | 调用者不是白名单医生 |
| `DoctorAlreadyWhitelisted` | 提供的医生已存在于白名单中 |
| `DoctorNotFound` | 提供的医生不在白名单中 |
| `DoctorLimitReached` | 白名单已满(最多 5 名医生) |
| `DocumentLocked` | 在患者签署后,文档哈希无法被修改 |
| `SurrogateRequired` | 患者为未成年人但未提供代理人地址 |
| `ConsentWindowExpired` | 24 小时的批准窗口已关闭;请调用 `expire_consent` |
| `ApprovalWindowStillOpen` | 在 24 小时窗口期结束之前调用了 `expire_consent` |
## 本地开发
### 前置条件
- [Rust](https://rustup.rs/) (稳定版)
- [Solana CLI](https://docs.solana.com/cli/install-solana-cli-tools)
- [Anchor CLI](https://www.anchor-lang.com/docs/installation) 0.31.1
- [Node.js](https://nodejs.org/) + [pnpm](https://pnpm.io/)
### 设置
```
# 安装 JS 依赖
pnpm install
# 构建程序
anchor build
# 针对本地 validator 运行测试
anchor test
```
## 安全说明
- **链上无 PHI。** 程序仅接受哈希值。调用者需在提交交易之前对患者身份标识和文件内容进行哈希处理。
- **权限不可变。** `ConsentAccount` 上的 `hospital` 字段在创建时从配置中设置,且从不更新 —— 即使配置后来发生更改,现有的知情同意书仍保留其原始权限。
- **未成年患者保护。** 18 岁以下的患者不能自行签署知情同意书。必须在创建患者档案时登记代理人地址,且该地址是唯一被允许代其签署的 wallet。
- **签署后的文件完整性。** 一旦知情同意书达到 `Signed` 状态,`document_hash` 字段即被冻结。医生和医院均无法更改 —— 只有 `emergency_override`(会转换为 `Overridden` 状态)可以越过该限制继续操作。
- **紧急 override 可追溯。** `reason_hash` 参数确保 override 携带了链下的正当理由说明,并可针对链上记录进行验证。
标签:Anchor, MITM代理, Solana, 区块链, 医疗信息化, 可视化界面, 审计追踪, 智能合约, 通知系统