Розробка системи прослідковування поставок на блокчейні
Більшість існуючих систем трекінгу поставок — це просто база даних з веб-інтерфейсом. Їхня проблема не у технології, а в архітектурі довіри: одна сторона записує дані, інші вимушені їй вірити. Коли в ланцюзі поставок учасні п'ять сторін з трьох різних країн — це неробоча модель. Блокчейн вирішує реальну проблему: невідмінність записів і верифіковність без центрального арбітра.
Але перш ніж проектувати, чесне питання: потрібен публічний блокчейн чи достатньо permissioned мережі? Для корпоративних supply chains з відомим колом учасників — Hyperledger Fabric або Besu у IBFT-режимі у більшості випадків більш правильно ніж публічний Ethereum. Для випадків де важлива публічна верифіковність (користувач сканує QR і видит історію продукту на блокчейні) — публічний L2 або Polygon.
Архітектура даних: що хранить on-chain, що off-chain
Головна помилка початківців — спробувати хранить все on-chain. Результат: дорого, повільно, надмірно. Правило:
On-chain зберігається: хеш документа/события, ідентифікатор актора (адреса), часова мітка, стан (enum), merkle root партійних даних.
Off-chain зберігається: фото, PDF-сертифікати, детальні sensor readings, великі JSON-обєкти. Посилання на хранилище (IPFS CID або URL) + хеш змісту записуються on-chain.
// Событие трекінгу: легке on-chain, деталі в IPFS
struct TrackingEvent {
bytes32 batchId; // ID партії/лота
bytes32 dataHash; // keccak256 від повного JSON события
string ipfsCid; // CID повних даних в IPFS
address actor; // хто записує (верифікований учасник)
EventType eventType; // PRODUCED, SHIPPED, RECEIVED, INSPECTED, SOLD
uint256 timestamp;
bytes32 locationHash; // хеш GPS координат (для приватності)
}
enum EventType { PRODUCED, SHIPPED, RECEIVED, INSPECTED, CERTIFIED, SOLD }
mapping(bytes32 => TrackingEvent[]) public batchHistory;
mapping(bytes32 => bool) public authorizedActors;
event BatchEvent(
bytes32 indexed batchId,
EventType indexed eventType,
address indexed actor,
bytes32 dataHash,
string ipfsCid
);
function recordEvent(
bytes32 batchId,
bytes32 dataHash,
string calldata ipfsCid,
EventType eventType
) external {
require(authorizedActors[keccak256(abi.encode(msg.sender, eventType))],
"Not authorized for this event type");
TrackingEvent memory evt = TrackingEvent({
batchId: batchId,
dataHash: dataHash,
ipfsCid: ipfsCid,
actor: msg.sender,
eventType: eventType,
timestamp: block.timestamp,
locationHash: bytes32(0)
});
batchHistory[batchId].push(evt);
emit BatchEvent(batchId, eventType, msg.sender, dataHash, ipfsCid);
}
Identity й авторизація учасників
У supply chain кілька типів акторів з різними правами: виробник, логіст, таможня, ритейлер, інспектор. Простий Ownable не підходить — потрібна ролева система з делегуванням.
Верифікація учасників через DID
Decentralized Identifiers (DID) — W3C стандарт для децентралізованої ідентичності. Кожен учасник має DID, привязаний до своїх смарт-контрактних адрес. Верифікація учасника (KYB — Know Your Business) відбувається off-chain через аккредитованих верифікаторів, які видають Verifiable Credentials (VC).
// Верифікація VC при реєстрації учасника
import { Resolver } from 'did-resolver'
import { getResolver as ethrResolver } from 'ethr-did-resolver'
import { verifyCredential } from 'did-jwt-vc'
async function verifyParticipantCredential(
vcJwt: string,
participantAddress: string
): Promise<boolean> {
const resolver = new Resolver({
...ethrResolver({ infuraProjectId: process.env.INFURA_ID })
})
const result = await verifyCredential(vcJwt, resolver)
// Перевіряємо, що VC видан аккредитованим верифікатором
const trustedIssuers = await getTrustedIssuers() // з смарт-контракту
if (!trustedIssuers.includes(result.issuer)) {
return false
}
// Перевіряємо, що VC стосується цієї адреси
return result.verifiableCredential.credentialSubject.ethereumAddress
.toLowerCase() === participantAddress.toLowerCase()
}
Role-based access з часовими вікнами
Учасник може мати право записувати況時只в певний період (час у дорозі товару):
struct ActorPermission {
bytes32 role; // PRODUCER_ROLE, SHIPPER_ROLE, тощо
uint256 validFrom;
uint256 validUntil;
bytes32[] allowedBatches; // порожній масив = всі партії
}
mapping(address => ActorPermission[]) public permissions;
function isAuthorized(
address actor,
bytes32 role,
bytes32 batchId
) public view returns (bool) {
ActorPermission[] storage perms = permissions[actor];
for (uint i = 0; i < perms.length; i++) {
if (perms[i].role == role &&
perms[i].validFrom <= block.timestamp &&
perms[i].validUntil >= block.timestamp) {
if (perms[i].allowedBatches.length == 0) return true;
for (uint j = 0; j < perms[i].allowedBatches.length; j++) {
if (perms[i].allowedBatches[j] == batchId) return true;
}
}
}
return false;
}
IoT інтеграція: зв'язок фізичного світу й блокчейну
Sensor data повинен потрапляти on-chain автоматично й невідмінно. Це архітектурна проблема: IoT-пристрій не може сам підписувати Ethereum транзакції (недостатньо RAM, battery для EVM-класу крипто).
Паттерн: Gateway + Oracle
[IoT Sensor] → [Edge Gateway] → [Oracle Service] → [Smart Contract]
↓
[IPFS / S3] ← повні sensor readings
Edge Gateway (Raspberry Pi, промисловий PC):
- Підписує дані від сенсорів своїм ключем
- Агрегує readings за період (наприклад, кожні 5 хвилин)
- Публікує агрегат в IPFS
- Відправляє хеш + CID до oracle service
Oracle Service (off-chain backend):
- Верифікує підпис gateway
- Перевіряє дані на аномалії (outlier detection)
- Ініціює транзакцію в смарт-контракт
# Oracle service: верифікація й запис sensor события
from web3 import Web3
from eth_account import Account
import ipfshttpclient
async def process_sensor_reading(gateway_id: str, payload: dict, signature: str):
# 1. Верифікуємо підпис gateway
message = encode_defunct(text=json.dumps(payload, sort_keys=True))
recovered = w3.eth.account.recover_message(message, signature=signature)
gateway_address = await get_registered_gateway(gateway_id)
if recovered.lower() != gateway_address.lower():
raise ValueError("Invalid gateway signature")
# 2. Публікуємо в IPFS
async with ipfshttpclient.connect() as ipfs:
cid = ipfs.add_json(payload)
# 3. Записуємо on-chain
data_hash = Web3.keccak(text=json.dumps(payload, sort_keys=True))
tx = tracking_contract.functions.recordSensorEvent(
payload['batch_id'].encode(),
data_hash,
cid,
EventType.SENSOR_READING
).build_transaction({
'from': oracle_account.address,
'nonce': w3.eth.get_transaction_count(oracle_account.address),
'maxFeePerGas': await get_gas_price(),
})
signed = oracle_account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
return tx_hash.hex()
Tamper-evident обладнання
Для високих вимог до довіри — Hardware Security Modules (HSM) або Trusted Execution Environment (TEE) прямо в пристрої. Microchip ATECC608 — недорогий чіп з ECC ключовою парою, яку неможливо витягти, зберігається в secure element. Пристрій підписує дані ключем, який фізично захищений від компрометації.
Реалізація реального сценарію: фармацевтична supply chain
Рассмотрим фармацевтичну supply chain (FDA DSCSA compliance вимагає електронний трекінг):
Событие 1: Виробництво
- Виробник записує: ID серії, дата виробництва, склад, хеш CoA (Certificate of Analysis)
- Генерується QR-код з batchId
Событие 2: Відгрузка
- Логіст сканує QR, записує: carrier ID, tracking number, температурний діапазон (для фарми критично)
- GPS-трекер починає писати temperature/humidity readings кожні 10 хвилин
Событие 3: Таможенна очистка
- Таможенний агент записує: декларація № , статус (cleared/held), інспектор ID
Событие 4: Отримання
- Одержувач записує: дата, фізичний огляд (OK/damaged), розхоження по кількості
- Верифікація: порівнюємо hash у блокчейні з hash від скачаного IPFS документа
Событие 5: Продаж споживачу
- Споживач сканує QR → видит повну історію партії
// Верифікація продукту споживачем (фронтенд)
async function verifyProduct(batchId: string): Promise<ProductHistory> {
const history = await trackingContract.getBatchHistory(batchId)
const verified = await Promise.all(history.map(async (event) => {
// Скачуємо дані з IPFS
const ipfsData = await fetchFromIPFS(event.ipfsCid)
// Верифікуємо хеш
const computedHash = ethers.keccak256(
ethers.toUtf8Bytes(JSON.stringify(ipfsData))
)
return {
...event,
ipfsData,
dataIntegrity: computedHash === event.dataHash,
actorName: await getActorName(event.actor), // з реєстру
}
}))
return verified
}
Вибір мережі
| Параметр | Публічний L2 (Polygon/Base) | Hyperledger Fabric | Besu (IBFT) |
|---|---|---|---|
| Публічна верифіковність | Так | Нi | Нi |
| Вартість запису | ~$0.001–$0.01/tx | Майже 0 | Майже 0 |
| Швидкість фіналізації | 2–5 сек | < 1 сек | 2–5 сек |
| Контроль доступу | Smart contracts | Native channel/MSP | Smart contracts |
| Регуляторні вимоги | Публічний блокчейн | Приватна мережа | Приватна мережа |
Для B2C сценаріїв (споживач видит історію) — публічний L2. Для B2B (тільки учасники ланцюга) — permissioned.
Етапи розробки
Фаза 1 — Design (2–3 нед): аналіз бізнес-процесів, визначення событій, учасників, прав доступу. Data model on/off-chain.
Фаза 2 — Smart contracts (3–4 нед): контракти трекінгу, ролева система, тесты.
Фаза 3 — Oracle + IoT (3–4 нед): gateway інтеграція, oracle service, IPFS pipeline.
Фаза 4 — API & Dashboard (3–4 нед): REST/GraphQL API, admin panel, consumer-facing верифікатор.
Фаза 5 — Integration & Pilot (2–4 нед): інтеграція з ERP/WMS системами учасників, пілот на реальних даних.
Итого: 13–19 тижнів. Найбільш трудомісткий етап — інтеграція з legacy ERP-системами учасників ланцюга, не розробка блокчейн-частини.







