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, 区块链, 医疗信息化, 可视化界面, 审计追踪, 智能合约, 通知系统