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.