Skip to content
This repository has been archived by the owner on Feb 9, 2021. It is now read-only.

Commit

Permalink
Import release v0.10.1
Browse files Browse the repository at this point in the history
This is a legacy import of the release originally managed in pure
github.  You may find the history of this release in the archives
here:

https://github.com/hyperledger-archives/fabric-chaintool/releases/tag/v0.10.1

Change-Id: I57fcb68c85ccf92b6eee4b57f97f41ddf0f59e2b
Signed-off-by: Gregory Haskins <[email protected]>
  • Loading branch information
ghaskins committed Feb 6, 2017
1 parent 9a8948b commit e40a745
Show file tree
Hide file tree
Showing 48 changed files with 447 additions and 585 deletions.
41 changes: 14 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ _chaintool_ is a toolchain to assist in various phases of [Hyperledger Fabric](h

### Why?

Current chaincode development is rather unstructured outside of the coarse-level callbacks for invoke or query passing a {function-name, argument-array} string-based tuple. The result of this is that input translation/validation is a manual, explicit, and likely fragile process in each chaincode function. Additionally, any potential chaincode consumer needs to study the chaincode source in order to ascertain its API.
Current chaincode development is rather unstructured outside of the coarse-level callbacks for Invoke() passing opaque bytes. The result of this is that input translation/validation is a manual, explicit, and likely fragile process in each chaincode function. Additionally, any potential chaincode consumer needs to study the chaincode source in order to ascertain its API.

Consider that some chaincode applications may employ confidentiality to hide their source, while others may wish to employ alternative programming languages. This aside, chaincode deployment lifecycles may be long enough to require us to be aware of managing potential API incompatibilities between the chaincode and its clients. It starts to become clear that there are some advantages to allowing chaincode to express its API interfaces in a way that is independent from the underlying implementation/language and in a manner that supports some form of schema management.

Expand All @@ -30,7 +30,7 @@ _chaintool_ provides some other benefits too, such as consistent language-neutra

```
$ chaintool -h
chaintool version: v0.7
chaintool version: v0.10.1
Usage: chaintool [general-options] action [action-options]
Expand Down Expand Up @@ -60,7 +60,7 @@ In all cases, you may obtain subcommand specific help by invoking "chaintool _$s

```
$ chaintool package -h
chaintool version: v0.7
chaintool version: v0.10.1
Description: chaintool package - Package the chaincode into a CAR file for deployment
Expand Down Expand Up @@ -210,7 +210,7 @@ The only core requirement is that both _chaintool_ and the chosen Hyperledger ne

#### Interface Declarations

Interfaces (as included in ./src/interfaces) may be in one or two categories: Provided or Consumed. _Provided_ means that the chaincode implements the interface and supports having clients or other chaincode invoke methods as declared. Likewise, _consumed_ indicates that the chaincode expects to perform inter-chaincode invoke/query operations to a disparate chaincode instance that provides the interface. It is perfectly fine (though perhaps uncommon) for a chaincode to both provide and consume a given interface (such as for proxy contracts which may accept operations in a polymorphic manner before passing operations on to a concrete instance).
Interfaces (as included in ./src/interfaces) may be in one or two categories: Provided or Consumed. _Provided_ means that the chaincode implements the interface and supports having clients or other chaincode invoke methods as declared. Likewise, _consumed_ indicates that the chaincode expects to perform inter-chaincode invoke operations to a disparate chaincode instance that provides the interface. It is perfectly fine (though perhaps uncommon) for a chaincode to both provide and consume a given interface (such as for proxy contracts which may accept operations in a polymorphic manner before passing operations on to a concrete instance).

Both Provides and Consumes are expressed as an array of 1 or more entries. For example:

Expand Down Expand Up @@ -273,20 +273,14 @@ message BalanceResult {
int32 balance = 1;
}
transactions {
functions {
void MakePayment(PaymentParams) = 1;
void DeleteAccount(Entity) = 2;
}
queries {
BalanceResult CheckBalance(Entity) = 1;
BalanceResult CheckBalance(Entity) = 3;
}
```

The _message_ definitions are almost 1:1 with protobuf grammar. The largest divergence is w.r.t. the _transactions_ and _queries_ sections. These two are similar to one another as well as to the notion of service/rpc in protobuf grammar. The reason we diverged is for a few different reasons:

- Chaincode has a strong delineation between and invoke and a query, and it was important for the parser to be able to understand the breakdown so that the proper code could be emitted
- It was felt that the lack of "field indices" in the protobuf service/rpc grammar was a large shortcoming in ABI compatibility. Therefore, the grammar used here retains the notion of indices even for function calls.
The _message_ definitions are almost 1:1 with protobuf grammar. The largest divergence is w.r.t. the _functions_ section. This section is similiar to the notion of service/rpc in protobuf grammar. We diverged from the protobuf/grpc grammar because it was felt that the lack of "field indices" was a large shortcoming in ABI compatibility. Therefore, the grammar used here retains the notion of indices even for function calls.

The main purpose of the grammar is to define RPC functions. For reasons of ABI stability, it was decided that all RPCs will have the following properties:
- Be indexed (e.g. ABI depends on index stability, not function name)
Expand Down Expand Up @@ -316,23 +310,16 @@ message ChaincodeInput {
}
```
Chaintool deterministically maps transactions/queries declared within a CCI to an [encoded function name](#function-encoding), and expects the corresponding input parameter to be a base64 encoded protobuf message as the first and only arg string.
Chaintool deterministically maps functions declared within a CCI to an [encoded function name](#function-encoding), and expects the corresponding input parameter to be a base64 encoded protobuf message as the first and only arg string.

Example:
```
{"function":"org.hyperledger.chaincode.example02/query/1","args":["CgNmb28="]}}
{"function":"org.hyperledger.chaincode.example02/fcn/3","args":["CgNmb28="]}}
```

#### Function Encoding

Function naming follows the convention *interface-name/method-type/method-index*. For instance, invoking *MakePayment* from our [example](./examples/example02/app/src/interfaces/org.hyperledger.chaincode.example02.cci) would be *org.hyperledger.chaintool.example02/txn/1*. Because its transaction #1 in the org.hyperledger.chaintool.example02 interface.

##### Method Types

There are two types of methods: transactions and queries. We therefore have two values in the function name that correspond to the underlying method type:

- "txn" - transactions
- "query" - queries
Function naming follows the convention *interface-name/method-type/method-index*. For instance, invoking *MakePayment* from our [example](./examples/example02/app/src/interfaces/org.hyperledger.chaincode.example02.cci) would be *org.hyperledger.chaintool.example02/fcn/1*. Because its function #1 in the org.hyperledger.chaintool.example02 interface.

### Output Protocol

Expand Down Expand Up @@ -368,9 +355,9 @@ message PaymentParams {
//
// Available RPC functions exported by this interface
//
// void MakePayment(PaymentParams) -> org.hyperledger.chaincode.example02/txn/1
// void DeleteAccount(Entity) -> org.hyperledger.chaincode.example02/txn/2
// BalanceResult CheckBalance(Entity) -> org.hyperledger.chaincode.example02/query/1
// void MakePayment(PaymentParams) -> org.hyperledger.chaincode.example02/fcn/1
// void DeleteAccount(Entity) -> org.hyperledger.chaincode.example02/fcn/2
// BalanceResult CheckBalance(Entity) -> org.hyperledger.chaincode.example02/fcn/3
```
## Metadata

Expand Down Expand Up @@ -409,7 +396,7 @@ message Facts {
repeated Fact facts = 1;
}
queries {
functions {
Interfaces GetInterfaces(GetInterfacesParams) = 1;
InterfaceDescriptor GetInterface(GetInterfaceParams) = 2;
Facts GetFacts(GetFactsParams) = 3;
Expand Down
58 changes: 23 additions & 35 deletions examples/example02/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,48 +14,36 @@ This directory contains an implementation of the chaincode application called "e
│   ├── appinit.cci
│   └── org.hyperledger.chaincode.example02.cci
└── client
├── rest
│   ├── cljs
│   │   ├── Makefile
│   │   ├── appinit.proto
│   │   ├── org.hyperledger.chaincode.example02.proto
│   │   ├── project.clj
│   │   └── src
│   │   └── example02
│   │   ├── core.cljs
│   │   ├── main.cljs
│   │   └── rpc.cljs
│   └── nodejs
│   ├── appinit.proto
│   ├── index.js
│   ├── org.hyperledger.chaincode.example02.proto
│   └── package.json
└── sdk
├── Makefile
├── cljs
│   ├── Makefile
│   ├── appinit.proto
│   ├── org.hyperledger.chaincode.example02.proto
│   ├── project.clj
│   └── src
│   └── example02
│   ├── core.cljs
│   ├── hlc
│   │   ├── core.cljs
│   │   └── user.cljs
│   ├── main.cljs
│   ├── rpc.cljs
│   └── util.cljs
└── nodejs
├── appinit.proto
├── index.js
├── util.js
├── org.hyperledger.chaincode.example02.proto
├── project.clj
└── src
└── example02
├── core.cljs
├── hlc
│   ├── core.cljs
│   └── user.cljs
├── main.cljs
├── rpc.cljs
└── util.cljs
└── package.json
```
* app - contains a org.hyperledger.chaincode.golang platform based chaincode application.
* This is the code deployed to the blockchain
* client - client applications for interacting with the chaincode application
* rest - REST api based clients
* nodejs - A simple demonstration of using nodejs+REST.
* cljs - A complete client for example02 over REST written in ClojureScript
* sdk - SDK based client, written in ClojureScript
* nodejs - A simple demonstration of using nodejs.
* cljs - A complete client for example02 written in ClojureScript

## Deploying and interacting with the example02
### Step 1 - Fabric environment
You will need a functioning peer that has chaintool v0.7 or higher available in the $PATH. You may check the version of chaintool you have with 'chaintool -h'. Once confirmed, start the peer with _peer node start_ as you normally would. It is advised to keep the configuration as simple as possible (1 VP, no security, noops consensus)
You will need a functioning peer that has chaintool v0.10.1 or higher available in the $PATH. You may check the version of chaintool you have with 'chaintool -h'. Once confirmed, start the peer with _peer node start_ as you normally would. It is advised to keep the configuration as simple as possible (1 VP, no security, noops consensus)

### Step 2 - Package the chaincode application
Run 'chaintool package' from the app folder, noting the CAR output path
Expand All @@ -82,11 +70,11 @@ Chaincode SHA3: f7026e0675b22a9d78b9f7f0cb97c93165bdefedc86de97f00e76b506c7
#### Note:
The _chaintool package_ command is designed to package for deployment, not development. If you started your node with _peer node start --peer-chaincodedev_, run _chaintool build_ instead. This is analogous to building non-chaintool chaincode using _go build_. The output will be placed in the _app/build/bin/_ directory.
### Step 3 - Compile the client
Run 'make' from the client/rest/cljs folder
Run 'make' from the client/cljs folder
```
$ make
lein npm install
[email protected] /Users/ghaskins/sandbox/git/chaintool/examples/example02/client/rest/cljs
[email protected] /Users/ghaskins/sandbox/git/chaintool/examples/example02/client/cljs
├─┬ [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ message BalanceResult {
int32 balance = 1;
}

transactions {
functions {
void MakePayment(PaymentParams) = 1;
void DeleteAccount(Entity) = 2;
BalanceResult CheckBalance(Entity) = 3;
}

queries {
BalanceResult CheckBalance(Entity) = 1;
}
Binary file not shown.
Binary file not shown.
153 changes: 153 additions & 0 deletions examples/example02/client/nodejs/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
var program = require('commander');
var pb = require("protobufjs");

var builder = pb.newBuilder({ convertFieldsToCamelCase: true });

pb.loadProtoFile("./protos/appinit.proto", builder);
var init = builder.build("appinit");

pb.loadProtoFile("./protos/org.hyperledger.chaincode.example02.proto", builder);
var app = builder.build("org.hyperledger.chaincode.example02");

var hfc = require('fabric-client');
var hfcutils = require('fabric-client/lib/utils.js');
var utils = require('./lib/util.js');
var Peer = require('fabric-client/lib/Peer.js');
var Orderer = require('fabric-client/lib/Orderer.js');
var CA = require('fabric-ca-client/lib/FabricCAClientImpl.js');
var User = require('fabric-client/lib/User.js');

var chain;
var peer;

function createRequest(fcn, args) {
var tx_id = hfcutils.buildTransactionID({length:12});
var nonce = hfcutils.getNonce();

// send proposal to endorser
var request = {
type: 'car',
targets: [peer],
chainId: 'testchainid',
chaincodeId: 'mycc',
fcn: fcn,
args: [args.toBase64()],
txId: tx_id,
nonce: nonce
};

return request;
}

function connect() {
var client = new hfc();
chain = client.newChain('chaintool-demo');

peer = new Peer('grpc://localhost:7051');
var orderer = new Orderer('grpc://localhost:7050');

chain.addOrderer(orderer);
chain.addPeer(peer);

return utils.setStateStore(client, ".hfc-kvstore")
.then(function() {
var ca = new CA('http://localhost:7054');

return utils.getUser(client, ca, 'admin', 'adminpw');
});
}

function deploy(args, path) {

var request = createRequest('init', new init.Init(args));
if (path) {
request.chaincodePath = path;
} else {
chain.setDevMode(true);
}

// send proposal to endorser
return chain.sendDeploymentProposal(request)
.then(function(response) { utils.processProposalResponse(chain, response); })
.then(utils.intradelay);
}

function sendTransaction(fcn, args) {
var request = createRequest(fcn, args);
return chain.sendTransactionProposal(request)
.then(function(response) { utils.processProposalResponse(chain, response); })
.then(utils.intradelay);
}

function sendQuery(fcn, args) {
var request = createRequest(fcn, args);
return chain.queryByChaincode(request);
}

function makePayment(args) {
return sendTransaction('org.hyperledger.chaincode.example02/fcn/1',
new app.PaymentParams(args));
}

function checkBalance(args) {
return sendQuery('org.hyperledger.chaincode.example02/fcn/3',
new app.Entity(args))
.then(function(results) {
return app.BalanceResult.decode64(results[0].toString('utf-8'));
});
}

program
.version('0.0.1');

program
.command('deploy')
.description('deploy description')
.option("-p, --path <path>", "Path to chaincode.car")
.action(function(options){
return connect()
.then(function() {
return deploy({
'partyA': {'entity':'A', 'value':100},
'partyB': {'entity':'B', 'value':200}},
options.path);
})
.catch(function(err) {
console.log("error:" + err);
});
});

program
.command('makepayment <partySrc> <partyDst> <amount>')
.description('makepayment description')
.action(function(partySrc, partyDst, amount){
return connect()
.then(function() {
return makePayment({
'partySrc': partySrc,
'partyDst': partyDst,
'amount': parseInt(amount)});
})
.catch(function(err) {
console.log("error:" + err);
});
});

program
.command('checkbalance <id>')
.description('checkbalance description')
.action(function(id){
return connect()
.then(function() {
return checkBalance({'id':id});
})
.then(function(result) {
console.log("balance:" + result.balance);
})
.catch(function(err) {
console.log("error:" + err);
});
});


program.parse(process.argv);
Loading

0 comments on commit e40a745

Please sign in to comment.