diff --git a/README.md b/README.md index 2a45a63..c0f2e8a 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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] @@ -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 @@ -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: @@ -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) @@ -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 @@ -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 @@ -409,7 +396,7 @@ message Facts { repeated Fact facts = 1; } -queries { +functions { Interfaces GetInterfaces(GetInterfacesParams) = 1; InterfaceDescriptor GetInterface(GetInterfaceParams) = 2; Facts GetFacts(GetFactsParams) = 3; diff --git a/examples/example02/README.md b/examples/example02/README.md index 931beab..3226222 100644 --- a/examples/example02/README.md +++ b/examples/example02/README.md @@ -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 @@ -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 -example02@0.1.0-SNAPSHOT /Users/ghaskins/sandbox/git/chaintool/examples/example02/client/rest/cljs +example02@0.1.0-SNAPSHOT /Users/ghaskins/sandbox/git/chaintool/examples/example02/client/cljs ├─┬ protobufjs@5.0.1 │ ├─┬ ascli@1.0.0 │ │ ├── colour@0.7.1 diff --git a/examples/example02/app/src/interfaces/org.hyperledger.chaincode.example02.cci b/examples/example02/app/src/interfaces/org.hyperledger.chaincode.example02.cci index ad02021..f9f40e3 100644 --- a/examples/example02/app/src/interfaces/org.hyperledger.chaincode.example02.cci +++ b/examples/example02/app/src/interfaces/org.hyperledger.chaincode.example02.cci @@ -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; -} diff --git a/examples/example02/client/sdk/Makefile b/examples/example02/client/cljs/Makefile similarity index 100% rename from examples/example02/client/sdk/Makefile rename to examples/example02/client/cljs/Makefile diff --git a/examples/example02/client/sdk/project.clj b/examples/example02/client/cljs/project.clj similarity index 100% rename from examples/example02/client/sdk/project.clj rename to examples/example02/client/cljs/project.clj diff --git a/examples/example02/client/rest/cljs/appinit.proto b/examples/example02/client/cljs/protos/appinit.proto similarity index 100% rename from examples/example02/client/rest/cljs/appinit.proto rename to examples/example02/client/cljs/protos/appinit.proto diff --git a/examples/example02/client/rest/cljs/org.hyperledger.chaincode.example02.proto b/examples/example02/client/cljs/protos/org.hyperledger.chaincode.example02.proto similarity index 100% rename from examples/example02/client/rest/cljs/org.hyperledger.chaincode.example02.proto rename to examples/example02/client/cljs/protos/org.hyperledger.chaincode.example02.proto diff --git a/examples/example02/client/sdk/src/example02/core.cljs b/examples/example02/client/cljs/src/example02/core.cljs similarity index 100% rename from examples/example02/client/sdk/src/example02/core.cljs rename to examples/example02/client/cljs/src/example02/core.cljs diff --git a/examples/example02/client/sdk/src/example02/hlc/core.cljs b/examples/example02/client/cljs/src/example02/hfc/core.cljs similarity index 100% rename from examples/example02/client/sdk/src/example02/hlc/core.cljs rename to examples/example02/client/cljs/src/example02/hfc/core.cljs diff --git a/examples/example02/client/sdk/src/example02/hlc/user.cljs b/examples/example02/client/cljs/src/example02/hfc/user.cljs similarity index 100% rename from examples/example02/client/sdk/src/example02/hlc/user.cljs rename to examples/example02/client/cljs/src/example02/hfc/user.cljs diff --git a/examples/example02/client/sdk/src/example02/main.cljs b/examples/example02/client/cljs/src/example02/main.cljs similarity index 100% rename from examples/example02/client/sdk/src/example02/main.cljs rename to examples/example02/client/cljs/src/example02/main.cljs diff --git a/examples/example02/client/sdk/src/example02/rpc.cljs b/examples/example02/client/cljs/src/example02/rpc.cljs similarity index 100% rename from examples/example02/client/sdk/src/example02/rpc.cljs rename to examples/example02/client/cljs/src/example02/rpc.cljs diff --git a/examples/example02/client/sdk/src/example02/util.cljs b/examples/example02/client/cljs/src/example02/util.cljs similarity index 100% rename from examples/example02/client/sdk/src/example02/util.cljs rename to examples/example02/client/cljs/src/example02/util.cljs diff --git a/examples/example02/client/lib/fabric-ca-client-0.1.0.tgz b/examples/example02/client/lib/fabric-ca-client-0.1.0.tgz new file mode 100644 index 0000000..d057611 Binary files /dev/null and b/examples/example02/client/lib/fabric-ca-client-0.1.0.tgz differ diff --git a/examples/example02/client/lib/fabric-client-0.1.0.tgz b/examples/example02/client/lib/fabric-client-0.1.0.tgz new file mode 100644 index 0000000..978063a Binary files /dev/null and b/examples/example02/client/lib/fabric-client-0.1.0.tgz differ diff --git a/examples/example02/client/nodejs/client.js b/examples/example02/client/nodejs/client.js new file mode 100644 index 0000000..98fa88a --- /dev/null +++ b/examples/example02/client/nodejs/client.js @@ -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 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 ') + .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 ') + .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); diff --git a/examples/example02/client/nodejs/lib/util.js b/examples/example02/client/nodejs/lib/util.js new file mode 100644 index 0000000..b2c56bd --- /dev/null +++ b/examples/example02/client/nodejs/lib/util.js @@ -0,0 +1,80 @@ +var hfc = require('fabric-client'); +var utils = require('fabric-client/lib/utils.js'); +var User = require('fabric-client/lib/User.js'); + +module.exports = { + setStateStore: function(client, path) { + return new Promise(function(resolve, reject) { + return hfc.newDefaultKeyValueStore({path: path}) + .then(function(store) { + client.setStateStore(store); + resolve(true); + }); + }); + }, + + getUser: function(client, cop, username, password) { + return client.getUserContext(username) + .then( + function(user) { + if (user && user.isEnrolled()) { + return Promise.resolve(user); + } else { + // need to enroll it with COP server + console.log("enrolling"); + return cop.enroll({ + enrollmentID: username, + enrollmentSecret: password + }).then( + function(enrollment) { + console.log("enrollment"); + var member = new User(username, client); + return member.setEnrollment(enrollment.key, enrollment.certificate) + .then(function() { + return client.setUserContext(member); + }); + } + ); + } + } + ); + }, + + intradelay: function() { + return new Promise(function(resolve, reject) { + setTimeout(resolve, 20000); + }); + }, + + processProposalResponse: function(chain, results) { + var proposalResponses = results[0]; + //logger.debug('deploy proposalResponses:'+JSON.stringify(proposalResponses)); + var proposal = results[1]; + var header = results[2]; + var all_good = true; + + for(var i in proposalResponses) { + let one_good = false; + + if (proposalResponses && + proposalResponses[0].response && + proposalResponses[0].response.status === 200) { + + one_good = true; + } + all_good = all_good & one_good; + } + + if (all_good) { + var request = { + proposalResponses: proposalResponses, + proposal: proposal, + header: header + }; + return chain.sendTransaction(request); + } else { + throw "bad result:" + results; + } + + } +}; diff --git a/examples/example02/client/nodejs/package.json b/examples/example02/client/nodejs/package.json new file mode 100644 index 0000000..7e3d369 --- /dev/null +++ b/examples/example02/client/nodejs/package.json @@ -0,0 +1,20 @@ +{ + "name": "chaintool-example02", + "version": "0.0.1", + "description": "", + "main": "client.js", + "dependencies": { + "protobufjs":"5.0.1", + "commander":"2.9.0", + "winston":"2.3.1" + }, + "files": [ + "protos", + "lib" + ], + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/examples/example02/client/rest/nodejs/appinit.proto b/examples/example02/client/nodejs/protos/appinit.proto similarity index 100% rename from examples/example02/client/rest/nodejs/appinit.proto rename to examples/example02/client/nodejs/protos/appinit.proto diff --git a/examples/example02/client/rest/nodejs/org.hyperledger.chaincode.example02.proto b/examples/example02/client/nodejs/protos/org.hyperledger.chaincode.example02.proto similarity index 100% rename from examples/example02/client/rest/nodejs/org.hyperledger.chaincode.example02.proto rename to examples/example02/client/nodejs/protos/org.hyperledger.chaincode.example02.proto diff --git a/examples/example02/client/rest/cljs/Makefile b/examples/example02/client/rest/cljs/Makefile deleted file mode 100644 index 7d75171..0000000 --- a/examples/example02/client/rest/cljs/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -SRCS = $(shell find src -type f) -LEIN = $(shell which lein || echo ../../../../../lein) - -all: client - -client: ./out/example02.js - -node_modules: project.clj - $(LEIN) npm install - -./out/example02.js: $(SRCS) node_modules Makefile - $(LEIN) cljsbuild once - @echo "Compilation complete: use \"node $@ --help\" for execution instructions" - -clean: - -@rm -rf ./out ||: - -@rm -rf node_modules ||: diff --git a/examples/example02/client/rest/cljs/project.clj b/examples/example02/client/rest/cljs/project.clj deleted file mode 100644 index 612798a..0000000 --- a/examples/example02/client/rest/cljs/project.clj +++ /dev/null @@ -1,24 +0,0 @@ -(defproject example02 "0.1.0-SNAPSHOT" - :min-lein-version "2.0.0" - :description "Clojurescript client for chaintool version of example02" - :url "http://example.com/FIXME" - :dependencies [[org.clojure/clojure "1.8.0"] - [org.clojure/clojurescript "1.8.34"] - [org.clojure/tools.cli "0.3.3"]] - :jvm-opts ^:replace ["-Xmx1g" "-server"] - :plugins [[lein-npm "0.6.1"] - [lein-cljsbuild "1.1.3"]] - :npm {:dependencies [[source-map-support "0.4.0"] - [protobufjs "5.0.1"]]} - :source-paths ["src" "target/classes"] - :clean-targets ["out" "release"] - :target-path "target" - :cljsbuild {:builds [{:id "example02" - :source-paths ["src"] - :compiler {:output-to "out/example02.js" - :output-dir "out" - :source-map true - :optimizations :none - :target :nodejs - :main "example02.main" - :pretty-print true}}]}) diff --git a/examples/example02/client/rest/cljs/src/example02/core.cljs b/examples/example02/client/rest/cljs/src/example02/core.cljs deleted file mode 100644 index 6f37d99..0000000 --- a/examples/example02/client/rest/cljs/src/example02/core.cljs +++ /dev/null @@ -1,43 +0,0 @@ -(ns example02.core - (:require [cljs.nodejs :as nodejs] - [example02.rpc :as rpc])) - -(def pb (nodejs/require "protobufjs")) -(def builder (.newBuilder pb)) - -(defn- loadproto [name] - (do - (.loadProtoFile pb (str "./" name ".proto") builder) - (.build builder name))) - -(def init (loadproto "appinit")) -(def app (loadproto "org.hyperledger.chaincode.example02")) - -(defn deploy [{:keys [args] :as options}] - (rpc/deploy (assoc options - :func "init" - :args (init.Init. args) - :cb (fn [resp] (println "Response:" resp))))) - -(defn make-payment [{:keys [args] :as options}] - (rpc/invoke (assoc options - :func "org.hyperledger.chaincode.example02/txn/1" - :args (app.PaymentParams. args) - :cb (fn [resp] (println "Response:" resp))))) - -(defn delete-account [{:keys [args] :as options}] - (rpc/invoke (assoc options - :func "org.hyperledger.chaincode.example02/txn/2" - :args (app.Entity. args) - :cb (fn [resp] (println "Response:" resp))))) - -(defn check-balance [{:keys [args] :as options}] - (rpc/query (assoc options - :func "org.hyperledger.chaincode.example02/query/1" - :args (app.Entity. args) - :cb (fn [resp] - (if (= (->> resp :result :status) "OK") - (let [result (->> resp :result :message app.BalanceResult.decode64)] - (println "Success: Balance =" (.-balance result))) - ;; else - (println "Failure:" resp)))))) diff --git a/examples/example02/client/rest/cljs/src/example02/main.cljs b/examples/example02/client/rest/cljs/src/example02/main.cljs deleted file mode 100644 index b0211c2..0000000 --- a/examples/example02/client/rest/cljs/src/example02/main.cljs +++ /dev/null @@ -1,98 +0,0 @@ -(ns example02.main - (:require [clojure.string :as string] - [cljs.nodejs :as nodejs] - [cljs.tools.cli :refer [parse-opts]] - [example02.core :as core])) - - -(nodejs/enable-util-print!) - -(def _commands - [["deploy" - {:fn core/deploy - :default-args #js {:partyA #js { - :entity "foo" - :value 100 - } - :partyB #js { - :entity "bar" - :value 100 - }}}] - ["make-payment" - {:fn core/make-payment - :default-args #js {:partySrc "foo" - :partyDst "bar" - :amount 10}}] - ["delete-account" - {:fn core/delete-account - :default-args #js {:id "foo"}}] - ["check-balance" - {:fn core/check-balance - :default-args #js {:id "foo"}}]]) - -(def commands (into {} _commands)) -(defn print-commands [] (->> commands keys vec print-str)) - -(def options - [[nil "--host HOST" "Host name" - :default "localhost"] - [nil "--port PORT" "Port number" - :default 5000 - :parse-fn #(js/parseInt %) - :validate [#(< 0 % 65536) "Must be a number between 0 and 65536"]] - ["-p" "--path PATH" "Path/URL to the chaincode (deploy only, mutually exclsive with -n)"] - ["-n" "--name NAME" "Name of the chaincode (mutually exclusive with -p)"] - ["-c" "--command CMD" (str "One of " (print-commands)) - :default "check-balance" - :validate [#(contains? commands %) (str "Supported commands: " (print-commands))]] - ["-a" "--args ARGS" "JSON formatted arguments to submit"] - ["-h" "--help"]]) - -(defn exit [status msg & rest] - (do - (apply println msg rest) - status)) - -(defn prep-usage [msg] (->> msg flatten (string/join \newline))) - -(defn usage [options-summary] - (prep-usage ["Usage: example02 [options]" - "" - "Options Summary:" - options-summary - "" - ])) - -(defn run [{:keys [path name command args] :as options}] - (let [desc (commands command) - _args (if (nil? args) (:default-args desc) (.parse js/JSON args))] - (cond - - (and (some? path) (some? name)) - (println "ERROR: -p and -n are mutually exclusive") - - (and (nil? path) (nil? name)) - (println "ERROR: Must specify either -p or -n") - - (and (some? path) (not= command "deploy")) - (println "ERROR: -p only valid with deploy command") - - :else - (let [id (if (some? path) #js {:path path} #js {:name name})] - (println (str "Running " command "(" (.stringify js/JSON _args) ")")) - ((:fn desc) (assoc options :id id :args _args)))))) - -(defn -main [& args] - (let [{:keys [options arguments errors summary]} (parse-opts args options)] - (cond - - (:help options) - (exit 0 (usage summary)) - - (not= errors nil) - (exit -1 "Error: " (string/join errors)) - - :else - (run options)))) - -(set! *main-cli-fn* -main) diff --git a/examples/example02/client/rest/cljs/src/example02/rpc.cljs b/examples/example02/client/rest/cljs/src/example02/rpc.cljs deleted file mode 100644 index d1a7cad..0000000 --- a/examples/example02/client/rest/cljs/src/example02/rpc.cljs +++ /dev/null @@ -1,45 +0,0 @@ -(ns example02.rpc - (:require [cljs.nodejs :as nodejs])) - -(def http (nodejs/require "http")) - -(defn- stringify [json] - (.stringify js/JSON json)) - -(defn- response-handler [cb resp] - (.setEncoding resp "utf8") - (.on resp "data" (fn [data] - (let [resp (js->clj (.parse js/JSON data) :keywordize-keys true)] - (cb (select-keys resp [:error :result])))))) - -(defn- post [{:keys [host port path method id func args cb]}] - (let [meta #js {:host host - :port port - :path path - :method "POST" - :headers #js {:Content-Type "application/json"}} - data (stringify - #js {:jsonrpc "2.0" - :method method - :params #js {:type 3 - :chaincodeID id - :ctorMsg #js {:function func - :args #js [(.toBase64 args)]}} - :id "1"}) - req (.request http meta (partial response-handler cb))] - - (println "HTTP POST:" (str "http://" host ":" port) "-" data) - (.write req data) - (.end req))) - -(defn- chaincode-post [args] - (post (assoc args :path "/chaincode"))) - -(defn deploy [args] - (chaincode-post (assoc args :method "deploy"))) - -(defn invoke [args] - (chaincode-post (assoc args :method "invoke"))) - -(defn query [args] - (chaincode-post (assoc args :method "query"))) diff --git a/examples/example02/client/rest/nodejs/index.js b/examples/example02/client/rest/nodejs/index.js deleted file mode 100644 index 069ae30..0000000 --- a/examples/example02/client/rest/nodejs/index.js +++ /dev/null @@ -1,101 +0,0 @@ -var http = require('http'); -var pb = require("protobufjs"); - -var builder = pb.newBuilder({ convertFieldsToCamelCase: true }); - -pb.loadProtoFile("./appinit.proto", builder); -var init = builder.build("appinit"); - -pb.loadProtoFile("./org.hyperledger.chaincode.example02.proto", builder); -var app = builder.build("org.hyperledger.chaincode.example02"); - -function Deploy(id, params) { - - invoke('deploy', id, 'init', new init.Init(params), function(res) { - res.setEncoding('utf8'); - res.on('data', function (chunk) { - var resp = JSON.parse(chunk); - console.log('Response: ' + chunk); - }); - - }); -} - -function CheckBalance(id, params) { - - invoke('query', id, 'org.hyperledger.chaincode.example02/query/1', new app.Entity(params), function(res) { - res.setEncoding('utf8'); - res.on('data', function (chunk) { - var resp = JSON.parse(chunk); - if (resp.result.status == "OK") { - var result = app.BalanceResult.decode64(resp.result.message); - console.log("BalanceResult: " + result.balance); - } else { - console.log('ERROR: ' + chunk); - } - }); - - }); -} - -function invoke(method, id, func, args, cb) { - var post_data = JSON.stringify({ - 'jsonrpc': '2.0', - 'method': method, - 'params': { - 'type': 3, - 'chaincodeID': id, - 'ctorMsg': { - 'function': func, - 'args':[args.toBase64()] - } - }, - "id": 1 - }); - console.log(post_data); - post(post_data, '/chaincode', cb); -} - -function post(pdata, path, cb) { - var post_options = { - host: 'localhost', - port: '5000', - path: path, - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }; - - var post_req = http.request(post_options, cb); - - // post the data - post_req.write(pdata); - post_req.end(); - -} - -Deploy( - { - 'name': 'mycc' - }, - { - 'partyA': { - 'entity': 'foo', - 'value': 100 - }, - 'partyB': { - 'entity': 'bar', - 'value': 100 - } - } -); - -CheckBalance( - { - 'name': 'mycc' - }, - { - "id": "foo" - } -); diff --git a/examples/example02/client/rest/nodejs/package.json b/examples/example02/client/rest/nodejs/package.json deleted file mode 100644 index 4f2cb22..0000000 --- a/examples/example02/client/rest/nodejs/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "protobuf", - "version": "1.0.0", - "description": "", - "main": "index.js", - "dependencies": { - "protobufjs":"5.0.1" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC" -} diff --git a/examples/example02/client/sdk/appinit.proto b/examples/example02/client/sdk/appinit.proto deleted file mode 100644 index d92e13d..0000000 --- a/examples/example02/client/sdk/appinit.proto +++ /dev/null @@ -1,19 +0,0 @@ -// -// Generated by chaintool. DO NOT EDIT!! -// - -syntax = "proto3"; - -package appinit; - -message Init { - Party partyA = 1; - Party partyB = 2; -} - -message Party { - string entity = 1; - int32 value = 2; -} - - diff --git a/examples/example02/client/sdk/org.hyperledger.chaincode.example02.proto b/examples/example02/client/sdk/org.hyperledger.chaincode.example02.proto deleted file mode 100644 index 6a51ace..0000000 --- a/examples/example02/client/sdk/org.hyperledger.chaincode.example02.proto +++ /dev/null @@ -1,29 +0,0 @@ -// -// Generated by chaintool. DO NOT EDIT!! -// - -syntax = "proto3"; - -package org.hyperledger.chaincode.example02; - -message BalanceResult { - int32 balance = 1; -} - -message Entity { - string id = 1; -} - -message PaymentParams { - string partySrc = 1; - string partyDst = 2; - int32 amount = 3; -} - - -// -// 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 diff --git a/examples/invoker/src/chaincode/chaincode.go b/examples/invoker/src/chaincode/chaincode.go index ecf09c1..66695e9 100755 --- a/examples/invoker/src/chaincode/chaincode.go +++ b/examples/invoker/src/chaincode/chaincode.go @@ -17,7 +17,7 @@ specific language governing permissions and limitations under the License. */ -package chaincode +package main import ( "fmt" @@ -90,7 +90,7 @@ func (t *ChaincodeExample) CheckBalance(stub shim.ChaincodeStubInterface, param func main() { self := &ChaincodeExample{} - interfaces := ccs.Interfaces { + interfaces := ccs.Interfaces{ "org.hyperledger.chaincode.example02": self, "appinit": self, } diff --git a/examples/invoker/src/interfaces/org.hyperledger.chaincode.example02.cci b/examples/invoker/src/interfaces/org.hyperledger.chaincode.example02.cci index ad02021..dc0a39f 100644 --- a/examples/invoker/src/interfaces/org.hyperledger.chaincode.example02.cci +++ b/examples/invoker/src/interfaces/org.hyperledger.chaincode.example02.cci @@ -13,11 +13,8 @@ message BalanceResult { int32 balance = 1; } -transactions { +functions { void MakePayment(PaymentParams) = 1; void DeleteAccount(Entity) = 2; -} - -queries { - BalanceResult CheckBalance(Entity) = 1; + BalanceResult CheckBalance(Entity) = 3; } diff --git a/examples/parameterless/src/interfaces/org.hyperledger.chaincode.example.parameterless.cci b/examples/parameterless/src/interfaces/org.hyperledger.chaincode.example.parameterless.cci index 7fa5a9c..503262f 100644 --- a/examples/parameterless/src/interfaces/org.hyperledger.chaincode.example.parameterless.cci +++ b/examples/parameterless/src/interfaces/org.hyperledger.chaincode.example.parameterless.cci @@ -3,6 +3,6 @@ message MyReturnType { string foo = 1; } -queries { +functions { MyReturnType TestParameterless() = 1; } diff --git a/examples/sample_syscc/interfaces/org.hyperledger.chaincode.system.sample.cci b/examples/sample_syscc/interfaces/org.hyperledger.chaincode.system.sample.cci index ad02021..dc0a39f 100644 --- a/examples/sample_syscc/interfaces/org.hyperledger.chaincode.system.sample.cci +++ b/examples/sample_syscc/interfaces/org.hyperledger.chaincode.system.sample.cci @@ -13,11 +13,8 @@ message BalanceResult { int32 balance = 1; } -transactions { +functions { void MakePayment(PaymentParams) = 1; void DeleteAccount(Entity) = 2; -} - -queries { - BalanceResult CheckBalance(Entity) = 1; + BalanceResult CheckBalance(Entity) = 3; } diff --git a/project.clj b/project.clj index 0669530..82e4ad8 100644 --- a/project.clj +++ b/project.clj @@ -1,6 +1,6 @@ -(defproject chaintool "0.10.0" - :description "hyperledger chaincode tool" - :url "https://github.com/ghaskins/chaintool" +(defproject chaintool "0.10.1" + :description "Hyperledger Fabric chaincode tool" + :url "https://github.com/hyperledger/fabric-chaintool" :license {:name "Apache License" :url "http://www.apache.org/licenses/LICENSE-2.0.txt"} :min-lein-version "2.0.0" diff --git a/resources/generators/golang.stg b/resources/generators/golang.stg index 8c5d770..4246fd1 100755 --- a/resources/generators/golang.stg +++ b/resources/generators/golang.stg @@ -21,6 +21,7 @@ import ( "github.com/hyperledger/fabric/core/system_chaincode/api" "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" "/ccs/api" ) @@ -31,39 +32,51 @@ type stubHandler struct { dispatchers Dispatchers } -var txnre = regexp.MustCompile("([a-zA-Z0-9.]*)/txn/([0-9]*)") -var queryre = regexp.MustCompile("([a-zA-Z0-9.]*)/query/([0-9]*)") +var functionspec = regexp.MustCompile("([a-zA-Z0-9.]*)/fcn/([0-9]*)") // Initialization function, called only once -func (self *stubHandler) Init(stub shim.ChaincodeStubInterface) ([]byte, error) { +func (self *stubHandler) Init(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() if len(args) != 1 { - return nil, errors.New("Expected exactly one argument") + return shim.Error("Expected exactly one argument") } if function != "init" { - return nil, errors.New("Function must be \"init\"") + return shim.Error("Function must be \"init\"") } dispatcher, ok := self.dispatchers["appinit"] if !ok { - return nil, errors.New("Interface not found") + return shim.Error("Interface not found") } - return dispatcher.DispatchTxn(stub, 1, args[0]) + return dispatcher.Dispatch(stub, 1, args[0]) } // Callback representing the invocation of a chaincode - +func (self *stubHandler) Invoke(stub shim.ChaincodeStubInterface) pb.Response { -// Callback representing the query of a chaincode - + function, args := stub.GetFunctionAndParameters() -func (self *stubHandler) decodeFunction(re *regexp.Regexp, function string) (api.Dispatcher, int, error) { + var params string; - spec := re.FindAllStringSubmatch(function, -1) + if len(args) > 0 { + params = args[0] + } + + dispatcher, index, err := self.decodeFunction(function) + if err != nil { + return shim.Error(err.Error()) + } + + return dispatcher.Dispatch(stub, index, params) +} + +func (self *stubHandler) decodeFunction(function string) (api.Dispatcher, int, error) { + + spec := functionspec.FindAllStringSubmatch(function, -1) if spec == nil { return nil, 0, errors.New("Could not parse function name") } @@ -73,7 +86,7 @@ func (self *stubHandler) decodeFunction(re *regexp.Regexp, function string) (api return nil, 0, errors.New("Interface not found") } - index, err := strconv.Atoi(spec[0][2]) + index, err := strconv.Atoi(spec[0][2]) if err != nil { return nil, 0, errors.New("Could not convert function index") } @@ -145,11 +158,11 @@ package api import ( "errors" "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" ) type Dispatcher interface { - DispatchTxn(stub shim.ChaincodeStubInterface, function int, params string) ([]byte, error) - DispatchQuery(stub shim.ChaincodeStubInterface, function int, params string) ([]byte, error) + Dispatch(stub shim.ChaincodeStubInterface, function int, params string) pb.Response } type Factory interface { @@ -258,14 +271,14 @@ package import ( "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" "/ccs/api" "encoding/base64" - "errors" + "fmt" ) type CCInterface interface { - - + } @@ -286,27 +299,17 @@ func (self *factoryImpl) Create(intf interface{}) (api.Dispatcher, error) { return &stubImpl{intf: intf.(CCInterface)}, nil } -func (self *stubImpl) DispatchTxn(stub shim.ChaincodeStubInterface, function int, params string) ([]byte, error) { +func (self *stubImpl) Dispatch(stub shim.ChaincodeStubInterface, function int, params string) pb.Response { // Handle different functions switch { - + default: - return nil, errors.New("Received unknown function invocation") - } -} - -func (self *stubImpl) DispatchQuery(stub shim.ChaincodeStubInterface, function int, params string) ([]byte, error) { - // Handle different functions - switch { - - default: - return nil, errors.New("Received unknown function invocation") + return shim.Error("Received unknown function invocation") } } /* Server stubs */ - - + - + >> -compositename(txn, intf, func) ::= "\"/txnquery/\"" +compositename(intf, func) ::= "\"/fcn/\"" implementservers(intf, functions) ::= " }; separator=\"\n\">" -implementclients(txn, intf, functions) ::= " }; separator=\"\n\">" -dispatchfunctions(txn, intf, functions) ::= " }; separator=\"\n\">" +implementclients(intf, functions) ::= " }; separator=\"\n\">" +dispatchfunctions(intf, functions) ::= " }; separator=\"\n\">" declarefunctions(intf, functions) ::= << (shim.ChaincodeStubInterface, *) (*, error)error }; separator="\n"> >> -dispatchfunction(txn, intf, func) ::= +dispatchfunction(intf, func) ::= << case function == : return self.proxy(stub, params) >> -implementhandler(txn) ::= -<< -func (self *stubHandler) InvokeQuery(stub shim.ChaincodeStubInterface) ([]byte, error) { - - function, args := stub.GetFunctionAndParameters() - - var params string; - - if len(args) > 0 { - params = args[0] - } - - dispatcher, index, err := self.decodeFunction(txnrequeryre, function) - if err != nil { - return nil, err - } - - return dispatcher.DispatchTxnQuery(stub, index, params) -} ->> - implementserver(intf, func) ::= << -func (self *stubImpl) proxy(stub shim.ChaincodeStubInterface, _params string) ([]byte, error) { +func (self *stubImpl) proxy(stub shim.ChaincodeStubInterface, _params string) pb.Response { var err error; @@ -395,67 +377,77 @@ func (self *stubImpl) proxy(stub shim.ChaincodeStubInterface, _params params := &{} _pbinput, err := base64.StdEncoding.DecodeString(_params) if (err != nil) { - return nil, err + return shim.Error(fmt.Sprintf("base64 decode error: %s", err)) } err = proto.Unmarshal(_pbinput, params) if (err != nil) { - return nil, err + return shim.Error(fmt.Sprintf("protobuf unmarshal error: %s", err)) } result, err := err = self.intf.(stub, params) if (err != nil) { - return nil, err + return shim.Error(err.Error()) } _pboutput, err := proto.Marshal(result) if (err != nil) { - return nil, err + return shim.Error(fmt.Sprintf("protobuf marshal error: %s", err)) } _result := base64.StdEncoding.EncodeToString(_pboutput) - return []byte(_result), nil + return shim.Success([]byte(_result)) - return nil, nil + return shim.Success(nil) } >> -implementclient(txn, intf, func) ::= +implementclient(intf, func) ::= << func (stub shim.ChaincodeStubInterface, chaincodeName string, params *) <\\> (*, error)error { - args := make([]string, 1) + + args := make([][]byte, 2) + + args := make([][]byte, 1) + var err error + args[0] = []byte() + _pboutput, err := proto.Marshal(params) if (err != nil) { return nil, err } - args[0] = base64.StdEncoding.EncodeToString(_pboutput) + args[1] = make([]byte, base64.StdEncoding.EncodedLen(len(_pboutput))) + base64.StdEncoding.Encode(args[1], _pboutput) - _result, err :=_, err = stub.InvokeQueryChaincode(chaincodeName, , args) - - - result := &{} - _pbinput, err := base64.StdEncoding.DecodeString(string(_result)) - if (err != nil) { - return nil, err - } - err = proto.Unmarshal(_pbinput, result) - if (err != nil) { - return nil, err + resp := stub.InvokeChaincode(chaincodeName, args) + if resp.Status \< shim.ERROR { + + result := &{} + _pbinput, err := base64.StdEncoding.DecodeString(string(resp.Payload)) + if (err != nil) { + return nil, fmt.Errorf("Error decoding base64 return value: %s", err) + } + err = proto.Unmarshal(_pbinput, result) + if (err != nil) { + return nil, fmt.Errorf("Error unmarshalling return value: %s", err) + } + return result, nil + + return nil + } - return result, nil - - return err - + + return nil, fmt.Errorf("RPC failure: %s", resp.Message) } >> diff --git a/resources/metadata/org.hyperledger.chaintool.meta.cci b/resources/metadata/org.hyperledger.chaintool.meta.cci index 319d34c..8d76ca0 100644 --- a/resources/metadata/org.hyperledger.chaintool.meta.cci +++ b/resources/metadata/org.hyperledger.chaintool.meta.cci @@ -28,7 +28,7 @@ message Facts { repeated Fact facts = 1; } -queries { +functions { Interfaces GetInterfaces(GetInterfacesParams) = 1; InterfaceDescriptor GetInterface(GetInterfaceParams) = 2; Facts GetFacts(GetFactsParams) = 3; diff --git a/resources/parsers/interface/grammar.bnf b/resources/parsers/interface/grammar.bnf index a3e6854..b6c6f01 100644 --- a/resources/parsers/interface/grammar.bnf +++ b/resources/parsers/interface/grammar.bnf @@ -1,5 +1,5 @@ -interface ::= ( message | enum )* transactions? queries? +interface ::= ( message | enum )* functions? message ::= <"message"> ident messageBody @@ -9,10 +9,9 @@ enumField ::= enumName <"="> enumValue <";"> ::= ident ::= intLit -transactions ::= <"transactions"> functions -queries ::= <"queries"> functions +functions ::= <"functions"> functionblock - ::= <"{"> function* <"}"> + ::= <"{"> function* <"}"> function ::= rettype functionName <"("> param? <")"> <"="> index <";"> functionName := ident rettype ::= ident diff --git a/src/chaintool/build/core.clj b/src/chaintool/build/core.clj index d063540..c464633 100644 --- a/src/chaintool/build/core.clj +++ b/src/chaintool/build/core.clj @@ -16,7 +16,15 @@ [chaintool.platforms.api :as platforms.api]) (:refer-clojure :exclude [compile])) -(defn compile [{:keys [config] :as params}] + +(defn- run [fcn {:keys [config] :as params}] (when-let [platform (platforms.core/find config)] - ;; generate platform output (shim, protobufs, etc) - (platforms.api/build platform params))) + (fcn platform params))) + +;; generate platform output (shim, protobufs, etc) +(defn compile [params] + (run platforms.api/build params)) + +;; display environment variables used for build +(defn env [params] + (run platforms.api/env params)) diff --git a/src/chaintool/build/interface.clj b/src/chaintool/build/interface.clj index 1d33952..967f5c7 100644 --- a/src/chaintool/build/interface.clj +++ b/src/chaintool/build/interface.clj @@ -119,9 +119,9 @@ (if-let [results (ast/find term ast)] (getfunctions results))) -(defn gettransactions [ast] (getgeneric ast :transactions)) -(defn getqueries [ast] (getgeneric ast :queries)) -(defn getallfunctions [ast] (into {} (vector (gettransactions ast) (getqueries ast)))) +(defn getallfunctions [ast] (->> (getgeneric ast :functions) + vector + (into {}))) (defn find-definition-in-local [name ast] (let [start (zip/leftmost ast)] @@ -368,8 +368,8 @@ (let [ast (interfaces "appinit")] (cond - ;; We do not allow any explicit transactions or queries in the project interface - (or (ast/find :transactions ast) (ast/find :queries ast)) + ;; We do not allow any explicit functions in the project interface + (ast/find :functions ast) (str "appinit.cci: illegal RPCs detected") ;; We cannot continue if the user didnt supply a message "Init" which will diff --git a/src/chaintool/core.clj b/src/chaintool/core.clj index 0d0e3ea..c4c4f73 100644 --- a/src/chaintool/core.clj +++ b/src/chaintool/core.clj @@ -21,6 +21,7 @@ [chaintool.subcommands.package :as packagecmd] [chaintool.subcommands.proto :as protocmd] [chaintool.subcommands.unpack :as unpackcmd] + [chaintool.subcommands.env :as envcmd] [chaintool.util :as util] [clojure.string :as string] [clojure.tools.cli :refer [parse-opts]] @@ -78,6 +79,10 @@ :validate (fn [options arguments] (= (count arguments) 1)) :options common-options} + {:name "env" :desc "Display variables used in the build environment" + :handler envcmd/run + :options common-path-options} + {:name "proto" :desc "Compiles a CCI file to a .proto" :handler protocmd/run :arguments "path/to/file.cci" diff --git a/src/chaintool/platforms/api.clj b/src/chaintool/platforms/api.clj index 47c9016..677252a 100644 --- a/src/chaintool/platforms/api.clj +++ b/src/chaintool/platforms/api.clj @@ -15,6 +15,8 @@ (ns chaintool.platforms.api) (defprotocol Platform + ;; Displays environment variables relevant to the build environment + (env [this params]) ;; Compiles the platform (build [this params]) ;; Cleans any previous builds of the platform diff --git a/src/chaintool/platforms/golang/core.clj b/src/chaintool/platforms/golang/core.clj index d762e53..276418f 100644 --- a/src/chaintool/platforms/golang/core.clj +++ b/src/chaintool/platforms/golang/core.clj @@ -34,7 +34,7 @@ ;; (deftype Function [^String rettype ^String name ^String param ^Integer index]) -(deftype Interface [^String name ^String package ^String packageCamel ^String packagepath ^ArrayList transactions ^ArrayList queries]) +(deftype Interface [^String name ^String package ^String packageCamel ^String packagepath ^ArrayList functions]) (deftype InterfaceDefinition [^String name ^String bytes]) (deftype Fact [^String name ^String value]) @@ -101,9 +101,8 @@ (build-function v)))) (defn- build-interface [base name interface] - (let [transactions (build-functions (:transactions interface)) - queries (build-functions (:queries interface))] - (vector name (->Interface name (package-name name) (package-camel name) (package-path base name) transactions queries)))) + (let [functions (build-functions (:functions interface))] + (vector name (->Interface name (package-name name) (package-camel name) (package-path base name) functions)))) (defn- build-interfaces [base interfaces] (into {} (map (fn [[name interface]] (build-interface base name interface)) interfaces))) @@ -251,7 +250,7 @@ (emit-server-stub base name functions opath)))) ;; and now special case the appinit interface - (emit-server-stub base "appinit" {:transactions {1 {:rettype "void", :functionName "Init", :param "Init", :index 1, :subType nil, :typeName nil}}} opath)) + (emit-server-stub base "appinit" {:functions {1 {:rettype "void", :functionName "Init", :param "Init", :index 1, :subType nil, :typeName nil}}} opath)) ;; generate our client stubs (dorun (for [name (intf/getconsumes config)] diff --git a/src/chaintool/platforms/golang/system.clj b/src/chaintool/platforms/golang/system.clj index deff5f8..5ad428a 100644 --- a/src/chaintool/platforms/golang/system.clj +++ b/src/chaintool/platforms/golang/system.clj @@ -41,6 +41,11 @@ (deftype GolangSystemPlatform [] platforms.api/Platform + ;;----------------------------------------------------------------- + ;; env - Emits the GOPATH used for building system chaincode + ;;----------------------------------------------------------------- + (env [_ _]) + ;;----------------------------------------------------------------- ;; build - generates all golang platform artifacts within the ;; default location in the build area diff --git a/src/chaintool/platforms/golang/userspace.clj b/src/chaintool/platforms/golang/userspace.clj index 6d0bdcb..9ca6e4f 100644 --- a/src/chaintool/platforms/golang/userspace.clj +++ b/src/chaintool/platforms/golang/userspace.clj @@ -30,6 +30,12 @@ (deftype GolangUserspacePlatform [] platforms.api/Platform + ;;----------------------------------------------------------------- + ;; env - Emits the GOPATH used for building golang chaincode + ;;----------------------------------------------------------------- + (env [_ {:keys [path]}] + (println (str "GOPATH=" (buildgopath path)))) + ;;----------------------------------------------------------------- ;; build - generates all golang platform artifacts within the ;; default location in the build area diff --git a/src/chaintool/protobuf/generate.clj b/src/chaintool/protobuf/generate.clj index 54efbb2..338903e 100644 --- a/src/chaintool/protobuf/generate.clj +++ b/src/chaintool/protobuf/generate.clj @@ -44,14 +44,11 @@ (conj defs loc) defs)))))) -(def function-class - {:transactions "txn" - :queries "query"}) - (defn- getallfunctions [ast] - (flatten (for [[type functions] (intf/getallfunctions ast)] - (for [[_ func] functions] - (assoc func :type (function-class type)))))) + (let [{:keys [functions]} (intf/getallfunctions ast)] + (-> functions + vals + flatten))) ;;----------------------------------------------------------------- ;; buildX - build our ST friendly objects from the AST @@ -120,8 +117,8 @@ (mapv vec) (into {}))) -(defn- buildfunction [name {:keys [rettype functionName param index type] :as ast}] - (let [key (str name "/" type "/" index)] +(defn- buildfunction [name {:keys [rettype functionName param index] :as ast}] + (let [key (str name "/fcn/" index)] (->Function key rettype functionName param))) (defn- buildfunctions [name ast] diff --git a/src/chaintool/subcommands/env.clj b/src/chaintool/subcommands/env.clj new file mode 100644 index 0000000..0bfefce --- /dev/null +++ b/src/chaintool/subcommands/env.clj @@ -0,0 +1,19 @@ +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +(ns chaintool.subcommands.env + (:require [chaintool.config.util :as config.util] + [chaintool.build.core :as build.core])) + +(defn run [options args] + (let [[path config] (config.util/load-from-options options)] + (build.core/env {:path path :config config}))) diff --git a/test/chaintool/build/test_interface.clj b/test/chaintool/build/test_interface.clj index ec944ac..d0ac037 100644 --- a/test/chaintool/build/test_interface.clj +++ b/test/chaintool/build/test_interface.clj @@ -150,7 +150,7 @@ (def example-no-parameters " - queries { + functions { string Parameterless() = 1; } diff --git a/test/chaintool/protobuf/test_generate.clj b/test/chaintool/protobuf/test_generate.clj index 2742913..9ac21e9 100644 --- a/test/chaintool/protobuf/test_generate.clj +++ b/test/chaintool/protobuf/test_generate.clj @@ -96,7 +96,7 @@ (def parameterless-function (zip/vector-zip - [:interface [:queries [:function [:rettype "string"] [:functionName "Parameterless"] [:index "1"]]]])) + [:interface [:functions [:function [:rettype "string"] [:functionName "Parameterless"] [:index "1"]]]])) (deftest parameterless-test (let [result (round-about parameterless-function)]