-
Notifications
You must be signed in to change notification settings - Fork 29
Writing chaincode
An example implementation of using this tool to write chaincode is located at wearetheledger/fabric-network-boilerplate.
Chaincode View definition
The Chaincode class is a base class containing handlers for the Invoke()
and Init()
function which are required by fabric-shim
. The Init()
function can be overwritten by just implementing it in your MyChaincode class, this function should be called init
and will also be injected with the same arguments as your custom functions.
export class MyChaincode extends Chaincode {
async init(stubHelper: StubHelper, args: string[]) {
return 'this will override the init method in Chaincode';
}
}
The Chaincode base class also implements the Invoke()
method, it will search in your class for existing chaincode methods with the function name you sent. It will also automatically wrap and serialize the reponse with shim.success()
and shim.error()
. You can just return the javascript object and it will do the rest, BUT returning a Buffer is still supported aswell. So for example, if we invoke our chaincode with function queryCar
, the function below will be executed.
export class MyChaincode extends Chaincode {
async queryCar(stubHelper: StubHelper, args: string[]): Promise<any> {
const verifiedArgs = await Helpers.checkArgs<{ key: string }>(args[0], Yup.object()
.shape({
key: Yup.string().required(),
}));
const car = await stubHelper.getStateAsObject(verifiedArgs.key); //get the car from chaincode state
if (!car) {
throw new ChaincodeError('Car does not exist');
}
return car;
}
}
StubHelper View definition
The StubHelper is a wrapper around the fabric-shim
Stub. Its a helper to automatically serialize and deserialize data being saved/retreived.
Get an object by key
This is the same as the getState function, but it will deserialize the Buffer to an object.
stubHelper.getStateAsObject(verifiedArgs.key);
Put an object by key
This is the same as the putState function, but it will serialize the object to a Buffer for you.
stubHelper.putState(verifiedArgs.key, data);
Get a date by key
This is the same as the getState function, but it will deserialize the Buffer to a Date.
stubHelper.getStateAsDate(verifiedArgs.key);
Get a string by key
This is the same as the getState function, but it will deserialize the Buffer to a String.
stubHelper.getStateAsString(verifiedArgs.key);
Get an Array from rich query
This is the same as the getQueryResult function, but it will deserialize and convert the iterator to an Array for you. Passing the keyValue is optional and will return an array with key values.
stubHelper.getQueryResultAsList(queryString, keyValue);
Get an Array from range query
This is the same as the getStateByRange function, but it will deserialize and convert the iterator to an Array for you. Passing the keyValue is optional and will return an array with key values.
stubHelper.getStateByRangeAsList(startKey, endKey);
Get an Array from history by key
This is the same as the getHistoryByKey function, but it will deserialize and convert the iterator to an Array for you.
stubHelper.getHistoryForKeyAsList(key);
Get the original stub
This will expose the stub which is returned by fabric-shim.
stubHelper.getStub();
Get ClientIdentity
This will expose the ClientIdentity which is returned by fabric-shim.
stubHelper.getClientIdentity();
Get ChaincodeCrypto
This will expose the ShimCrypto which is returned by fabric-shim-crypto required for encryption, decryption, signing and verification.
stubHelper.getChaincodeCrypto();
Generating deterministic uuids
It is important for a blockchain applications to be deterministic. This means you can't use randoms and current dates. This is why we added a way to generate deterministic uuids based on a given key.
stubHelper.generateUUID("CAR")
Query by key
Returns an item matching the key
async queryCar(stubHelper: StubHelper, args: string[]): Promise<any> {
const verifiedArgs = await Helpers.checkArgs<{ key: string }>(args[0], Yup.object()
.shape({
key: Yup.string().required(),
}));
const car = await stubHelper.getStateAsObject(verifiedArgs.key); //get the car from chaincode state
if (!car) {
throw new ChaincodeError('Car does not exist');
}
return car;
}
Perform a rich query
Returns an array of items matching the rich query. Notice that we add the property 'docType' in the create method.
async queryAllCars(stubHelper: StubHelper, args: string[]): Promise<any> {
return stubHelper.getQueryResultAsList(
{selector:{ docType: 'car'}}
);
}
Query by range
async queryAllCars(stubHelper: StubHelper, args: string[]): Promise<any> {
const startKey = 'CAR0';
const endKey = 'CAR999';
return stubHelper.getStateByRangeAsList(startKey, endKey);
}
Creating
async createCar(stubHelper: StubHelper, args: string[]) {
const verifiedArgs = await Helpers.checkArgs<any>(args[0], Yup.object()
.shape({
key: Yup.string().required(),
make: Yup.string().required(),
model: Yup.string().required(),
color: Yup.string().required(),
owner: Yup.string().required(),
}));
let car = {
docType: 'car',
make: verifiedArgs.make,
model: verifiedArgs.model,
color: verifiedArgs.color,
owner: verifiedArgs.owner
};
await stubHelper.putState(verifiedArgs.key, car);
}
Updating object
async changeCarOwner(stubHelper: StubHelper, args: string[]) {
const verifiedArgs = await Helpers.checkArgs<{ key: string; owner: string }>(args[0], Yup.object()
.shape({
key: Yup.string().required(),
owner: Yup.string().required(),
}));
let car = await <any>stubHelper.getStateAsObject(verifiedArgs.key);
car.owner = verifiedArgs.owner;
await stubHelper.putState(verifiedArgs.key, car);
}
Using private data
Collections can be passed as options in the following functions as the last argument: getQueryResultAsList
, getStateByRangeAsList
, deleteAllByQuery
, putState
, getStateAsObject
, getStateAsString
, getStateAsDate
,
async createPrivateCar(stubHelper: StubHelper, args: string[]) {
const verifiedArgs = await Helpers.checkArgs<any>(args[0], Yup.object()
.shape({
key: Yup.string().required(),
make: Yup.string().required(),
model: Yup.string().required(),
color: Yup.string().required(),
owner: Yup.string().required(),
}));
let car = {
docType: 'car',
make: verifiedArgs.make,
model: verifiedArgs.model,
color: verifiedArgs.color,
owner: verifiedArgs.owner
};
await stubHelper.putState(verifiedArgs.key, car, {privateCollection: "testCollection"});
}
async getPrivateCar(stubHelper: StubHelper, args: string[]) {
const verifiedArgs = await Helpers.checkArgs<any>(args[0], Yup.object()
.shape({
key: Yup.string().required(),
}));
const car = await stubHelper.getStateAsObject(verifiedArgs.key, {privateCollection: "testCollection"});
if (!car) {
throw new ChaincodeError('Car does not exist');
}
return car;
}
Transform View definition
The Transform class is a helper to provide data transformation to and from the formats required by hyperledger fabric.