Перейти к основному содержимому

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 и работают с состоянием — в разделе «Обработчики команд».