Seed и инициализация реестра
У свежеустановленного контракта состояние пусто, а между тем первая же команда уже требует, чтобы в
реестре кто-то был — хотя бы системный пользователь, от чьего имени заводят всех остальных. Эту
начальную инициализацию — genesis — hlf-core берёт на себя через пару Seeder и
TransportFabricSeededChaincode.
Идемпотентный genesis на Init
Контракт наследует TransportFabricSeededChaincode и получает сидер в конструктор. На Init сидер
сперва проверяет, есть ли уже начальные данные, и только если их нет — создаёт их. Это важно: Init
в Fabric может вызываться не один раз (например, при обновлении контракта), и инициализация обязана
быть идемпотентной, иначе второй запуск затёр бы или задублировал реестр.
@Injectable()
export class Chaincode extends TransportFabricSeededChaincode<SeedService> {
constructor(logger: Logger, transport: TransportFabricChaincodeReceiver, seeder: SeedService) {
super(logger, transport, seeder);
}
public get name(): string {
return 'MyChaincode';
}
}
Проверку и создание выполняет уже сам TransportFabricSeededChaincode — нам достаточно реализовать
сидер.
Сидер
Сидер наследует Seeder и реализует add — то, что должно оказаться в реестре при первом запуске.
Обычно это системный пользователь со всеми ролями и начальные настройки реестра:
@Injectable()
export class SeedService extends Seeder<ISeed, IUserStubHolder> {
constructor(logger: Logger, transport: TransportFabricChaincodeReceiver, private user: UserService) {
super(logger, transport);
}
protected async add(holder: IUserStubHolder): Promise<ISeed> {
this.log('Adding seed user...');
await this.user.seed(holder);
let settings = this.getDefaultSettings();
return TransformUtil.toClass(Seed, { created: holder.stub.transaction.date, settings });
}
}
Системному пользователю нужен детерминированный, одинаковый у всех пиров идентификатор — его даёт
UserUtil.seed: фиксированные время и хэш вместо тех, что обычно приходят из транзакции (см.
«Идентификацию»).
const seedUser = UserUtil.seed(User); // user_00000000000000_0000...0 — детерминированный uid
Доступ к сидам в рантайме
Созданные при инициализации данные остаются доступны и позже — через тот же сидер. Это удобно для значений, которые заданы один раз при genesis и потом только читаются — как, например, начальные настройки реестра в примере выше: обработчику не нужно хранить их отдельно, он берёт их из сида. Как обработчики получают holder и работают с состоянием — в разделе «Обработчики команд».