Building and starting a chaincode
The «Quick start» shows the skeleton — a contract and a client in a few lines. Here we look at how this plays out in a production chaincode: on NestJS, with modules, start-up modes and the metadata through which the contract describes itself.
The entry point and DI outside HTTP
A chaincode is not a web server, but the NestJS DI container is still convenient for us — modules,
providers, dependency injection. That is why Nest is brought up in standalone mode, without HTTP, via
createApplicationContext. There is one mandatory and easily missed step: right after creating the
context you need to hand its ModuleRef to APPLICATION_INJECTOR — otherwise the part of the
@ts-core infrastructure that resolves dependencies on demand will not find the container.
async function bootstrap() {
let settings = new AppSettings();
let logger = (settings.logger = new DefaultLogger(settings.loggerLevel));
let application = await NestFactory.createApplicationContext(AppModule.forRoot(settings), { logger });
APPLICATION_INJECTOR(application.get(ModuleRef));
let chaincode = application.get(Chaincode);
if (settings.chaincodeMode === ChaincodeMode.INTERNAL) {
Shim.start(chaincode);
return;
}
let server = Shim.server(chaincode, settings.chaincodeServerOptions);
server.start();
}
bootstrap();
The module and registration
The application module ties everything together: it wires up transport via TransportFabricChaincodeModule.forRoot(settings)
and the domain modules, and it declares command handlers in controllers — as controllers, specifically,
rather than providers, because a handler subscribes to the transport in its constructor and must be created
right away (more on this in «Command handlers»).
public static forRoot(settings: AppSettings): DynamicModule {
return {
module: AppModule,
imports: [
LoggerModule.forRoot(settings),
TransportFabricChaincodeModule.forRoot(settings),
UserModule, CoinModule, SeedModule, // domain modules
],
providers: [{ provide: AppSettings, useValue: settings }, Chaincode],
controllers: [MetadataGetHandler, NonceGetHandler],
};
}
The contract itself is Chaincode, a descendant of TransportFabricSeededChaincode, which on Init
idempotently initializes the ledger through the seeder (see «Seed and initialization»).
Two start-up modes
The same contract code is started in two ways, and the choice is determined by the chaincodeMode setting.
In INTERNAL mode the contract is brought up with the familiar Shim.start — as a process managed by the
peer itself. Otherwise it is chaincode-as-a-service: the contract runs as a separate service and listens for
the peer at the address set in chaincodeServerOptions (its ccid and address usually come from
the environment):
public get chaincodeMode(): ChaincodeMode {
return this.getValue('CHAINCODE_MODE', ChaincodeMode.INTERNAL);
}
public get chaincodeServerOptions(): ChaincodeServerOpts {
return { ccid: this.getValue('CORE_CHAINCODE_ID'), address: this.getValue('CORE_CHAINCODE_ADDRESS'), tlsProps: null };
}
The second mode is more convenient during development and with frequent logic updates: the contract can be restarted without touching the peer. But you need to know about this fork in advance, because both the image build and the way the contract registers in the network depend on it.
Contract metadata
It is useful for a client to learn about a contract without looking into its sources — whether batch is
enabled, which mode it is in. For this there is the readonly command ChaincodeMetadataGetCommand, and its
handler returns IChaincodeMetadata:
@Injectable()
export class MetadataGetHandler extends TransportCommandAsyncHandler<void, IChaincodeMetadata, ChaincodeMetadataGetCommand> {
constructor(logger: Logger, transport: Transport, private settings: AppSettings, private chaincode: Chaincode) {
super(logger, transport, ChaincodeMetadataGetCommand.NAME);
}
protected async execute(): Promise<IChaincodeMetadata> {
return { name: this.chaincode.name, mode: this.settings.chaincodeMode, batch: this.settings.batch };
}
}
Based on this data the client chooses the transport — ordinary or batched — which is discussed in the «Batch mode» section.