Разработка системы прослеживания поставок на блокчейне
Большинство существующих систем трекинга поставок — это просто база данных с веб-интерфейсом. Их проблема не в технологии, а в архитектуре доверия: данные записывает одна сторона, другие вынуждены ей верить. Когда в цепочке поставок участвуют пять сторон из трёх разных стран — это неработающая модель. Именно здесь блокчейн решает реальную проблему: immutability записей и verifiability без центрального арбитра.
Но прежде чем проектировать, честный вопрос: нужен ли публичный блокчейн или достаточно 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, etc.
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, industrial 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
Для высоких требований к доверию — Hardware Security Modules (HSM) или Trusted Execution Environment (TEE) прямо в устройстве. Microchip ATECC608 — недорогой чип с ECC ключевой парой, которую нельзя извлечь, хранится в secure element. Устройство подписывает данные ключом, который физически защищён от компрометации.
Реализация трекинга партий: реальный сценарий
Рассмотрим фармацевтическую 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) |
|---|---|---|---|
| Публичная верифицируемость | Да | Нет | Нет |
| Стоимость записи | ~$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 integration, 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-системами участников цепочки, не разработка блокчейн-части.







