Skip to main content

Seed and ledger initialization

A freshly installed contract has empty state, yet the very first command already requires that someone be in the ledger — at least the system user, on whose behalf everyone else is created. hlf-core takes this initial initialization — genesis — upon itself through the pair Seeder and TransportFabricSeededChaincode.

An idempotent genesis on Init​

The contract inherits TransportFabricSeededChaincode and receives the seeder in its constructor. On Init the seeder first checks whether the initial data already exists, and creates it only if it does not. This is important: Init in Fabric may be called more than once (for example, when upgrading the contract), and the initialization is obliged to be idempotent, otherwise a second run would overwrite or duplicate the ledger.

@Injectable()
export class Chaincode extends TransportFabricSeededChaincode<SeedService> {
constructor(logger: Logger, transport: TransportFabricChaincodeReceiver, seeder: SeedService) {
super(logger, transport, seeder);
}

public get name(): string {
return 'MyChaincode';
}
}

The check and creation are performed by TransportFabricSeededChaincode itself — all we need to do is implement the seeder.

The seeder​

The seeder inherits Seeder and implements add — what should end up in the ledger on first start. Usually this is the system user with all roles and the ledger's initial settings:

@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 });
}
}

The system user needs a deterministic identifier that is the same across all peers — this is provided by UserUtil.seed: a fixed time and hash instead of those that normally come from the transaction (see «Identity»).

const seedUser = UserUtil.seed(User); // user_00000000000000_0000...0 — a deterministic uid

Accessing seeds at runtime​

The data created during initialization remains available later too — through the same seeder. This is convenient for values that are set once at genesis and are only read afterwards — such as, for example, the ledger's initial settings in the example above: a handler does not need to store them separately, it takes them from the seed. How handlers obtain the holder and work with state is covered in the «Command handlers» section.