Разработка системы управления согласием пациента на блокчейне
Consent management в healthcare — больше чем юридическая формальность. GDPR требует explicit, informed, freely given consent для обработки health data. HIPAA требует авторизацию для раскрытия PHI (Protected Health Information). Бумажные формы согласий теряются, изменяются задним числом, и не дают пациенту реального контроля. Блокчейн превращает consent в verifiable, auditable, и revocable on-chain record.
Модель согласия
Consent — это структурированный документ с конкретными параметрами: кто может видеть какие данные, для каких целей, на какой срок. Это не бинарное "да/нет", а гранулированное разрешение.
contract ConsentRegistry {
enum ConsentStatus { ACTIVE, REVOKED, EXPIRED }
enum DataCategory {
DIAGNOSIS,
LAB_RESULTS,
PRESCRIPTIONS,
IMAGING,
MENTAL_HEALTH,
GENETIC,
HIV_STATUS,
SUBSTANCE_ABUSE
}
enum Purpose {
TREATMENT, // непосредственное лечение
PAYMENT, // страховые и платёжные операции
HEALTHCARE_OPS, // операционная деятельность
RESEARCH, // медицинские исследования
QUALITY_IMPROVEMENT, // улучшение качества обслуживания
CARE_COORDINATION // координация лечения
}
struct Consent {
bytes32 patientId;
address grantee; // кому выдано согласие
DataCategory[] categories; // какие категории данных
Purpose[] purposes; // для каких целей
uint256 grantedAt;
uint256 expiresAt; // 0 = бессрочно (до отзыва)
ConsentStatus status;
bytes32 documentHash; // hash PDF документа согласия
string version; // версия privacy policy
}
// consentId => Consent
mapping(bytes32 => Consent) public consents;
// patientId => grantee => consentIds
mapping(bytes32 => mapping(address => bytes32[])) public patientConsents;
event ConsentGranted(
bytes32 indexed consentId,
bytes32 indexed patientId,
address indexed grantee,
uint256 expiresAt
);
event ConsentRevoked(bytes32 indexed consentId, bytes32 indexed patientId);
function grantConsent(
bytes32 patientId,
address grantee,
DataCategory[] calldata categories,
Purpose[] calldata purposes,
uint256 duration,
bytes32 documentHash,
string calldata version
) external onlyPatient(patientId) returns (bytes32 consentId) {
consentId = keccak256(abi.encodePacked(
patientId, grantee, block.timestamp, block.number
));
consents[consentId] = Consent({
patientId: patientId,
grantee: grantee,
categories: categories,
purposes: purposes,
grantedAt: block.timestamp,
expiresAt: duration == 0 ? 0 : block.timestamp + duration,
status: ConsentStatus.ACTIVE,
documentHash: documentHash,
version: version
});
patientConsents[patientId][grantee].push(consentId);
emit ConsentGranted(consentId, patientId, grantee, block.timestamp + duration);
}
function revokeConsent(bytes32 consentId) external {
Consent storage consent = consents[consentId];
require(
isPatient(consent.patientId, msg.sender),
"Not patient"
);
require(consent.status == ConsentStatus.ACTIVE, "Not active");
consent.status = ConsentStatus.REVOKED;
emit ConsentRevoked(consentId, consent.patientId);
}
function isConsentValid(
bytes32 consentId,
DataCategory category,
Purpose purpose
) external view returns (bool) {
Consent storage consent = consents[consentId];
if (consent.status != ConsentStatus.ACTIVE) return false;
if (consent.expiresAt != 0 && block.timestamp > consent.expiresAt) return false;
bool hasCategory = false;
for (uint i = 0; i < consent.categories.length; i++) {
if (consent.categories[i] == category) { hasCategory = true; break; }
}
bool hasPurpose = false;
for (uint i = 0; i < consent.purposes.length; i++) {
if (consent.purposes[i] == purpose) { hasPurpose = true; break; }
}
return hasCategory && hasPurpose;
}
}
Электронная подпись документов согласия
Согласие должно быть юридически значимым. Это требует не только on-chain записи, но и подписанного документа:
// Пациент подписывает structured consent data через EIP-712
const consentTypedData = {
domain: {
name: "HealthConsent",
version: "1",
chainId: 1,
verifyingContract: CONSENT_REGISTRY_ADDRESS,
},
types: {
Consent: [
{ name: "patientId", type: "bytes32" },
{ name: "grantee", type: "address" },
{ name: "categories", type: "uint8[]" },
{ name: "purposes", type: "uint8[]" },
{ name: "expiresAt", type: "uint256" },
{ name: "documentHash", type: "bytes32" },
{ name: "version", type: "string" },
],
},
message: consentData,
};
const signature = await walletClient.signTypedData(consentTypedData);
// Подпись хранится вместе с consent и верифицируется при необходимости
PDF документ согласия генерируется автоматически из structured данных, его SHA-256 hash хранится on-chain в documentHash. Пациент получает копию PDF, может верифицировать hash в блокчейне.
Emergency access
Критический edge case: пациент без сознания, согласие не выдано, но медицинская помощь необходима. Break-glass механизм:
contract EmergencyAccess {
struct EmergencyAccessEvent {
bytes32 patientId;
address requester;
string justification; // причина экстренного доступа
uint256 timestamp;
bool approved; // одобрен ли постфактум
}
// Время на оспаривание emergency access: 72 часа
uint256 constant CHALLENGE_PERIOD = 72 hours;
mapping(bytes32 => EmergencyAccessEvent[]) public emergencyLog;
// Emergency access доступен авторизованным медицинским провайдерам
// Логируется, уведомление пациенту после выхода из критического состояния
function requestEmergencyAccess(
bytes32 patientId,
string calldata justification
) external onlyEmergencyProvider {
emergencyLog[patientId].push(EmergencyAccessEvent({
patientId: patientId,
requester: msg.sender,
justification: justification,
timestamp: block.timestamp,
approved: false // требует постфактум одобрения
}));
emit EmergencyAccessRequested(patientId, msg.sender, justification);
}
}
Интеграция с национальными системами
В разных странах — разные требования к consent management:
EU (GDPR + eHealth): explicit consent, right to withdraw, purpose limitation. Consent должен быть granular (нельзя требовать согласие "на всё").
US (HIPAA): authorization form, minimum necessary standard, patient rights to amend. Consent для research отдельный от treatment.
Beларусь / Россия / Украина: национальные health data законы, требования к локализации данных.
Для мультинациональных систем: consent template system с jurisdiction-specific полями, автоматическая загрузка нужной формы по геолокации.
Уведомления и patient portal
После каждого доступа к данным — уведомление пациенту:
// После каждого доступа к записям
async function notifyPatientOfAccess(event: AccessEvent) {
const patient = await getPatient(event.patientId);
await sendNotification(patient.email, {
type: "data_access",
accessor: await getProviderName(event.accessor),
dataCategory: event.category,
purpose: event.purpose,
timestamp: event.timestamp,
txHash: event.transactionHash,
});
}
Patient portal показывает: список всех активных consent, историю доступов (audit trail), возможность отозвать consent в один клик.
Технический стек
| Компонент | Технология |
|---|---|
| Smart contracts | Solidity + OpenZeppelin |
| EIP-712 подписи | viem / ethers.js |
| PDF generation | PDFKit / WeasyPrint |
| Patient portal | React + wagmi |
| Notifications | Email (SendGrid) + Push (Web Push API) |
| Indexing | The Graph |
Разработка consent management системы: 2-3 месяца для полной реализации с patient portal, PDF generation и emergency access.







