Розробка системи верифікації лекарських препаратів на блокчейні
Фальсифіковані лікарства — це не абстрактна проблема. ВОЗ оцінює частку поддільних препаратів на ринках країн, що розвиваються, у 10-30%. В Євросоюзі Директива 2011/62/EU (Falsified Medicines Directive) зобов'язує виробників розміщувати унікальний серійний номер та код верифікації на кожній упаковці. Блокчейн дає конкретне преимущество над централізованими реєстрами: виробник, дистрибютор та аптека не довіряють один одному — їм потрібен загальний immutable ledger без єдиного адміністратора.
Реальні впровадження: MediLedger (найбільший фармацевтичний консорціум у США, Pfizer, Genentech, AmerisourceBergen), Track and Trace EU система, пілоти на Hyperledger Fabric в Індії та Китаї. Архітектурні уроки з цих систем дозволяють не вигадувати велосипед.
Вимоги до даних та нормативний контекст
Серіалізація даних на упаковці
Стандарт GS1 DataMatrix на фарм упаковці кодує чотири атрибути (DSCSA в США, FMD в EU):
- GTIN (Global Trade Item Number) — ідентифікатор продукту
- Lot number — номер партії
- Expiry date — термін придатності
- Serial number — унікальний для кожної упаковки
GS1 Application Identifiers:
(01)04150753537323 — GTIN
(17)270131 — Expiry (YYMMDD)
(10)LOT-A4581 — Lot number
(21)000123456789 — Serial number
DataMatrix scannable string:
]d201041507535373232702310100101-A45812100123456789
Серійний номер + GTIN = унікальний ключ для on-chain запису. Всі інші дані — атрибути цього ключа.
Приватність даних: zero-knowledge або приватні канали
Критична архітектурна проблема. Конкуруючі фармацевтичні компанії не хочуть розкривати обсяги продаж, логістичні партнери не розкривають маршрути. On-chain дані публічні за замовчуванням. Рішення:
Hyperledger Fabric приватні колекції даних. Консорціумний blockchain з приватними каналами: дані конкретної транзакції видимі тільки учасникам каналу, у публічному ledger зберігається тільки хеш. Це стандартне рішення для корпоративних консорціумів (MediLedger використовує цей підхід).
ZK proofs для верифікації. Аптека доводить, що препарат з легітимного ланцюга поставок без розкриття конкретного дистрибютора:
// Verifier контракт: перевіряє ZK proof легітимності
contract DrugVerifier {
IVerifier public zkVerifier; // Groth16 або PLONK verifier
// Merkle root всіх легітимних серійних номерів (оновлюється регулятором)
bytes32 public legitimacyRoot;
// Аптека: доповісти що S/N у легітимному дереві без розкриття S/N
function verifyDrug(
bytes calldata proof,
uint256[2] calldata publicInputs // [nullifier, merkle_root]
) external view returns (bool) {
// publicInputs[0] — nullifier (не повторити верифікацію тієї ж упаковки)
// publicInputs[1] — повинен збігатися з legitimacyRoot
require(publicInputs[1] == uint256(legitimacyRoot), "Wrong root");
return zkVerifier.verifyProof(proof, publicInputs);
}
}
Chain of Custody: трансфер власності
Кожне переміщення упаковки — подія transfer custody. Виробник → дистрибютор → аптека → пацієнт. Кожен крок повинен бути верифікований обома сторонами (передаючою та приймаючою).
contract DrugTraceability {
enum Status { MANUFACTURED, IN_TRANSIT, RECEIVED, DISPENSED, RECALLED }
struct DrugUnit {
bytes32 serialHash; // keccak256(GTIN + serial) — не зберігаємо в открито
address currentHolder;
Status status;
uint256 manufacturedAt;
uint256 expiryTimestamp;
bool recalled;
}
mapping(bytes32 => DrugUnit) public drugs; // serialHash => DrugUnit
mapping(bytes32 => address[]) public custodyHistory;
// Виробник реєструє упаковку
function manufacture(
bytes32 serialHash,
uint256 expiryTimestamp,
bytes32 lotMerkleRoot // Merkle root для верифікації lot атрибутів
) external onlyManufacturer {
require(drugs[serialHash].manufacturedAt == 0, "Already registered");
drugs[serialHash] = DrugUnit({
serialHash: serialHash,
currentHolder: msg.sender,
status: Status.MANUFACTURED,
manufacturedAt: block.timestamp,
expiryTimestamp: expiryTimestamp,
recalled: false
});
custodyHistory[serialHash].push(msg.sender);
emit Manufactured(serialHash, msg.sender, block.timestamp);
}
// Двусторонній трансфер: ініціює відправник, підтверджує одержувач
mapping(bytes32 => address) public pendingTransfer; // serialHash => запропонований одержувач
function initiateTransfer(bytes32 serialHash, address recipient) external {
require(drugs[serialHash].currentHolder == msg.sender, "Not holder");
pendingTransfer[serialHash] = recipient;
emit TransferInitiated(serialHash, msg.sender, recipient);
}
function confirmTransfer(bytes32 serialHash) external {
require(pendingTransfer[serialHash] == msg.sender, "Not recipient");
drugs[serialHash].currentHolder = msg.sender;
custodyHistory[serialHash].push(msg.sender);
delete pendingTransfer[serialHash];
emit TransferConfirmed(serialHash, msg.sender);
}
// Відзив препарату
function recall(bytes32 serialHash, string calldata reason) external onlyRegulator {
drugs[serialHash].recalled = true;
emit Recalled(serialHash, reason, block.timestamp);
}
}
Інтеграція з фізичним світом
Захищена від підробок упаковка + NFC/QR
Блокчейн корисний тільки якщо зв'язок між фізичною упаковкою та on-chain записом надійний. Три рівні захисту:
QR код з DataMatrix — мінімум, відповідає FMD. Вразливість: QR можна скопіювати та наклеїти на поддільний препарат. Допомагає від грубих підробок, але не від складних атак.
NFC з криптографічним challenge-response (наприклад, NXP NTAG 424 DNA). Чип містить приватний ключ у захищеній пам'яті (не видобувній). При скануванні генерує підписаний challenge — неможливо клонувати без фізичного доступу до чипа. Виробник при випуску реєструє публічний ключ чипа on-chain.
// Верифікація NFC чипа (reader side)
async function verifyNFCChip(
chipPublicKey: string,
challenge: Buffer,
signature: Buffer
): Promise<boolean> {
// Верифікуємо підпис чипа
const isValidSignature = await verifyECDSA(chipPublicKey, challenge, signature)
// Перевіряємо реєстрацію публічного ключа on-chain
const isRegistered = await contract.isChipRegistered(chipPublicKey)
// Перевіряємо що чип не у списку recalled
const isRecalled = await contract.isRecalled(chipPublicKey)
return isValidSignature && isRegistered && !isRecalled
}
Голографічні мітки + блокчейн — гібридний підхід для ринків без NFC інфраструктури.
Off-chain дані та IPFS
Повна документація партії (Certificate of Analysis, результати тестування) не помещається в блокчейн. Стандартний паттерн:
- On-chain:
bytes32 documentsIPFSHash— Merkle root або IPFS CID документів - Off-chain: IPFS зберігає документи
- Верифікатор: отримує документи через IPFS, перевіряє хеш on-chain
struct BatchRecord {
bytes32 lotNumber;
address manufacturer;
uint256 productionDate;
bytes32 coaIpfsHash; // Certificate of Analysis
bytes32 testResultsHash; // Результати QC тестування
uint32 quantity;
}
Вибір блокчейну для фармацевтики
| Параметр | Публічний (Ethereum/Polygon) | Permissioned (Hyperledger Fabric) |
|---|---|---|
| Доступність даних | Публічна, всі бачать | Приватні канали |
| Учасники | Будь-які гаманці | KYC'd члени консорціуму |
| Вартість транзакцій | Gas (оптимізувати) | Практично безплатно |
| Compliance | Складніше (публічність) | Легше |
| Децентралізація | Висока | Консорціум |
| Швидкість | L2: ~2 sec | ~1 sec |
Рекомендація: для B2B консорціуму виробників/дистрибюторів — Hyperledger Fabric (модель MediLedger). Для consumer-facing верифікації (пацієнт перевіряє препарат) — публічний blockchain (Polygon або Arbitrum для вартості газу) з privacy-preserving ZK перевіркою.
Hyperledger Fabric: ключові концепції
// Chaincode (смарт-контракт) на Go
package main
import (
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type DrugContract struct {
contractapi.Contract
}
func (c *DrugContract) RegisterDrug(ctx contractapi.TransactionContextInterface,
serialHash, gtin, lot, expiry string) error {
// Перевіряємо що викликає авторизований виробник
mspID, _ := ctx.GetClientIdentity().GetMSPID()
if !isAuthorizedManufacturer(mspID) {
return fmt.Errorf("unauthorized: %s", mspID)
}
// Перевіряємо дублікат
existing, _ := ctx.GetStub().GetState(serialHash)
if existing != nil {
return fmt.Errorf("drug %s already registered", serialHash)
}
drug := Drug{
SerialHash: serialHash,
GTIN: gtin,
Lot: lot,
Expiry: expiry,
Holder: mspID,
Status: "MANUFACTURED",
Timestamp: time.Now().Unix(),
}
drugJSON, _ := json.Marshal(drug)
return ctx.GetStub().PutState(serialHash, drugJSON)
}
Нормативні інтерфейси
Регулятор (FDA, EMA, локальний орган) повинен мати доступ на читання та право відзиву. В Hyperledger Fabric: окремий канал між виробниками та регулятором. У публічному blockchain: on-chain роль через AccessControl.
// OpenZeppelin AccessControl для ролей
bytes32 public constant MANUFACTURER_ROLE = keccak256("MANUFACTURER_ROLE");
bytes32 public constant DISTRIBUTOR_ROLE = keccak256("DISTRIBUTOR_ROLE");
bytes32 public constant REGULATOR_ROLE = keccak256("REGULATOR_ROLE");
// Тільки регулятор може відозвати
function recall(bytes32 serialHash, string calldata reason)
external onlyRole(REGULATOR_ROLE) { ... }
Процес розробки
Нормативний аналіз (1-2 тижні). Визначаємо застосовні стандарти: DSCSA (США), FMD (EU), локальні вимоги. Фармацевтичні системи підпадають під вимоги до валідованого софту (FDA 21 CFR Part 11) — потрібна документація та матриця відстеження вимог.
Архітектурний дизайн (1-2 тижні). Вибір blockchain (permissioned vs public), модель приватності, схема даних GS1, NFC/QR стратегія. Проектуємо схему учасників консорціуму та governance.
Розробка смарт-контрактів (4-6 тижнів). Контракти реєстрації, transfer custody, recall. Розширене тестування: edge cases (упаковка після recall, expired drug transfer, дублікат серійних номерів).
Backend інтеграція (4-8 тижнів). API для інтеграції з ERP системами виробників (SAP, Oracle Pharma), WMS дистрибюторів. Batch import для реєстрації тисяч серійних номерів одночасно.
Hardware інтеграція (2-4 тижні). SDK для сканерів DataMatrix, NFC readers, інтеграція з існуючим складським обладнанням. Offline mode для слабозв'язаних мереж (аптеки у віддалених регіонах).
Аудит та валідація (4-6 тижнів). Аудит смарт-контрактів (обов'язковий — дані про лікарства впливають на здоров'я людей). Документація валідації для regulatory compliance (IQ/OQ/PQ по GAMP5).
Орієнтири за термінами
MVP для пілота з одним виробником та однією аптечною мережею — 3-4 місяці. Production система для консорціуму з повним regulatory compliance — 9-15 місяців. Складність проекту визначається не blockchain частиною, а інтеграцією з legacy ERP системами та regulatory валідацією.







