Skip to content

Commit

Permalink
[FAB-14260] Update jsdoc
Browse files Browse the repository at this point in the history
Change-Id: I5fda5e95126f17633ad5ce0e0c4e22ee501b4fcf
Signed-off-by: Matthew B. White <whitemat@uk.ibm.com>
  • Loading branch information
mbwhite authored and heatherlp committed Sep 12, 2019
1 parent 6946d91 commit 571d1e5
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 46 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
## Hyperledger Fabric Shim for node.js chaincodes

This is the project for the fabric shim for node.js chaincodes development. The following instructions are oriented to a contributor or an early adopter and describes the steps to build and test the library.
This is the project to support the writing of Chaincode within the node.js runtime. The following instructions are orientated to a contributor or an early adopter and therefore describes the steps to build and test the library.

As an application developer, to learn about how to implement **"Smart Contracts"** for the Hyperledger Fabric using Node.js, Please visit the [documentation](https://fabric-shim.github.io/).
As an application developer, to learn about how to implement **"Smart Contracts"** for Hyperledger Fabric using Node.js, please visit the [documentation](https://fabric-shim.github.io/).

This project publishes `fabric-shim` public npm package for developers consumption.

### Folder structure

The "src" folder contains the resources to become part of the npm package, including javascript files, protobuf definition files (.proto) that the code depends on, and a package.json to describe the package.

The "build" folder contains the "build" steps. This being javascript there's no need to compile, but special build steps are still needed to accomplish the following:
* linting: to make sure we enforce a somewhat consistent coding style

Expand Down
63 changes: 63 additions & 0 deletions docs/tutorials/data-types-and-contracts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# How are data types handling with Contracts?
This document deals with JavaScript and TypeScript contracts

## Function parameters

### JavaScript - no metadata

This is the lowest common denominator; all data that is sent to the transaction function from the client is represented as strings.
Therefore the conversion that takes place in the lack of any metadata is

```
json = JSON.parse(data.toString());
```

If the json has a property of 'type' and that equals 'Buffer' then a byte buffer is constructed. The standard JSON.stringify() form of a byte buffer is to add the type field.

It is then left to JavaScript to be able to coerce the data as per standard language rules

### JavaScript - with metadata

If the metadata for the property specifies a String or a Number, then conversion to a String or Number takes place.
Otherwise, the same conversion takes place as with 'no-metadata'


### Typescript
Typescript needs to have no annotations for types, (other than arrays). The metadata is inferred with sufficient detail.
For arrays, the `@Param(<name>,<array type>,[<description>])` needs to be used to mark the type that is in the array.


## Return types

A transaction function is free to return anything it wishes. (Strictly speaking it must return a promise, and that can be resolved or rejected with anything).

With the Node.js runtime, either JavaScript or TypeScript can be used. For both languages you can supply additional metadata, either as annotations, or as a JSON file.

### JavaScript - no metadata

This is the lowest common denominator; if no metadata is either provided by annotations, file, or inferred by introspection this is behaviour.
All return values will be processed as follows:

```javascript
Buffer.from(JSON.stringify(functionsReturnValue))
```

The data will be stringified, then converted to a buffer to be returned. The buffer conversion is required by the shim api to communicate with the peer. This will be 'reversed' in the client SDK so that clients are given a string.

### JavaScript with metadata

It is beneficial to supply metadata; specifically in the case of JavaScript to identify strings and numbers from objects.

By doing this means that the transaction functions can

- Return strings and numbers directly. Numbers are converted to their textual form eg 42 becomes "42"
- Anything else is returned by being stringified first

## Typescript

Without the '@return' annotations and/or metadata Typescript introspection can not provide enough details about the return type. Primarily as this is a Promise that resolves a type introspection only indicates the promise aspect.

Metadata can be explicitly provided or the `@Returns(<string of type name>)` can be used to indicate the type. The annotation needs to have the type name specified as a string value.



133 changes: 133 additions & 0 deletions docs/tutorials/deep-dive-contract-interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Details on the programming model

Smart Contract packages consist of a set of code that contains the implementations of the code you want to run. Taken as a whole Hyperledger refers to this code as *chaincode*; a single chaincode is run within a docker container that is created and started by each peer. Depending on the language, a nodejs, Java or Go runtime might be used within the docker containers.

## Node.js project structure

Each Smart Contract package is, from a node perspective, a NPM module.

- package.json
- This needs to import the `fabric-contract-api` and `fabric-shim` npm modules
- The 'start' script must be set to `fabric-chaincode-node-start` - this has to be present for the peer to call the node module to start
- It is recommended to have a 'start:dev' script that can be used for development (details on this later)
- A 'main' entry to point to your `index.js` file contain the exports of the node module

- index.js
- It is mandatory to have a `contracts` element exported that is a array of classes.
- Each of these classes must extend the `Contract` class from the `fabric-contract-api` module
- Optionally, a custom `serializer` may be defined to control how data is converted for transmission between chaincode, peer and ultimately client applications.

*JavaScript example index.js*

```javascript
const cpcontract = require('./lib/papernet/papercontract.js');
module.exports.contracts = [cpcontract];

module.exports.CommercialPaper = require('./lib/papernet/paper.js');
```

*TypeScript example index.ts*

```typescript
import { GreetingContract } from './greetingcontract';
export { GreetingContract } from './greetingcontract';

export const contracts: any[] = [ GreetingContract ];
```

- contract-metadata/metadata.json

This file describes the *external* api that is exposed from these Smart Contracts; these are the functions that can be invoked by client applications. It describes all details about what is callable, and the datatypes of parameter and return values. It can also include information about documentation and licensing.

It describes the callable interface, and does not make any assertions about how the code is implemented.

## Defining your contract classes

The node module must export an array of one or more contract classes in the `contracts` property.
Each of these class must extend the correct type. At runtime each of these will have a single instance created, and will persist for the lifetime of the chaincode container.

> Each function MUST NOT use the instance to store data; all data MUST be stored within either the ledger, or within the transaction context
```typescript
import { Contract } from 'fabric-contract-api';

export class GreetingContract extends Contract {

public constructor() {
super('Greeting');
}
}
```

The constructor must call super, the argument is optional but is used to name this instance, and is used to refer to this instance when it is called by client . applications. If no argument is supplied, then the name of the class is used (in this case GreetingContract ). If an empty string is supplied that is valid, but not recommended.

It is not recommended to supply the same name, the behaviour if function names within the two contracts overlap is undefined.

### Transaction Functions

Within each contract instance, you may has few or many functions as you wish. Each of them is eligible to a transaction function that is callable by applications.
If a function name is prefixed with a _ it will be ignored. For Javascript all the functions will be eligible, but for Typescript the functions that are required must have a `@Transaction()` decorator

Each transaction must take as it's first parameter the transaction context

### Context

The first parameter is the 'transaction context' - it is quite plausible for several transactions to be invoked concurrently; the transaction context is required to give information specific to the transaction that is currently being executed.

Currently the 'stub' api for handling world state, and the 'Client Identity' is available from the context.
Each contract has a 'createContext' method that can be overridden by specific implementations to provide specific control to add information to the


### Before, After and Unknown Functions

The Contract class defines three functions that can be overridden by specific implementations.

```javascript
async beforeTransaction(ctx) {
// default implementation is do nothing
}

async afterTransaction(ctx, result) {
// default implementation is do nothing
}
```

Before is called immediately before the transaction function, and after immediately afterwards. Note that before does not get the arguments to the function (note this was the subject of debate, opinions welcomed). After gets the result from the transaction function (this is the result returned from transaction function without any processing).

If the transaction function throws an Error then the whole transaction fails, likewise if the before or after throws an Error then the transaction fails. (note that if say before throws an error the transaction function is never called, nor the after. Similarly if transaction function throws an Error, after is not called. )

Typical use cases of these functions would be

- logging of the functions called
- checks of the identity of the caller

The unknown function is called if the requested function is not known; the default implementation is to throw an error. `You've asked to invoke a function that does not exist: {requested function}`
However you can implement an `unkownTransition` function - this can return a successful or throw an error as you wish.

```javascript
async unknownTransaction(ctx) {
// throw an error here or return succesfully if you wish
}
```

## Metadata

### Supplying your own metadata
A correctly specified metadata file, at the top level has this structure

```json
{
"$schema" : "https://fabric-shim.github.io/release-1.4/contract-schema.json",
"info" : {

},
"contracts" : {

},
"components" : {

}
}
```

The metadata file that the user specifies has precedence over the information generated from the code, on a per section basis. If the user has not specified any of the above sections, then the 'gap' will be filled with auto generated values.
46 changes: 14 additions & 32 deletions docs/tutorials/using-contractinterface.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ This outlines the theory of the how the new node module works; with the fabric s

### 1: Chaincode is created as an npm module.

An initial `package.json` is as follows - the only runtime dependency as far as anything blockchain is concerned is the `fabric-chaincode-api`. This provides the API definition that can be used for development, and also unit test.
An initial `package.json` is as follows;

For development an implementation of the `fabric-shim` and specifically the CLI that accompanies it is required

**NOTE: for Fabric 1.4, this will need to be made a development dependency of the node module, and the `npm start` will need to call a defined app to start**
The dependencies of `fabric-chaincode-api` and `fabric-shim` will be required.

```
{
Expand All @@ -30,26 +28,22 @@ For development an implementation of the `fabric-shim` and specifically the CLI
"author": "",
"license": "ISC",
"dependencies": {
"fabric-chaincode-api: "^1.3.0"
"fabric-chaincode-api": "^1.4.0",
"fabric-shim": "^1.4.0"
}
}
```
Remember to add in any additional business logic, and testing libraries needed.
Remember to add in any additional business logic, and testing libraries needed

Please also add in `fabric-shim` as a dependency, and `fabric-chaincode-node` as the script to run for `npm start`. Therefore this would include
Adding `fabric-shim` as a dependency, gives a command `fabric-chaincode-node` that is the script to run for `npm start`.

```
"scripts": {
"start": "fabric-chaincode-node start",
"test": "nyc mocha test",
....
},
"dependencies": {
"fabric-contract-api": "^1.4.0-snapshot.1",
"fabric-shim": "^1.4.0-snapshot.1",
....
},
```


Expand All @@ -58,11 +52,14 @@ Please also add in `fabric-shim` as a dependency, and `fabric-chaincode-node` as
Chaincode is deployed by the peer in response to issuing a number of (usually CLI) commands. For node.js chaincode the location of the chaincode npm project is required (the directory that the package.json is in). This does not need to be an installed project, but has to have all the code, and the package.json.

A docker image is built for this chaincode, the package.json and code copied in. and `npm install` run.
> It is important to make sure that you have a `package-lock.json` to ensure the correct packages are imported.

After the install there is a 'bootstrap' process that starts the chaincode up (more details later). The constructors of the exported Contracts will be run at this point; these constructors are for setting the name and optional setup of the 'error/monitoring functions', (again more later). This instance of the contract will existing whilst this chaincode docker image is up.

When chaincode is instantiated or updated, the `init()` function is the chaincode is called. As with the `invoke()` call from the client, a fn name and parameters can be passed. Remember therefore to have specific functions to call on `init()` and `update()` in order to do any data initialization or migration that might be needed. These two functions have been abstracted away to focus on specific function implementations.
When chaincode is instantiated or updated, the `init()` function is the chaincode is called. As with the `invoke()` call from the client, a fn name and parameters can be passed. Remember therefore to have specific functions to call on `init()` and `update()` in order to do any data initialisation or migration that might be needed. These two functions have been abstracted away to focus on specific function implementations.

It is strongly recommended to use the npm shrinkwrap mechanism so the versions of the modules that are used are fixed.

Within the class you can defined as many or functions as you wish. These transaction functions will form the basis of the business logic you contract needs to execute. These are `async` functions, and can take parameters and return values. There is a single mandatory parameter of the 'transaction context'; this represents the currently executing transaction and is the way functions can access the world state, and other APIs.

### 3: What needs to be exported?

Expand All @@ -77,7 +74,7 @@ In this example we have a single value that can be queried and updated. This has
const UpdateValues = require('./updatevalues')
const RemoveValues = require('./removevalues')
module.exports.contracts = ['UpdateValues','RemoveValues'];
module.exports.contracts = [UpdateValues,RemoveValues];
```

This exports two classes that together form the Contract. There can be other code that within the model that is used in a support role.
Expand Down Expand Up @@ -123,28 +120,13 @@ class UpdateValues extends Contract
module.exports = UpdateValues;
```

Note that ALL the functions defined in these modules will be called by the client SDK.
Note that ALL the functions defined in these modules will be called by the client SDK.

- There are 3 functions `setup` `setNewAssetValue` and `doubleAssetValue` that can be called by issuing the appropriate invoke client side
- The `ctx` in the function is a transaction context; each time a invoke is called this will be a new instance that can be used by the function implementation to access apis such as the world state of information on invoking identity.
- The arguments are split out from the array passed on the invoke.
- The constructor contains a 'name' to help identify the sets of functions


### 5: Alternative ways of specifying the contracts

*package.json*

Instead of providing the Smart Contracts as exports, you can add details to the package.json. Using the above functions add this to the package.json

```
"contracts":{
"classes": ["removevalues.js","updatevalues.js"]
}
```

If present this takes precedence over the exports.

## Running chaincode in development mode

This is quite easy - as you need to run the startChaincode command.
Expand Down Expand Up @@ -217,7 +199,7 @@ For example

### Structure of the Transaction Context

In Fabric 1.2, there is a *stub* api that provides chaincode with functionality.
In Fabric, there is a *stub* api that provides chaincode with functionality.
No functionality has been removed, but a new approach to providing abstractions on this to facilitate programming.

*user additions*: additional properties can be added to the object to support for example common handling of the data serialization.
Expand Down
Loading

0 comments on commit 571d1e5

Please sign in to comment.