├── .github └── settings.yml ├── .gitignore ├── CHANGELOG.md ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── LICENSE ├── MAINTAINERS.txt ├── Makefile ├── README.md ├── ci └── azure-pipelines.yml ├── dev └── user.clj ├── docs ├── application-development.md ├── client-development.md ├── command-reference.md ├── getting-started.md ├── images │ ├── readme-drawings.graffle │ └── typical-workflow.png ├── index.md ├── interface.md ├── metadata.md └── platforms │ ├── golang.md │ └── system.md ├── examples ├── example02 │ ├── README.md │ ├── app │ │ ├── chaincode.yaml │ │ └── src │ │ │ ├── chaincode │ │ │ └── chaincode_example02.go │ │ │ └── interfaces │ │ │ ├── appinit.cci │ │ │ └── org.hyperledger.chaincode.example02.cci │ └── client │ │ ├── cljs │ │ ├── Makefile │ │ ├── project.clj │ │ ├── protos │ │ │ ├── appinit.proto │ │ │ └── org.hyperledger.chaincode.example02.proto │ │ ├── sample.config │ │ └── src │ │ │ ├── example02 │ │ │ ├── api.cljs │ │ │ ├── connection.cljs │ │ │ ├── main.cljs │ │ │ ├── protobuf.cljs │ │ │ └── rpc.cljs │ │ │ └── fabric_sdk │ │ │ ├── channel.cljs │ │ │ ├── core.cljs │ │ │ ├── eventhub.cljs │ │ │ ├── macros.clj │ │ │ └── user.cljs │ │ └── nodejs │ │ ├── client.config │ │ ├── client.js │ │ ├── lib │ │ └── util.js │ │ ├── package.json │ │ └── protos │ │ ├── appinit.proto │ │ └── org.hyperledger.chaincode.example02.proto ├── helloworld │ ├── chaincode.yaml │ └── src │ │ ├── chaincode │ │ └── main.go │ │ ├── interfaces │ │ ├── appinit.cci │ │ └── org.hyperledger.chaincode.helloworld.cci │ │ └── org │ │ └── hyperledger │ │ └── chaincode │ │ └── helloworld │ │ └── core.go ├── invoker │ ├── chaincode.yaml │ └── src │ │ ├── chaincode │ │ └── chaincode.go │ │ └── interfaces │ │ ├── appinit.cci │ │ └── org.hyperledger.chaincode.example02.cci ├── parameterless │ ├── chaincode.yaml │ └── src │ │ ├── chaincode │ │ └── main.go │ │ └── interfaces │ │ ├── appinit.cci │ │ └── org.hyperledger.chaincode.example.parameterless.cci └── sample_syscc │ ├── chaincode.yaml │ ├── interfaces │ ├── appinit.cci │ └── org.hyperledger.chaincode.system.sample.cci │ └── sample_syscc.go ├── lein ├── mkdocs.yml ├── project.clj ├── resources ├── generators │ ├── golang.stg │ └── proto.stg ├── metadata │ └── org.hyperledger.chaintool.meta.cci ├── parsers │ ├── interface │ │ ├── grammar.bnf │ │ └── skip.bnf │ └── proto │ │ ├── grammar.bnf │ │ └── skip.bnf └── proto │ └── car.proto ├── scripts └── changelog.sh ├── src ├── chaintool │ ├── ast.clj │ ├── build │ │ ├── core.clj │ │ └── interface.clj │ ├── car │ │ ├── abi │ │ │ └── Car.java │ │ ├── ls.clj │ │ ├── read.clj │ │ ├── types.clj │ │ ├── unpack.clj │ │ └── write.clj │ ├── codecs.clj │ ├── config │ │ ├── parser.clj │ │ └── util.clj │ ├── core.clj │ ├── inspect │ │ └── core.clj │ ├── platforms │ │ ├── api.clj │ │ ├── core.clj │ │ └── golang │ │ │ ├── core.clj │ │ │ ├── system.clj │ │ │ └── userspace.clj │ ├── protobuf │ │ └── generate.clj │ ├── subcommands │ │ ├── build.clj │ │ ├── buildcar.clj │ │ ├── clean.clj │ │ ├── deps.clj │ │ ├── env.clj │ │ ├── inspect.clj │ │ ├── ls.clj │ │ ├── package.clj │ │ ├── proto.clj │ │ └── unpack.clj │ └── util.clj └── org │ └── hyperledger │ └── chaintool │ └── meta │ └── OrgHyperledgerChaintoolMeta.java └── test └── chaintool ├── build └── test_interface.clj ├── platforms └── golang │ ├── test_core.clj │ └── test_system.clj └── protobuf └── test_generate.clj /.github/settings.yml: -------------------------------------------------------------------------------- 1 | repository: 2 | name: fabric-chaintool 3 | description: null 4 | homepage: https://wiki.hyperledger.org/display/fabric 5 | default_branch: master 6 | has_downloads: true 7 | has_issues: false 8 | has_projects: false 9 | has_wiki: false 10 | archived: true 11 | private: false 12 | allow_squash_merge: true 13 | allow_merge_commit: false 14 | allow_rebase_merge: true 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | *.jar 7 | *.class 8 | .lein-deps-sum 9 | .lein-failures 10 | .lein-plugins 11 | .idea 12 | *.iml 13 | **/node_modules 14 | **/out 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.10.3 February 22, 2017 2 | 3 | * [c7caae4](https://github.com/hyperledger/fabric/commit/c7caae4) Release v0.10.3 4 | * [5fde1e5](https://github.com/hyperledger/fabric/commit/5fde1e5) Link golang chaincode with -static 5 | * [cf55d6c](https://github.com/hyperledger/fabric/commit/cf55d6c) Prepare for v0.10.3 development 6 | 7 | ## v0.10.2 February 14, 2017 8 | 9 | * [b579a6a](https://github.com/hyperledger/fabric/commit/b579a6a) Release v0.10.2 10 | * [708af63](https://github.com/hyperledger/fabric/commit/708af63) Add basic support for eventhub 11 | * [62f24c2](https://github.com/hyperledger/fabric/commit/62f24c2) Remove base64 from protocol 12 | * [d2ceb4e](https://github.com/hyperledger/fabric/commit/d2ceb4e) Prepare for v0.10.2 development 13 | 14 | ## v0.10.1 February 7, 2017 15 | 16 | * [e40a745](https://github.com/hyperledger/fabric/commit/e40a745) Import release v0.10.1 17 | 18 | ## v0.10.0 February 7, 2017 19 | 20 | * [9a8948b](https://github.com/hyperledger/fabric/commit/9a8948b) Import release v0.10.0 21 | 22 | ## v0.9.2 February 7, 2017 23 | 24 | * [6d0f535](https://github.com/hyperledger/fabric/commit/6d0f535) Import release v0.9.2 25 | 26 | ## v0.9.1 February 7, 2017 27 | 28 | * [3c23654](https://github.com/hyperledger/fabric/commit/3c23654) Import release v0.9.1 29 | 30 | ## v0.9.0 February 7, 2017 31 | 32 | * [aecaced](https://github.com/hyperledger/fabric/commit/aecaced) Import release v0.9.0 33 | 34 | ## v0.8.1 February 7, 2017 35 | 36 | * [f92acb3](https://github.com/hyperledger/fabric/commit/f92acb3) Import release v0.8.1 37 | 38 | ## v0.8.0 February 7, 2017 39 | 40 | * [20eadfd](https://github.com/hyperledger/fabric/commit/20eadfd) Import release v0.8.0 41 | 42 | # Change Log 43 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 44 | 45 | ## [Unreleased][unreleased] 46 | ### Changed 47 | - Add a new arity to `make-widget-async` to provide a different widget shape. 48 | 49 | ## [0.1.1] - 2016-01-20 50 | ### Changed 51 | - Documentation on how to make the widgets. 52 | 53 | ### Removed 54 | - `make-widget-sync` - we're all async, all the time. 55 | 56 | ### Fixed 57 | - Fixed widget maker to keep working when daylight savings switches over. 58 | 59 | ## 0.1.0 - 2016-01-20 60 | ### Added 61 | - Files from the new template. 62 | - Widget maker public API - `make-widget-sync`. 63 | 64 | [unreleased]: https://github.com/your-name/obcc/compare/0.1.1...HEAD 65 | [0.1.1]: https://github.com/your-name/obcc/compare/0.1.0...0.1.1 66 | 67 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 68 | s 69 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | # Fabric Maintainers 4 | * @hyperledger/fabric-maintainers 5 | 6 | # Fabric Chaintool Maintainers 7 | * @hyperledger/fabric-chaintool-maintainers 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Code of Conduct Guidelines 2 | ========================== 3 | 4 | Please review the Hyperledger [Code of 5 | Conduct](https://wiki.hyperledger.org/community/hyperledger-project-code-of-conduct) 6 | before participating. It is important that we keep things civil. 7 | 8 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 9 | -------------------------------------------------------------------------------- /MAINTAINERS.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: CC-BY-4.0 5 | # 6 | 7 | Greg Haskins 8 | Eric Baur 9 | Muralidharan Srinivasan 10 | 11 | Also: 12 | https://github.com/hyperledger/fabric/blob/master/docs/source/MAINTAINERS.rst 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | NAME=chaintool 8 | LEIN = $(shell which lein || echo ./lein) 9 | BINDIR ?= /usr/local/bin 10 | OUTPUT=target/$(NAME) 11 | META=org.hyperledger.chaintool.meta 12 | 13 | SRCS += $(shell find src -type f) 14 | SRCS += $(shell find resources -type f) 15 | 16 | PROTOS += $(shell find resources/proto -name "*.proto") 17 | PROTOS += target/$(META).proto 18 | 19 | all: $(OUTPUT) 20 | 21 | $(OUTPUT): $(SRCS) Makefile project.clj 22 | @$(LEIN) bin 23 | 24 | $(PREFIX)$(BINDIR): 25 | mkdir -p $@ 26 | 27 | # Bootstrap! We use chaintool to build a .proto for chaintool. 28 | # If HEAD will not build, an older release of chaintool may be 29 | # used to generate this file by hand 30 | target/$(META).proto: resources/metadata/$(META).cci $(OUTPUT) 31 | $(OUTPUT) proto -o $@ $< 32 | 33 | proto: $(PROTOS) 34 | protoc --java_out=./src $(PROTOS) 35 | 36 | install: $(OUTPUT) $(PREFIX)$(BINDIR) 37 | cp $(OUTPUT) $(PREFIX)$(BINDIR) 38 | 39 | clean: 40 | @echo "Cleaning up.." 41 | @$(LEIN) clean 42 | -@rm -rf target 43 | -@rm -f *~ 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Note:** Issue tracking is handled in [Jira](https://jira.hyperledger.org) 2 | 3 | ## Documentation, Getting Started and Developer Guides 4 | 5 | Please visit our [online documentation](http://fabric-chaintool.readthedocs.io/en/latest/) for information on getting started using and developing with chaintool. 6 | 7 | ## Contributing 8 | 9 | We welcome contributions to the Hyperledger Project in many forms. There’s always plenty to do! 10 | Check [the documentation on how to contribute to this project](http://hyperledger-fabric.readthedocs.io/en/latest/CONTRIBUTING.html) for the full details. 11 | 12 | ## Community 13 | 14 | [Hyperledger Community](https://www.hyperledger.org/community) 15 | 16 | [Hyperledger mailing lists and archives](http://lists.hyperledger.org/) 17 | 18 | [Hyperledger Chat](http://chat.hyperledger.org/channel/fabric-chaintool) 19 | 20 | [Hyperledger Wiki](https://wiki.hyperledger.org/) 21 | 22 | [Hyperledger Code of Conduct](https://wiki.hyperledger.org/community/hyperledger-project-code-of-conduct) 23 | 24 | [Community Calendar](https://wiki.hyperledger.org/community/calendar-public-meetings) 25 | 26 | ## License 27 | The Hyperledger Project uses the [Apache License Version 2.0](LICENSE) software license. 28 | 29 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 30 | -------------------------------------------------------------------------------- /ci/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | 4 | pool: 5 | vmImage: 'ubuntu-latest' 6 | 7 | steps: 8 | 9 | - script: | 10 | make all 11 | displayName: 'Make everything' -------------------------------------------------------------------------------- /dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require [clojure.tools.namespace.repl :refer [refresh]])) 3 | 4 | ;; user is a namespace that the Clojure runtime looks for and loads if 5 | ;; its available 6 | 7 | ;; You can place helper functions in here. This is great for starting 8 | ;; and stopping your webserver and other development services 9 | 10 | ;; The definitions in here will be available if you run "lein repl" or launch a 11 | ;; Clojure repl some other way 12 | 13 | ;; You have to ensure that the libraries you :require are listed in the :dependencies 14 | ;; in the project.clj 15 | 16 | ;; Once you start down this path 17 | ;; you will probably want to look at 18 | ;; tools.namespace https://github.com/clojure/tools.namespace 19 | ;; and Component https://github.com/stuartsierra/component 20 | 21 | ;; or the exciting newcomer https://github.com/weavejester/integrant 22 | 23 | ;; DEVELOPMENT SERVER HELPERS: starting and stopping a development server in the REPL 24 | -------------------------------------------------------------------------------- /docs/application-development.md: -------------------------------------------------------------------------------- 1 | # Chaincode Application Development 2 | 3 | ## Project Structure 4 | 5 | Like many modern build tools, _chaintool_ is opinionated. It expects a specific structure to your project as follows: 6 | 7 | - [chaincode.yaml](#chaincodeyaml) in the top-level directory of your project 8 | - a chaincode entry-point in ./src/chaincode ([example](https://github.com/hyperledger/fabric-chaintool/tree/master/examples/example02/app/src/chaincode)) 9 | - interface files in ./src/interfaces ([example](https://github.com/hyperledger/fabric-chaintool/tree/master/examples/example02/app/src/interfaces)) 10 | - every project must define an appinit interface ./src/interfaces/appinit.cci ([example](interface.md#appinit-interface)) 11 | 12 | ### chaincode.yaml 13 | 14 | _chaincode.yaml_ is the central configuration file for a given chaintool-managed chaincode project. An example looks like this: 15 | 16 | ``` 17 | # ---------------------------------- 18 | # chaincode example02 19 | # ---------------------------------- 20 | # 21 | # Copyright (C) 2016 - Hyperledger 22 | # All rights reserved 23 | # 24 | 25 | Schema: 1 26 | Name: org.hyperledger.chaincode.example02 27 | Version: 0.1-SNAPSHOT 28 | 29 | Platform: 30 | Name: org.hyperledger.chaincode.golang 31 | Version: 1 32 | 33 | Provides: [self] # 'self' is a keyword that means there should be $name.cci (e.g. org.hyperledger.chaincode.example02.cci) 34 | ``` 35 | 36 | All chaincode.yaml should minimally contain: 37 | 38 | - schema 39 | - project name/version 40 | - platform 41 | - interface declarations (provides/consumes) 42 | 43 | #### Schema 44 | This helps to relay compatibility with the structures used in the chaincode.yaml itself. At the time of writing, it should be "1". 45 | 46 | #### Project name/version 47 | 48 | This is something that should uniquely identify your chaincode project for human/UX consumption. It is generally advised that a DNS name of some kind be incorporated to promote global uniqueness. Note that the Hyperledger subsystem in general does not interpret these names in any meaningful way other than for display purposes. 49 | 50 | #### Platform 51 | 52 | It is here that a chaincode may declare the compatibility/conformity to a specific platform. The idea is to promote extensibility (e.g. other platforms may be added in the future) and also compatility (e.g. platform X, version Y may mean something very specifically about the type of chaincode language supported, the ABI for any peripheral libraries, etc). It is analogous to the notion that java 1.7 is a different ABI than java 1.8, etc. At the time of writing, the only supported platform are: 53 | 54 | - [org.hyperledger.chaincode.golang](./platforms/golang.md) - The canonical platform for chaincode development, expressed as a chaintool managed superset. 55 | - [org.hyperledger.chaincode.system](./platforms/system.md) - A special variant of [org.hyperledger.chaincode.golang](./platforms/golang.md) designed for system-chaincode development as an extension of the hyperledger fabric. This platform is implicitly golang based and notably not compiled as system chaincode is compiled as part of the fabric build. 56 | 57 | More platforms may be added in the future. 58 | 59 | ##### Adding platforms 60 | 61 | The only core requirement is that both _chaintool_ and the chosen Hyperledger network are in agreement to support said platform. The details of implementing this are "coming soon". 62 | 63 | #### Interface Declarations 64 | 65 | 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). 66 | 67 | Both Provides and Consumes are expressed as an array of 1 or more entries. For example: 68 | 69 | ``` 70 | Provides: [org.hyperledger.chaincode.example02, org.hyperledger.chaincode.example03] 71 | Consumes: [org.hyperledger.chaincode.example02] 72 | ``` 73 | 74 | If there aren't any interfaces in a particular category, the entry may be omitted. Note that a chaincode that doesn't provide any interfaces doesn't sound particularly useful, however. Therefore, it is expected that every project will include at least a Provides clause. 75 | 76 | ##### "self" 77 | 78 | The keyword _self_ may be used as shorthand for an interface that shares the same name as the project (for instance, the org.hyperledger.chaincode.example02 project surfacing the org.hyperledger.chaincode.example02.cci interface), as a convenience. It is idiomatic for a project to name its primary interfaces after itself, and therefore this shortcut is expected to be commonly used. Example: 79 | 80 | ``` 81 | Provides: [self] 82 | ``` 83 | 84 | ### Chaincode 85 | 86 | The opinionated portion of chaincode path solely applies to the entry-point for your application. Other paths for non-entry point code are generally fine if you are using a language that supports namespaces, etc. For instance, the org.hyperledger.chaincode.golang platform assumes a $GOPATH of ./src and tries to build "chaincode" (via $GOPATH/src/chaincode). However, if your chaincode uses go imports such as: 87 | 88 | ```golang 89 | import ( 90 | "foo" 91 | "bar/baz" 92 | ) 93 | ``` 94 | 95 | placed in ./src/foo and ./src/bar/baz respectively, they will be discovered perfectly fine. 96 | 97 | 98 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 99 | s 100 | -------------------------------------------------------------------------------- /docs/client-development.md: -------------------------------------------------------------------------------- 1 | # Interacting With Chaintool Managed Applications 2 | 3 | ## Tooling 4 | 5 | _chaintool_ uses a protobuf-based protocol tunneled over the standard Fabric chaincode protocol. You may find that [chaintool proto](command-reference.md#chaintool-proto) and [chaintool inspect](command-reference.md#chaintool-inspect) commands are helpful to translate from [CCI](interface.md) to .proto files for your client's consumption. 6 | 7 | ## Protocol 8 | 9 | ### Input Protocol 10 | 11 | The standard Fabric protocol consists of an array of byte "args". This protobuf schema for the standard chaincode protocol is: 12 | ``` 13 | message ChaincodeInput { 14 | 15 | repeated byte args = 1; 16 | 17 | } 18 | ``` 19 | Chaintool deterministically maps functions declared within a CCI to an [encoded function name](#function-encoding), and expects the corresponding input parameter to be a serialized protobuf message as the first and only arg string. 20 | 21 | Example: 22 | ``` 23 | ["org.hyperledger.chaincode.example02/fcn/3","CgNmb28="] 24 | ``` 25 | 26 | #### Function Encoding 27 | 28 | Function naming follows the convention *interface-name/fcn/method-index*. For instance, invoking *MakePayment* from our [example](https://github.com/hyperledger/fabric-chaintool/tree/master/examples/example02) would be *org.hyperledger.chaintool.example02/fcn/1*. Because its function #1 in the org.hyperledger.chaintool.example02 interface. 29 | 30 | ### Output Protocol 31 | 32 | Standard chaincode protocol allows chaincode applications to return a byte-array payload to a caller. Chaintool managed applications will encode this payload with a serialized protobuf structure (when applicable). 33 | 34 | ### Protobuf "hints" 35 | 36 | The .proto file generated from *chaintool* (such as *chaintool proto*) contains hints to help developers understand the protocol. For instance, see the comments at the bottom of this .proto generated from example02: 37 | 38 | ``` 39 | // 40 | // Generated by chaintool. DO NOT EDIT!! 41 | // 42 | 43 | syntax = "proto3"; 44 | 45 | package org.hyperledger.chaincode.example02; 46 | 47 | message BalanceResult { 48 | int32 balance = 1; 49 | } 50 | 51 | message Entity { 52 | string id = 1; 53 | } 54 | 55 | message PaymentParams { 56 | string partySrc = 1; 57 | string partyDst = 2; 58 | int32 amount = 3; 59 | } 60 | 61 | // 62 | // Available RPC functions exported by this interface 63 | // 64 | // void MakePayment(PaymentParams) -> org.hyperledger.chaincode.example02/fcn/1 65 | // void DeleteAccount(Entity) -> org.hyperledger.chaincode.example02/fcn/2 66 | // BalanceResult CheckBalance(Entity) -> org.hyperledger.chaincode.example02/fcn/3 67 | ``` 68 | 69 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 70 | s 71 | -------------------------------------------------------------------------------- /docs/command-reference.md: -------------------------------------------------------------------------------- 1 | # Command Reference 2 | 3 | #### chaintool build 4 | 5 | Builds your chaincode project into a executable. When used locally, _chaintool build_ allows a developer to verify that their project compiles without errors or warnings before deployment. Validating peers also each use _chaintool build_ to prepare a chaincode archive for execution on the actual blockchain. Developers achieve fidelity in chaincode development workflows because they have access to the same build environment that will eventually be used when their application is deployed. 6 | 7 | Various artifacts are emitted to ./build, depending on the platform. For [org.hyperledger.chaincode.golang](./platforms/golang.md): 8 | 9 | - ./build/src: stub, protobufs, etc 10 | - ./build/deps: direct and transitive dependencies of your chaincode, as retrieved by "go get". NOTE: this option is likely to default to disabled in the future, since it is not a good idea for a validating peer to be pulling dependencies down. Rather, there should be some fixed number of dependencies that are implicitly included with the platform. For now, we pull things in dynamically. 11 | - ./build/bin: the default location for the binary generated (override with -o) 12 | 13 | #### chaintool clean 14 | 15 | Cleans a chaincode project. This typically translates to removing the ./build directory, but platforms are free to define this as they see fit and may perform additional or alternative operations. 16 | 17 | #### chaintool package 18 | 19 | Packages the sourcecode, interfaces, chaincode.yaml, and other project data into a .car file suitable for deployment. Note that any artifacts generated by commands such as _build_ and _buildcar_ are _not_ included but rather will be rebuilt locally by each validating peer in the network. 20 | 21 | #### chaintool ls 22 | 23 | Displays the contents of an existing .car file. 24 | ``` 25 | $ chaintool ls ./build/org.hyperledger.chaincode.example02-0.1-SNAPSHOT.car 26 | |------+------------------------------------------+--------------------------------------------------------| 27 | | Size | SHA1 | Path | 28 | |------+------------------------------------------+--------------------------------------------------------| 29 | | 438 | d28b22c7c30506af926dcb5bc8b946ac35ddac7f | chaincode.yaml | 30 | | 3856 | 542d088197e1a46bc21326e67e5d84d2d2807283 | src/chaincode/chaincode_example02.go | 31 | | 143 | 7305f65e18e4aab860b201d40916bb7adf97544f | src/interfaces/appinit.cci | 32 | | 375 | 9492a1e96f380a97bba1f16f085fc70140154c65 | src/interfaces/org.hyperledger.chaincode.example02.cci | 33 | |------+------------------------------------------+--------------------------------------------------------| 34 | Platform: org.hyperledger.chaincode.golang version 1 35 | Digital Signature: none 36 | Raw Data Size: 4812 bytes 37 | Archive Size: 2371 bytes 38 | Compression Alg: gzip 39 | Chaincode SHA3: f7026e0675b22a9d78b9f7f0cb97c93165bdefedc86de97f00e76b506c707b4ddbdfe97ad702ad600eae518891b9f0f1c8cb9a8b29b83908c2f6d46a6bcf4ecd 40 | ``` 41 | 42 | #### chaintool unpack 43 | 44 | Unpacks a .car archive into the filesystem as a chaincode project. 45 | 46 | #### chaintool buildcar 47 | 48 | Combines _unpack_ with _build_ by utilizing a temporary directory. This allows a project to be built from a .car file without explicitly unpacking it first, as a convenience. 49 | 50 | #### chaintool proto 51 | 52 | Compiles a .cci file into a .proto file, suitable for developing clients using standard protobuf-based tooling. 53 | 54 | #### chaintool inspect 55 | 56 | Retrieves [metadata](./metadata.md) from a running instance, optionally saving the interface definitions to a local directory. 57 | 58 | ``` 59 | $ chaintool inspect -n mycc 60 | Connecting to http://localhost:3000/chaincode 61 | |---------------------+--------------------------------------------| 62 | | Fact | Value | 63 | |---------------------+--------------------------------------------| 64 | | Application Name | org.hyperledger.chaincode.example02 | 65 | | Application Version | 0.1-SNAPSHOT | 66 | | Platform | org.hyperledger.chaincode.golang version 1 | 67 | | Chaintool Version | 0.7 | 68 | |---------------------+--------------------------------------------| 69 | Exported Interfaces: 70 | - appinit 71 | - org.hyperledger.chaincode.example02 72 | 73 | ``` 74 | 75 | 76 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 77 | s 78 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Installation 4 | 5 | ### Prerequisites 6 | - [Java](https://www.java.com) JRE/JDK v1.8 (or higher) 7 | - [Golang](https://golang.org) v1.7 or higher 8 | - [Protobuf Compiler](https://developers.google.com/protocol-buffers/docs/downloads) v3.0 or higher 9 | - [protoc-gen-go](https://github.com/golang/protobuf/tree/master/protoc-gen-go) 10 | 11 | ### Install 12 | Download the latest [release](https://github.com/hyperledger/fabric-chaintool/releases) and install it in your $PATH 13 | 14 | #### Example 15 | ``` 16 | $ sudo curl https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric/hyperledger-fabric/chaintool-1.0.0/hyperledger-fabric-chaintool-1.0.0.jar -Lo /usr/local/bin/chaintool && sudo chmod +x /usr/local/bin/chaintool 17 | ``` 18 | 19 | ### Usage 20 | Chaintool supports a number of "actions" (e.g. "chaintool build"). See _chaintool -h_ for details. 21 | 22 | ``` 23 | $ chaintool -h 24 | chaintool version: v1.0.0 25 | 26 | Usage: chaintool [general-options] action [action-options] 27 | 28 | General Options: 29 | -v, --version Print the version and exit 30 | -h, --help 31 | 32 | Actions: 33 | build -> Build the chaincode project 34 | buildcar -> Build the chaincode project from a CAR file 35 | clean -> Clean the chaincode project 36 | package -> Package the chaincode into a CAR file for deployment 37 | unpack -> Unpackage a CAR file 38 | ls -> List the contents of a CAR file 39 | proto -> Compiles a CCI file to a .proto 40 | inspect -> Retrieves metadata from a running instance 41 | 42 | (run "chaintool -h" for action specific help) 43 | ``` 44 | 45 | ## Development Overview 46 | Every chaintool based chaincode application minimally consists of the chaincode itself, and one or more clients to communicate with it. For our walk-through, we will be using the standard [golang-based](platforms/golang.md#golang-chaincode-platform) chaincode model, version 1, and the [Node SDK](https://www.npmjs.com/package/fabric-client). 47 | 48 | It is assumed that the reader is familiar with Hyperledger Fabric in general, and [how to develop](http://hyperledger-fabric.readthedocs.io/en/latest/chaincode.html) "standard" (i.e. non-chaintool based) chaincode and clients. 49 | 50 | ### Phase 1: Chaincode Development 51 | We will begin by defining our chaincode application, since this will dictate the operations that a client needs to support. 52 | 53 | - Create a directory to hold your chaincode application logic. 54 | - We will refer to this directory as $CHAINCODE in the remainder of this document. 55 | - Optional: Set up an SCM such as git. 56 | - Define your [interfaces](interface.md) under $CHAINCODE/src/interfaces 57 | - [$CHAINCODE/src/interfaces/appinit.cci](interface.md#appinit-interface): This defines the "constructor" arguments for your application. 58 | - Create one or more application specific interfaces: These define the general methods and arguments for your application. 59 | - E.g. $CHAINCODE/src/interfaces/org.acme.myapplication.cci 60 | - Tip: Use reverse DNS naming to ensure your interface is globally unique. 61 | - Define your [$CHAINCODE/chaincode.yaml](application-development.md#chaincodeyaml) project definition. 62 | - Set [schema v1](application-development.md#schema) 63 | - Set your project [name and version](application-development.md#project-nameversion) 64 | - Use platform specifier [org.hyperledger.chaincode.golang v1](platforms/golang.md#platform-specifier) 65 | - Be sure to update the [interface declarations](application-development#interface-declarations) (i.e. _Provides_ and _Consumes_) with the CCI files added in the previous step. 66 | - Define your [chaincode entrypoint](platforms/golang.md#entry-point) under $CHAINCODE/src/chaincode. 67 | - E.g. $CHAINCODE/src/chaincode/main.go 68 | - [Import](platforms/golang.md#imports) the chaintool generated code and fabric shim. 69 | - [Register](platforms/golang.md#hooks-and-registration) your chaincode inside your main() function. 70 | - Write your application logic by [implementing](platforms/golang.md#callbacks) your interfaces. 71 | - Init() for appinit.cci 72 | - Any other functions declared within interfaces specified as _Provided_ in your chaincode.yaml 73 | 74 | Note: You may find a complete chaincode example [here](https://github.com/hyperledger/fabric-chaintool/tree/master/examples/example02/app) 75 | 76 | You man run _chaintool build_ at this time to locally verify compilation of your application. However, before you may deploy it to a Fabric network, you will need to develop a client for your application using one of the Fabric SDKs. Proceed to Phase 2. 77 | 78 | ### Phase 2: Client Development 79 | Chaintool-based chaincodes employ a specific [parameter encoding](client-development.md#protocol) based on [Google Protocol Buffers (protobufs)](https://developers.google.com/protocol-buffers/). Because of this encoding, chaintool-based chaincodes are not generally compatible with the _peer CLI_ methods. Rather, we must develop a client in code that is capable of performing the encoding/decoding for us. 80 | 81 | Fortunately Fabric provides a variety of SDKs on platforms that also enjoy robust protobuf support. Therefore, one only needs to build a standard client on the platform of the reader's choosing, with the additional understanding of the chaintool imposed parameter encoding scheme. 82 | 83 | We will be building a client using the [Node SDK](https://www.npmjs.com/package/fabric-client). It is beyond the scope of this document to cover basic Node SDK client development. Instead, we will focus solely on the elements that are specific to chaintool. 84 | 85 | #### Encoding Details 86 | ##### Input Parameter Encoding 87 | Consider the following CCI snippet taken from _org.hyperledger.chaincode.example02.cci_: 88 | ``` 89 | message PaymentParams { 90 | string partySrc = 1; 91 | string partyDst = 2; 92 | int32 amount = 3; 93 | } 94 | 95 | ... 96 | 97 | functions { 98 | void MakePayment(PaymentParams) = 1; 99 | ... 100 | } 101 | ``` 102 | 103 | A client wishing to invoke MakePayment() would encode the request into an input array via the SDK as follows: 104 | 105 | ``` 106 | ["org.hyperledger.chaincode.example02/fcn/1", "CgNmb28="] 107 | ``` 108 | 109 | Where "org.hyperledger.chaincode.example02" is the name of the interface we are invoking, "fcn" is a constant, "1" is the index of the "MakePayment" method, and _ "CgNmb28="_ is a protobuf encoding of _PaymentParams_. 110 | 111 | ##### Output Parameter Encoding 112 | For methods that return non-void types, the output will be a protobuf encoded byte array. 113 | 114 | #### Integrating with the Node SDK 115 | 116 | As mentioned above, a client for a chaintool-based application is virtually identical to a non-chaintool application in all aspects except for the parameter encoding. In order to add support for the requisite encoding, we need three basic things: 117 | 118 | - One or more .proto files representing the protobuf schema we want to use as the basis of encode/decode. 119 | - TIP: _chaintool proto_ can convert a CCI file to a pure .proto file for convenient client consumption. 120 | - A NodeJS compatible protobuf library that can work with our .proto schemas. 121 | - [protobuf.js](https://www.npmjs.com/package/protobufjs) is an excellent choice. It can work using just your .proto files and reflection, eliminating a discrete protoc compilation phase found in many other platforms. 122 | - The integration of the two items above together to encode/decode our input and output parameters properly. 123 | 124 | For the Node SDK, this can be as simple as defining a request object such as: 125 | ``` 126 | var args = new app.PaymentParams({'partySrc':'A', 'partyDst':'B', 'amount':100}); 127 | var request = { 128 | chaincodeType: 'car', 129 | fcn: 'org.hyperledger.chaincode.example02/fcn/1', 130 | args: [args.toBuffer()] 131 | }; 132 | ``` 133 | 134 | A complete example client can be found [here](https://github.com/hyperledger/fabric-chaintool/tree/master/examples/example02/client/nodejs) 135 | 136 | ### Phase 3: Deployment 137 | Generally speaking, deployment of chaincode in Fabric involves two discrete steps: _Install_ and _Instantiate_. The Install phase is where a chaincode application is provided to the network. The Instantiate phase is where a previously installed application is initialized and enters an active state in the network. 138 | 139 | Chaintool-based chaincode is not materially different in this overall flow. What is different is the packaging and encodings that are used. 140 | 141 | #### Packaging 142 | Chaintool provides a package format called CAR (Chaincode Archive). This format was designed from the ground up to be a deterministic and platform agnostic way to package up chaincodes for Fabric. 143 | 144 | - Run _chaintool package_ to create a Fabric deployment package from your project. 145 | - Use _chaintool ls_ or _chaintool unpack_ to work with CAR files previously generated. 146 | 147 | #### Installation 148 | Both the _peer CLI_ and _Node SDK_ have native support for installing CAR packages. Since there are no parameter encodings that accompany _install_, you may chose the workflow that best suits you. 149 | 150 | #### Instantiation 151 | Instantiating a previously installed CAR file involves encoding the Init() parameters (as designed in your application's appinit.cci). Therefore, it is most convenient to use the SDK's _chain.sendInstantiateProposal()_ for this operation. Simply encode the appinit.cc::Init{} message in the request.fcn/request.args as previously noted. 152 | 153 | ### Phase 4: Interacting with your service 154 | At this phase, your chaincode application is up and running. You may execute Invoke() and Query() operations against it just like any other chaincode, as long as you adhere to the encoding schemas. 155 | 156 | Congratulations! 157 | 158 | ### Questions/Comments? 159 | Join us on the #fabric-chaintool channel on [Hyperledger Rocket Chat](https://chat.hyperledger.org) or reach out to us on the [hyperledger-fabric mailing list](https://lists.hyperledger.org/mailman/listinfo/hyperledger-fabric). 160 | 161 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 162 | s 163 | -------------------------------------------------------------------------------- /docs/images/typical-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledger-archives/fabric-chaintool/57d8f460d0bfdc7cb4ddea0147fea16f15a06258/docs/images/typical-workflow.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Introduction to Chaintool - A Hyperledger Fabric Chaincode Compiler 2 | 3 | ## Summary 4 | 5 | _chaintool_ is a utility to assist in various phases of [Hyperledger Fabric](https://github.com/hyperledger/fabric) chaincode development, such as compilation, test, packaging, and deployment. A chaincode app developer may express the interface to their application in a highlevel interface definition language, and _chaintool_ will generate (1) chaincode stubs and (2) package the chaincode for convenient deployment. 6 | 7 | This provides the following benefits to chaincode development: 8 | 9 | - a language neutral description of the application interface 10 | - a language neutral packaging of chaincode for deployment 11 | - on the wire endian neutrality 12 | - built-in field validation 13 | - forwards/backwards compatiblity management 14 | - stub function dispatch 15 | - build/package management 16 | - a unified query, signing, hashing, and introspection interface 17 | 18 | ## Overview 19 | 20 | Working with _chaintool_ generally involves structuring your chaincode [project root](./application-development.md#project-structure) a specific way and then running various [subcommands](./command-reference.md). Some commands, such as _build_, _clean_, and _package_, are used to manage general development activities. These commands expect to be executed from within your project root by default, but may also be executed from anywhere using the "-p" switch. 21 | 22 | Other commands such as _buildcar_, _unpack_, and _ls_ are designed to operate against a Chaincode Archive (CAR) from a previous _package_ operation. These commands expect a path to a CAR file. 23 | 24 | In all cases, you may obtain subcommand specific help by invoking "chaintool _$subcommand_ -h". For example: 25 | 26 | ``` 27 | $ chaintool package -h 28 | chaintool version: v0.10.1 29 | 30 | Description: chaintool package - Package the chaincode into a CAR file for deployment 31 | 32 | Usage: chaintool package [options] 33 | 34 | Command Options: 35 | -o, --output NAME path to the output destination 36 | -c, --compress NAME gzip compression algorithm to use 37 | -p, --path PATH ./ path to chaincode project 38 | -h, --help 39 | ``` 40 | 41 | ### Typical Workflow 42 | 43 | ![typical-worflow](images/typical-workflow.png) 44 | 45 | 46 | 47 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 48 | s 49 | -------------------------------------------------------------------------------- /docs/interface.md: -------------------------------------------------------------------------------- 1 | # Interface Definition Language 2 | 3 | An interface is a file ending in .cci (Chaincode Interface) that defines a language neutral definition for various RPC-like functions that a given chaincode instance supports. An chaincode instance may in fact support many different interfaces at one time. This is convenient for creating a type of polymorphism within a network of chaincode instances. 4 | 5 | Each .cci file is meant to represent an interface contract for compatibility. Items declared within a .cci file have provisions (similar to protobuf indices) for mutating structures over time that do not break forwards or backwards compatibility. Changes to a given interface should only be done in a manner which exploits this compatibility mechanism. If for some reason it is mandated that compatibility _must_ be broken, the name of the interface should be changed. 6 | 7 | ## Interface names 8 | 9 | The name of the .cci file has direct meaning to the ABI: the name of the file will be translated into ABI tokens on the wire. This was intentionally chosen so that the filesystem itself (under ./src/interfaces) takes a role in ensuring that only one interface of a particular type is in use within a project at any given time. Likewise, if a project wishes to import and consume an interface from a different project, it is imperative that the filename be retained across both projects or the endpoints will be inadvertently namespace-isolated from one another. To put it another way, do not rename .cci files on import! 10 | 11 | Perhaps even more importantly, interface ABI needs to be globally managed. Therefore it is advised to name .cci files in a way that is globally unique. A UUID would suffice, at the expense of being somewhat difficult to humans to deal with. Therefore, it is advised to name interfaces using DNS names as in the examples provided here. 12 | 13 | ## Definition 14 | 15 | Each interface definition loosely adheres to a protobuf-ish syntax. This was intentional, as the .cci file is actually translated into an intermediate .proto file before being handed to protoc to do the real work. The reason we did not just use protobuf syntax directly was because it was felt there were a few areas of the protobuf grammar that were suboptimal w.r.t. chaincode definition. Consider an example .cci: 16 | 17 | ``` 18 | message PaymentParams { 19 | string partySrc = 1; 20 | string partyDst = 2; 21 | int32 amount = 3; 22 | } 23 | 24 | message Entity { 25 | string id = 1; 26 | } 27 | 28 | message BalanceResult { 29 | int32 balance = 1; 30 | } 31 | 32 | functions { 33 | void MakePayment(PaymentParams) = 1; 34 | void DeleteAccount(Entity) = 2; 35 | BalanceResult CheckBalance(Entity) = 3; 36 | } 37 | ``` 38 | 39 | 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. 40 | 41 | 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: 42 | - Be indexed (e.g. ABI depends on index stability, not function name) 43 | - Accept only 0 or 1 _message_ as input and return only 0 (via _void_) or 1 message as output 44 | - We rely on the message definitions for further ABI stability. 45 | 46 | ## "Appinit" interface 47 | 48 | Every project has an implicit interface: appinit.cci. This interface is intended to define the "init" or constructor function for a given chaincode. It is also generally assumed to be not something that needs to be shared with other projects in the same manner that application-level interfaces might, thus we are not concerned about "appinit.cci" name conflicting in the way we care about other interfaces. 49 | 50 | The interface expected to define a message "Init" with no RPCs. This message will be assumed to be the argument to the chaincode constructor. 51 | 52 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 53 | s 54 | -------------------------------------------------------------------------------- /docs/metadata.md: -------------------------------------------------------------------------------- 1 | ## Metadata 2 | 3 | Every chaincode application built with chaintool includes metadata which may be queried with _chaintool inspect_. This metadata contains various details about a running application, such as enumerating the interfaces surfaced by the endpoint. The caller may optionally request to download the CCI schemas for these interfaces to facilitate application-specific client interaction. 4 | 5 | ### Details 6 | 7 | Chaintool emits a shadow interface _org.hyperledger.chaintool.meta_ that supports meta queries in every application built with chaintool. This interface has the following CCI at the time of writing: 8 | ``` 9 | message InterfaceDescriptor { 10 | string name = 1; 11 | bytes data = 2; 12 | } 13 | 14 | message Interfaces { 15 | repeated InterfaceDescriptor descriptors = 1; 16 | } 17 | 18 | message GetInterfacesParams { 19 | bool IncludeContent = 1; 20 | } 21 | 22 | message GetInterfaceParams { 23 | string name = 1; 24 | } 25 | 26 | message GetFactsParams { 27 | } 28 | 29 | message Facts { 30 | message Fact { 31 | string name = 1; 32 | string value = 2; 33 | } 34 | 35 | repeated Fact facts = 1; 36 | } 37 | 38 | functions { 39 | Interfaces GetInterfaces(GetInterfacesParams) = 1; 40 | InterfaceDescriptor GetInterface(GetInterfaceParams) = 2; 41 | Facts GetFacts(GetFactsParams) = 3; 42 | } 43 | ``` 44 | This means that clients may optionally interact with this CCI using the same protocol discussed above to learn further details about the running application. This includes obtaining the CCI specifications of the application which may be consumed in other projects. Alternatively, users may simply use the _chaintool inspect_ command to obtain the desired information. 45 | 46 | 47 | 48 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 49 | s 50 | -------------------------------------------------------------------------------- /docs/platforms/golang.md: -------------------------------------------------------------------------------- 1 | # Golang Chaincode Platform 2 | _Chaintool_ provides support for chaincode written in the [Go](https://golang.org/) language. This document describes the conventions of the chaintool Golang environment required for understanding and working with chaincode on this platform. It is assumed that the reader is already familar with the general usage of chaintool as well as general Golang platform within Hyperledger Fabric. 3 | 4 | ## Platform specifier 5 | Users enable the golang platform with the following configuration in chaincode.yaml: 6 | ``` 7 | Platform: 8 | Name: org.hyperledger.chaincode.golang 9 | Version: 1 10 | ``` 11 | 12 | ## Environment 13 | Any invocation of 'chaintool build*' will automatically synthesize the correct value for the $GOPATH environment variable based on the current value of the variable and the path of your chaincode project tree. Your chaincode project tree need _not_ be located within the directory hierarchy defined by the current value of $GOPATH. The 'chaintool build' command will ensures that the build correctly includes Go code from following paths (where $CHAINCODE is the root directory of your application chaincode.) 14 | 15 | - $CHAINCODE/build/deps 16 | - Direct and transitive dependencies of your application as retrieved by _go get_. (Please note: it is highly unlikely that production Hyperledger peers will use _go get_ to resolve dependencies. This use of _go get_ is an artifact of ongoing Hyperledger development; reducing development friction as the platform dependencies are refined. Operationally all dependencies will be explicit. 17 | - $CHAINCODE/build 18 | - Root directory of the default location for compiler generated artifacts. 19 | - $CHAINCODE 20 | - Root directory for your application source files. Typically all application code is stored under $CHAINCODE/src/chaincode. 21 | - current $GOPATH as set in the environment. 22 | 23 | ## Chaincode Integration 24 | ### Entry-point 25 | Your chaincode entry-point _func main()_ should be placed in a file stored in a directory under $CHAINCODE/src/chaincode/. The function should be part of a package called "chaincode". Other packages may be placed in files stored in other locations in the $CHAINCODE directory hierarchy and may be imported by your entry-point module using standard golang mechanisms. (The typical Go convention is to place source files in directories rooted at $CHAINCODE/src). Any Go files that are stored under $CHAINCODE/src will be included in the final CAR package. 26 | 27 | ### Imports 28 | In addition to any packages imported as part of your application logic your chaincode will import packages from four other locations. 29 | 30 | * hyperledger/ccs - "chaincode support" 31 | - generated "stub" code produced by the chaintool compiler 32 | * hyperledger/cci/... - "chaincode interface" 33 | - Go code which implements the interfaces defined in the application's .cci files including the required appinit.cci. The functions are placed in a package the name of which is generated from the name of the .cci file. The path to these files is likewise generated from the name of the .cci file. For example code generated from a file named "com.foo.bar.cci" is placed in the package "bar" under the path $CHAINCODE/src/hyperledger/cci/com/foo/bar 34 | * github.com/golang/protobuf/proto - google protocol buffer support 35 | - Go implementation of Google Protocol Buffers. Protocol Buffers are used by the chaintool generated code to encode messages used by chaincode. 36 | * github.com/hyperledger/fabric/core/chaincode/shim - generic Hyperledger chaincode support 37 | - Common Go language support for required chaincode operations. 38 | 39 | Here's the import section required for the 'example02' sample chaincode. This application includes "org.hyperledger.chaincode.example02.cci" and "project.cci" located in $CHAINCODE/src/interfaces/. 40 | 41 | ``` 42 | import ( 43 | "hyperledger/ccs" 44 | "hyperledger/cci/appinit" 45 | "hyperledger/cci/org/hyperledger/chaincode/example02" 46 | 47 | "github.com/golang/protobuf/proto" 48 | "github.com/hyperledger/fabric/core/chaincode/shim" 49 | ) 50 | ``` 51 | 52 | ### Hooks and Registration 53 | Each chaincode application must call ccs.Start() to register itself as chaincode with the peer. The ccs.Start() function takes a map of interface names to interface implementations expressed as pointers compatible with the CCInterface specification. This interface is generated by the compiler and placed within the interface specific stub (e.g. $CHAINCODE/build/src/hyperledger/cci/appinit/server-stub.go) . It is the application programmer's responsibility to provide an implementation for each interface declared as provided in the chaincode.yaml, plus the implicit appinit.cci. 54 | 55 | It is idiomatic to create one implementation structure (e.g. "ChaincodeExample", as shown below) to handle all interfaces. However, for special circumstances such as function-name collisions between interfaces, the caller may wish to dispatch certain interfaces to different handlers. 56 | ``` 57 | type ChaincodeExample struct { 58 | } 59 | 60 | ... 61 | 62 | func main() { 63 | self := &ChaincodeExample{} 64 | 65 | // Our one instance implements both Transactions and Queries functions for all interfaces 66 | interfaces := ccs.Interfaces { 67 | "org.hyperledger.chaincode.example02": self, 68 | "appinit": self, 69 | } 70 | 71 | err := ccs.Start(interfaces) 72 | if err != nil { 73 | fmt.Printf("Error starting example chaincode: %s", err) 74 | } 75 | } 76 | ``` 77 | 78 | ### Callbacks 79 | 80 | For each interface declared in the _provided_ section of the application configuration the application programmer must provide implementations for all the functions specified in the respective _transactions_ or _queries_ declarations. There is generally a 1:1 mapping between the CCI declaration and the required function signature in your code. For instance, the following CCI declaration: 81 | ``` 82 | queries { 83 | BalanceResult CheckBalance(Entity) = 1; 84 | } 85 | ``` 86 | requires a function signature that looks like: 87 | ``` 88 | func (t *ChaincodeExample) CheckBalance(stub shim.ChaincodeStubInterface, param *example02.Entity) (*example02.BalanceResult, error) {} 89 | ``` 90 | Every callback requires a shim.ChaincodeStubInterface as its first parameter, followed by an input parameter and return parameter as declared in the interface definition. Functions that return _void_ simply return a single _error_ rather than _(type, error)_. 91 | 92 | ## Generated Code File Structure 93 | 94 | ### Overview 95 | 96 | All generated code is placed in directories rooted at $CHAINCODE/build which, as mentioned earlier, is implicitly included in the $GOPATH. Interfaces are emitted to $CHAINCODE/build/src/hyperledger/cci/..., and general chaincode support stubs are emitted to $CHAINCODE/build/src/hyperledger/ccs. 97 | ``` 98 | build/src/ 99 | └── hyperledger 100 | ├── cci 101 | │   ├── appinit 102 | │   │   ├── interface.pb.go 103 | │   │   ├── interface.proto 104 | │   │   └── server-stub.go 105 | │   └── org 106 | │   └── hyperledger 107 | │   ├── chaincode 108 | │   │   └── example02 109 | │   │   ├── interface.pb.go 110 | │   │   ├── interface.proto 111 | │   │   └── server-stub.go 112 | │   └── chaintool 113 | │   └── meta 114 | │   ├── interface.pb.go 115 | │   ├── interface.proto 116 | │   └── server-stub.go 117 | └── ccs 118 | ├── api 119 | │   └── api.go 120 | ├── entrypoint.go 121 | └── metadata.go 122 | ``` 123 | ### Interfaces 124 | The chaintool compiler generates up to four artifact types per declared interface: 125 | 126 | * interface.proto - The google protobuf definition derived from the corresponding .cci definition. 127 | * interface.pb.go - The compiled protobuf definition, as emitted by _protoc --go_out_. 128 | * server-stub.go - The server-side stub for a provided interface. 129 | * client-stub.go - The client-side stub for a consumed interface. 130 | 131 | 132 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 133 | s 134 | -------------------------------------------------------------------------------- /docs/platforms/system.md: -------------------------------------------------------------------------------- 1 | # System Chaincode Platform 2 | 3 | System chaincode is a special variant of [org.hyperledger.chaincode.golang](golang.md) designed for system-chaincode development as an extension of the hyperledger fabric. This platform is implicitly golang based and notably not compiled as system chaincode is compiled as part of the fabric build. 4 | 5 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 6 | s 7 | -------------------------------------------------------------------------------- /examples/example02/README.md: -------------------------------------------------------------------------------- 1 | # Example02 2 | ## Introduction 3 | This directory contains an implementation of the chaincode application called "example02" as found in the hyperledger fabric distribution. The application has been ported to chaintool to demonstrate the features, capabilities, and techniques for working with chaintool based development. 4 | ## Directory Layout 5 | ``` 6 | ├── README.md 7 | ├── app 8 | │   ├── app.iml 9 | │   ├── chaincode.yaml 10 | │   └── src 11 | │   ├── chaincode 12 | │   │   └── chaincode_example02.go 13 | │   └── interfaces 14 | │   ├── appinit.cci 15 | │   └── org.hyperledger.chaincode.example02.cci 16 | └── client 17 | ├── cljs 18 | │   ├── Makefile 19 | │   ├── appinit.proto 20 | │   ├── org.hyperledger.chaincode.example02.proto 21 | │   ├── project.clj 22 | │   └── src 23 | │   └── example02 24 | │   ├── core.cljs 25 | │   ├── hlc 26 | │   │   ├── core.cljs 27 | │   │   └── user.cljs 28 | │   ├── main.cljs 29 | │   ├── rpc.cljs 30 | │   └── util.cljs 31 | └── nodejs 32 | ├── appinit.proto 33 | ├── index.js 34 | ├── util.js 35 | ├── org.hyperledger.chaincode.example02.proto 36 | └── package.json 37 | ``` 38 | * app - contains a org.hyperledger.chaincode.golang platform based chaincode application. 39 | * This is the code deployed to the blockchain 40 | * client - client applications for interacting with the chaincode application 41 | * nodejs - A simple demonstration of using nodejs. 42 | * cljs - A complete client for example02 written in ClojureScript 43 | 44 | ## Deploying and interacting with the example02 45 | ### Step 1 - Fabric environment 46 | 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) 47 | 48 | ### Step 2 - Package the chaincode application 49 | Run 'chaintool package' from the app folder, noting the CAR output path 50 | ``` 51 | $ cd app 52 | $ chaintool package 53 | Writing CAR to: /Users/ghaskins/sandbox/git/chaintool/examples/example02/app/build/org.hyperledger.chaincode.example02-0.1-SNAPSHOT.car 54 | Using path ./ ["src" "chaincode.yaml"] 55 | |------+------------------------------------------+--------------------------------------------------------| 56 | | Size | SHA1 | Path | 57 | |------+------------------------------------------+--------------------------------------------------------| 58 | | 438 | d28b22c7c30506af926dcb5bc8b946ac35ddac7f | chaincode.yaml | 59 | | 3856 | 542d088197e1a46bc21326e67e5d84d2d2807283 | src/chaincode/chaincode_example02.go | 60 | | 143 | 7305f65e18e4aab860b201d40916bb7adf97544f | src/interfaces/appinit.cci | 61 | | 375 | 9492a1e96f380a97bba1f16f085fc70140154c65 | src/interfaces/org.hyperledger.chaincode.example02.cci | 62 | |------+------------------------------------------+--------------------------------------------------------| 63 | Platform: org.hyperledger.chaincode.golang version 1 64 | Digital Signature: none 65 | Raw Data Size: 4812 bytes 66 | Archive Size: 2371 bytes 67 | Compression Alg: gzip 68 | Chaincode SHA3: f7026e0675b22a9d78b9f7f0cb97c93165bdefedc86de97f00e76b506c707b4ddbdfe97ad702ad600eae518891b9f0f1c8cb9a8b29b83908c2f6d46a6bcf4ecd 69 | ``` 70 | #### Note: 71 | 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. 72 | ### Step 3 - Compile the client 73 | Run 'make' from the client/cljs folder 74 | ``` 75 | $ make 76 | lein npm install 77 | example02@0.1.0-SNAPSHOT /Users/ghaskins/sandbox/git/chaintool/examples/example02/client/cljs 78 | ├─┬ protobufjs@5.0.1 79 | │ ├─┬ ascli@1.0.0 80 | │ │ ├── colour@0.7.1 81 | │ │ └── optjs@3.2.2 82 | │ ├─┬ bytebuffer@5.0.1 83 | │ │ └── long@3.1.0 84 | │ ├─┬ glob@5.0.15 85 | │ │ ├─┬ inflight@1.0.4 86 | │ │ │ └── wrappy@1.0.1 87 | │ │ ├── inherits@2.0.1 88 | │ │ ├─┬ minimatch@3.0.0 89 | │ │ │ └─┬ brace-expansion@1.1.4 90 | │ │ │ ├── balanced-match@0.4.1 91 | │ │ │ └── concat-map@0.0.1 92 | │ │ ├── once@1.3.3 93 | │ │ └── path-is-absolute@1.0.0 94 | │ └─┬ yargs@3.32.0 95 | │ ├── camelcase@2.1.1 96 | │ ├─┬ cliui@3.2.0 97 | │ │ ├─┬ strip-ansi@3.0.1 98 | │ │ │ └── ansi-regex@2.0.0 99 | │ │ └── wrap-ansi@2.0.0 100 | │ ├── decamelize@1.2.0 101 | │ ├─┬ os-locale@1.4.0 102 | │ │ └─┬ lcid@1.0.0 103 | │ │ └── invert-kv@1.0.0 104 | │ ├─┬ string-width@1.0.1 105 | │ │ ├─┬ code-point-at@1.0.0 106 | │ │ │ └── number-is-nan@1.0.0 107 | │ │ └── is-fullwidth-code-point@1.0.0 108 | │ ├── window-size@0.1.4 109 | │ └── y18n@3.2.1 110 | └─┬ source-map-support@0.4.0 111 | └─┬ source-map@0.1.32 112 | └── amdefine@1.0.0 113 | 114 | lein cljsbuild once 115 | Compiling ClojureScript... 116 | Compiling "out/example02.js" from ["src"]... 117 | Successfully compiled "out/example02.js" in 3.075 seconds. 118 | Compilation complete: use "node out/example02.js --help" for execution instructions 119 | ``` 120 | This will generate a nodejs application in _out/example02.js_. You may run it with --help to get a command summary. 121 | ``` 122 | $ node out/example02.js --help 123 | Usage: example02 [options] 124 | 125 | Options Summary: 126 | --host HOST localhost Host name 127 | --port PORT 3000 Port number 128 | -p, --path PATH Path/URL to the chaincode (deploy only, mutually exclsive with -n) 129 | -n, --name NAME Name of the chaincode (mutually exclusive with -p) 130 | -c, --command CMD check-balance One of [deploy make-payment delete-account check-balance] 131 | -a, --args ARGS JSON formatted arguments to submit 132 | -h, --help 133 | ``` 134 | ### Step 4 - Deploy the CAR 135 | We can deploy the CAR we packaged in Step 2 using the "-c deploy" feature of the client. We specify the path the CAR with -p and args with -a. 136 | 137 | ``` 138 | $ node ./out/example02.js -c deploy -p /fqp/to/app.car --port 5000 --args '{"partyA":{"entity":"a", "value":100}, "partyB":{"entity":"b", "value":100}}' 139 | ``` 140 | This will return something that looks like: 141 | ``` 142 | Response: {:result {:status OK, :message a9114852d11579bb6000abd7b2d3b25403aa7ff4f365a80ab2382a1616a066cddacefd3422c62337ba1b5eda2b2f4f04f5a2e3dbd411159db188d6946e83a95b}} 143 | ``` 144 | Note the hash that is returned in the {:result {:message}}, as this is your chaincode instance ID or "name". 145 | 146 | #### Note: 147 | -p must be a fully qualified path since it is passed to the VP as is. Future versions of the tool/peer may allow inline data, TBD. 148 | 149 | -a is expected to be a JSON structure that matches the protobuf definition for the request in particular. In this case, we are deploying so we are interested in the _Init_ message within the appinit.proto. 150 | 151 | #####If you started your node with _peer node start --peer-chaincodedev_, deploy your chaintool build like you would with non-chaintool chaincode. 152 | ``` 153 | $ CORE_CHAINCODE_ID_NAME=org.hyperledger.chaincode.example02 CORE_PEER_ADDRESS=0.0.0.0:30303 ./app/build/bin/org.hyperledger.chaincode.example02-0.1-SNAPSHOT 154 | $ node ./out/example02.js -c deploy -n org.hyperledger.chaincode.example02 --port 5000 --args '{"partyA":{"entity":"a", "value":100}, "partyB":{"entity":"b", "value":100}}' 155 | ``` 156 | 157 | #### Where did the .proto files come from? 158 | _chaintool proto_ was used to generate .proto files from the .cci files defined in ./app/src/interfaces 159 | ### Step 5 - Query our current balances 160 | We can use "-n $hash -c check-balance" to check the balance of one of our accounts 161 | ``` 162 | $ node ./out/example02.js -n a9114....946e83a95b --port 5000 -c check-balance --args '{"id":"a"}' 163 | ``` 164 | This should return with 165 | ``` 166 | Success: Balance = 100 167 | ``` 168 | We can repeat the process with id "b". Likewise, we can confirm that the system should return an error for any ids besides "a" or "b". 169 | #### Note: 170 | If you started your node with _peer node start --peer-chaincodedev_, change the hash (_a9114....946e83a95b_) to the name you chose in Step 4. 171 | ``` 172 | $ node ./out/example02.js -n org.hyperledger.chaincode.example02 --port 5000 -c check-balance --args '{"id":"a"}' 173 | ``` 174 | ### Step 6 - Make a Payment 175 | Now lets transfer 10 tokens from a to b. 176 | ``` 177 | $ node ./out/example02.js -n a9114....946e83a95b --port 5000 -c make-payment --args '{ "partySrc": "a", "partyDst": "b", "amount": 10}' 178 | ``` 179 | You should be able to repeat the query in Step 5 and confirm that a now holds 90 tokens while b holds 110. 180 | 181 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 182 | -------------------------------------------------------------------------------- /examples/example02/app/chaincode.yaml: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------- 3 | # chaincode example02 4 | # ---------------------------------- 5 | # 6 | # Copyright Greg Haskins All Rights Reserved 7 | # 8 | # SPDX-License-Identifier: Apache-2.0 9 | # 10 | 11 | Schema: 1 12 | Name: org.hyperledger.chaincode.example02 13 | Version: 0.1-SNAPSHOT 14 | 15 | Platform: 16 | Name: org.hyperledger.chaincode.golang 17 | Version: 1 18 | 19 | Provides: [self] # 'self' is a keyword that means there should be $name.cci (e.g. org.hyperledger.chaincode.example02.cci) 20 | -------------------------------------------------------------------------------- /examples/example02/app/src/chaincode/chaincode_example02.go: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | package main 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "strconv" 11 | 12 | "hyperledger/cci/appinit" 13 | "hyperledger/cci/org/hyperledger/chaincode/example02" 14 | "hyperledger/ccs" 15 | 16 | "github.com/golang/protobuf/proto" 17 | "github.com/hyperledger/fabric/core/chaincode/shim" 18 | ) 19 | 20 | type ChaincodeExample struct { 21 | } 22 | 23 | // Called to initialize the chaincode 24 | func (t *ChaincodeExample) Init(stub shim.ChaincodeStubInterface, param *appinit.Init) error { 25 | 26 | var err error 27 | 28 | fmt.Printf("Aval = %d, Bval = %d\n", param.PartyA.Value, param.PartyB.Value) 29 | 30 | // Write the state to the ledger 31 | err = t.PutState(stub, param.PartyA) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | err = t.PutState(stub, param.PartyB) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // Transaction makes payment of X units from A to B 45 | func (t *ChaincodeExample) MakePayment(stub shim.ChaincodeStubInterface, param *example02.PaymentParams) error { 46 | 47 | var err error 48 | 49 | // Get the state from the ledger 50 | src, err := t.GetState(stub, param.PartySrc) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | dst, err := t.GetState(stub, param.PartyDst) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | // Perform the execution 61 | X := int(param.Amount) 62 | src = src - X 63 | dst = dst + X 64 | fmt.Printf("Aval = %d, Bval = %d\n", src, dst) 65 | 66 | // Write the state back to the ledger 67 | err = stub.PutState(param.PartySrc, []byte(strconv.Itoa(src))) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | err = stub.PutState(param.PartyDst, []byte(strconv.Itoa(dst))) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | return nil 78 | } 79 | 80 | // Deletes an entity from state 81 | func (t *ChaincodeExample) DeleteAccount(stub shim.ChaincodeStubInterface, param *example02.Entity) error { 82 | 83 | // Delete the key from the state in ledger 84 | err := stub.DelState(param.Id) 85 | if err != nil { 86 | return errors.New("Failed to delete state") 87 | } 88 | 89 | return nil 90 | } 91 | 92 | // Query callback representing the query of a chaincode 93 | func (t *ChaincodeExample) CheckBalance(stub shim.ChaincodeStubInterface, param *example02.Entity) (*example02.BalanceResult, error) { 94 | var err error 95 | 96 | // Get the state from the ledger 97 | val, err := t.GetState(stub, param.Id) 98 | if err != nil { 99 | return nil, err 100 | } 101 | 102 | fmt.Printf("Query Response: %d\n", val) 103 | return &example02.BalanceResult{Balance: *proto.Int32(int32(val))}, nil 104 | } 105 | 106 | func main() { 107 | self := &ChaincodeExample{} 108 | interfaces := ccs.Interfaces { 109 | "org.hyperledger.chaincode.example02": self, 110 | "appinit": self, 111 | } 112 | 113 | err := ccs.Start(interfaces) // Our one instance implements both Transactions and Queries interfaces 114 | if err != nil { 115 | fmt.Printf("Error starting example chaincode: %s", err) 116 | } 117 | } 118 | 119 | //------------------------------------------------- 120 | // Helpers 121 | //------------------------------------------------- 122 | func (t *ChaincodeExample) PutState(stub shim.ChaincodeStubInterface, party *appinit.Party) error { 123 | return stub.PutState(party.Entity, []byte(strconv.Itoa(int(party.Value)))) 124 | } 125 | 126 | func (t *ChaincodeExample) GetState(stub shim.ChaincodeStubInterface, entity string) (int, error) { 127 | bytes, err := stub.GetState(entity) 128 | if err != nil { 129 | return 0, errors.New("Failed to get state") 130 | } 131 | if bytes == nil { 132 | return 0, errors.New("Entity not found") 133 | } 134 | 135 | val, _ := strconv.Atoi(string(bytes)) 136 | return val, nil 137 | } 138 | -------------------------------------------------------------------------------- /examples/example02/app/src/interfaces/appinit.cci: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | message Party { 8 | string entity = 1; 9 | int32 value = 2; 10 | } 11 | 12 | message Init { 13 | Party partyA = 1; 14 | Party partyB = 2; 15 | } 16 | -------------------------------------------------------------------------------- /examples/example02/app/src/interfaces/org.hyperledger.chaincode.example02.cci: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | message PaymentParams { 8 | string partySrc = 1; 9 | string partyDst = 2; 10 | int32 amount = 3; 11 | } 12 | 13 | message Entity { 14 | string id = 1; 15 | } 16 | 17 | message BalanceResult { 18 | int32 balance = 1; 19 | } 20 | 21 | functions { 22 | void MakePayment(PaymentParams) = 1; 23 | void DeleteAccount(Entity) = 2; 24 | BalanceResult CheckBalance(Entity) = 3; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | SRCS = $(shell find src -type f) 8 | LEIN = $(shell which lein || echo ../../../../lein) 9 | 10 | all: client 11 | 12 | client: ./target/nodecljs/main.js 13 | 14 | ./target/nodecljs/main.js: $(SRCS) Makefile 15 | $(LEIN) nodecompile 16 | @echo "Compilation complete: use \"node $@ --help\" for execution instructions" 17 | 18 | clean: 19 | -@rm -rf ./target ||: 20 | -@rm -rf ./node_modules ||: 21 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/project.clj: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | 7 | (defproject example02 "0.1.0-SNAPSHOT" 8 | :description "FIXME: write this!" 9 | :url "http://example.com/FIXME" 10 | :dependencies [[org.clojure/clojure "1.8.0"] 11 | [org.clojure/clojurescript "1.9.542"] 12 | [org.clojure/tools.cli "0.3.5"] 13 | [funcool/promesa "1.8.1"]] 14 | :plugins [[lein-nodecljs "0.7.0"]] 15 | :npm {:dependencies [[source-map-support "0.4.15"] 16 | [protobufjs "5.0.3"] 17 | [read-yaml "1.1.0"] 18 | [fabric-client "1.0.0-beta"]]} 19 | :source-paths ["src" "target/classes"] 20 | :clean-targets ["out" "release"] 21 | :target-path "target" 22 | :nodecljs {:main example02.main 23 | :files ["protos"]}) 24 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/protos/appinit.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by chaintool. DO NOT EDIT!! 3 | // 4 | 5 | syntax = "proto3"; 6 | 7 | package appinit; 8 | 9 | message Init { 10 | Party partyA = 1; 11 | Party partyB = 2; 12 | } 13 | 14 | message Party { 15 | string entity = 1; 16 | int32 value = 2; 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/protos/org.hyperledger.chaincode.example02.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by chaintool. DO NOT EDIT!! 3 | // 4 | 5 | syntax = "proto3"; 6 | 7 | package org.hyperledger.chaincode.example02; 8 | 9 | message BalanceResult { 10 | int32 balance = 1; 11 | } 12 | 13 | message Entity { 14 | string id = 1; 15 | } 16 | 17 | message PaymentParams { 18 | string partySrc = 1; 19 | string partyDst = 2; 20 | int32 amount = 3; 21 | } 22 | 23 | 24 | // 25 | // Available RPC functions exported by this interface 26 | // 27 | // void MakePayment(PaymentParams) -> org.hyperledger.chaincode.example02/txn/1 28 | // void DeleteAccount(Entity) -> org.hyperledger.chaincode.example02/txn/2 29 | // BalanceResult CheckBalance(Entity) -> org.hyperledger.chaincode.example02/query/1 30 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/sample.config: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # Sample configuration, originally generated by fabric.git/examples/cluster. 7 | # 8 | ca: 9 | url: https://172.18.0.2:7054 10 | certificate: | 11 | -----BEGIN CERTIFICATE----- 12 | MIICLTCCAdOgAwIBAgIQUtpA1+L1r0wtq2xQbDUD4zAKBggqhkjOPQQDAjBjMQsw 13 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy 14 | YW5jaXNjbzERMA8GA1UEChMIb3JnMS5uZXQxFDASBgNVBAMTC2NhLm9yZzEubmV0 15 | MB4XDTE3MDUxODEzNDcyNVoXDTI3MDUxNjEzNDcyNVowYzELMAkGA1UEBhMCVVMx 16 | EzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xETAP 17 | BgNVBAoTCG9yZzEubmV0MRQwEgYDVQQDEwtjYS5vcmcxLm5ldDBZMBMGByqGSM49 18 | AgEGCCqGSM49AwEHA0IABD7I6vDGG8rDKT8JXNcQ5SibUcAV3LEETbPyVEFIqzmW 19 | FTRt4WP2PBMLmCaWJ1o3imxnQDH5Q4azM31iwkjmGUqjaTBnMA4GA1UdDwEB/wQE 20 | AwIBpjAZBgNVHSUEEjAQBgRVHSUABggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/ 21 | MCkGA1UdDgQiBCAZHkKKVsQcda9JgHYhr2lFNOT912AMyD2IawTFPGfwoTAKBggq 22 | hkjOPQQDAgNIADBFAiEA0B+8HPbVBIk1xaxrfaEPaB0soa15IaaVygEIEK5xsuYC 23 | IDx/qwQWsR/F8EeNnrgCRXpT03v74L96pAoUj0Ik4/6q 24 | -----END CERTIFICATE----- 25 | 26 | orderer: 27 | url: grpcs://172.18.0.3:7050 28 | hostname: orderer 29 | ca: | 30 | -----BEGIN CERTIFICATE----- 31 | MIICOjCCAeCgAwIBAgIRAPUydZl5CtViAfdhGxmx3L0wCgYIKoZIzj0EAwIwaTEL 32 | MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG 33 | cmFuY2lzY28xFDASBgNVBAoTC29yZGVyZXIubmV0MRcwFQYDVQQDEw5jYS5vcmRl 34 | cmVyLm5ldDAeFw0xNzA1MTgxMzQ3MjVaFw0yNzA1MTYxMzQ3MjVaMGkxCzAJBgNV 35 | BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNp 36 | c2NvMRQwEgYDVQQKEwtvcmRlcmVyLm5ldDEXMBUGA1UEAxMOY2Eub3JkZXJlci5u 37 | ZXQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQS4tZgasL3dvFlZZ/tuYjF2EqD 38 | 6831TL21Lg7aCY37jUUjWYVOZoyDQviYaXwPwD6cnOTeQHrSRKVdbP28KOBYo2kw 39 | ZzAOBgNVHQ8BAf8EBAMCAaYwGQYDVR0lBBIwEAYEVR0lAAYIKwYBBQUHAwEwDwYD 40 | VR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgiip3TGazExyZaoDdbFkJPt0T//SqvzYv 41 | 05KcJc5bZJEwCgYIKoZIzj0EAwIDSAAwRQIhAM514AwlLgB23iSbYfiIOye/Sknx 42 | sBOAts8Q3eiKNS/lAiA6b6dG0ACK87Zwz+WmFGy8idsUz6+VUeZnGn3Q7fUQJA== 43 | -----END CERTIFICATE----- 44 | 45 | peers: 46 | - api: grpcs://172.18.0.4:7051 47 | events: grpcs://172.18.0.4:7053 48 | hostname: peer1 49 | - api: grpcs://172.18.0.7:7051 50 | events: grpcs://172.18.0.7:7053 51 | hostname: peer2 52 | - api: grpcs://172.18.0.5:7051 53 | events: grpcs://172.18.0.5:7053 54 | hostname: peer3 55 | - api: grpcs://172.18.0.6:7051 56 | events: grpcs://172.18.0.6:7053 57 | hostname: peer4 58 | 59 | identity: 60 | principal: Admin@org1.net 61 | mspid: Org1MSP 62 | privatekey: | 63 | -----BEGIN PRIVATE KEY----- 64 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBjZL0WX600IdbNTE 65 | ksDiPDNrfeFWujnL040elNc5PWyhRANCAAReHGXySigTarRxC9yExi+x1kkUQ5Ek 66 | vl01POTbj/zUr/rBcYFbcr0VofanA7bs7v2sNBLu4XKrcMtZhM1kGe8E 67 | -----END PRIVATE KEY----- 68 | certificate: | 69 | -----BEGIN CERTIFICATE----- 70 | MIICFjCCAbygAwIBAgIQQmDezE6sFW+liP/mJyPGZDAKBggqhkjOPQQDAjBjMQsw 71 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy 72 | YW5jaXNjbzERMA8GA1UEChMIb3JnMS5uZXQxFDASBgNVBAMTC2NhLm9yZzEubmV0 73 | MB4XDTE3MDUxODEzNDcyNVoXDTI3MDUxNjEzNDcyNVowUzELMAkGA1UEBhMCVVMx 74 | EzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xFzAV 75 | BgNVBAMMDkFkbWluQG9yZzEubmV0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE 76 | Xhxl8kooE2q0cQvchMYvsdZJFEORJL5dNTzk24/81K/6wXGBW3K9FaH2pwO27O79 77 | rDQS7uFyq3DLWYTNZBnvBKNiMGAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoG 78 | CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwKwYDVR0jBCQwIoAgGR5CilbEHHWvSYB2 79 | Ia9pRTTk/ddgDMg9iGsExTxn8KEwCgYIKoZIzj0EAwIDSAAwRQIhANvdmrxyJOgG 80 | 9pCcqm5j882lPYrb0+e7lY6jWgfyPBfOAiArov4lrvavFoHZzxZefqxUJFWbty5I 81 | w8+qlZElFL0fyg== 82 | -----END CERTIFICATE----- 83 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/example02/api.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns example02.api 7 | (:require [example02.protobuf :as pb] 8 | [example02.rpc :as rpc] 9 | [promesa.core :as p :include-macros true])) 10 | 11 | (def intf-name "org.hyperledger.chaincode.example02") 12 | 13 | (def all-interfaces ["appinit" intf-name]) 14 | 15 | (defn init [dir] 16 | (pb/init dir all-interfaces)) 17 | 18 | (defn install [context] 19 | (-> (rpc/install context) 20 | (p/then #(println "Success!")))) 21 | 22 | (defn instantiate [{:keys [args] :as context}] 23 | (let [proto (pb/get "appinit")] 24 | (-> context 25 | (assoc :func "init" 26 | :args (proto.Init. args)) 27 | rpc/instantiate 28 | (p/then #(println "Success!"))))) 29 | 30 | (defn make-payment [{:keys [args] :as context}] 31 | (let [proto (pb/get intf-name)] 32 | (-> context 33 | (assoc :func "org.hyperledger.chaincode.example02/fcn/1" 34 | :args (proto.PaymentParams. args)) 35 | rpc/transaction 36 | (p/then #(println "Success!"))))) 37 | 38 | (defn delete-account [{:keys [args] :as context}] 39 | (let [proto (pb/get intf-name)] 40 | (-> context 41 | (assoc :func "org.hyperledger.chaincode.example02/fcn/2" 42 | :args (proto.Entity. args)) 43 | rpc/transaction 44 | (p/then #(println "Success!"))))) 45 | 46 | (defn check-balance [{:keys [args] :as context}] 47 | (let [proto (pb/get intf-name)] 48 | (-> context 49 | (assoc :func "org.hyperledger.chaincode.example02/fcn/3" 50 | :args (proto.Entity. args)) 51 | rpc/query 52 | (p/then #(println "Success: Balance =" 53 | (->> % first proto.BalanceResult.decode64 .-balance)))))) 54 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/example02/connection.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns example02.connection 7 | (:require [fabric-sdk.core :as fabric] 8 | [fabric-sdk.channel :as fabric.channel] 9 | [fabric-sdk.eventhub :as fabric.eventhub] 10 | [fabric-sdk.user :as fabric.user] 11 | [promesa.core :as p :include-macros true])) 12 | 13 | (defn- set-state-store [client path] 14 | (-> (fabric/new-default-kv-store path) 15 | (p/then #(fabric/set-state-store client %)))) 16 | 17 | (defn- create-user [client identity] 18 | (let [config #js {:username (:principal identity) 19 | :mspid (:mspid identity) 20 | :cryptoContent #js {:privateKeyPEM (:privatekey identity) 21 | :signedCertPEM (:certificate identity)}}] 22 | 23 | (fabric/create-user client config))) 24 | 25 | (defn- connect-orderer [client channel config] 26 | (let [{:keys [ca hostname url]} (:orderer config) 27 | orderer (fabric/new-orderer client 28 | url 29 | #js {:pem ca 30 | :ssl-target-name-override hostname})] 31 | 32 | (fabric.channel/add-orderer channel orderer) 33 | 34 | orderer)) 35 | 36 | (defn- connect-peer [client channel config peercfg] 37 | (let [ca (-> config :ca :certificate) 38 | {:keys [api hostname]} peercfg 39 | peer (fabric/new-peer client 40 | api 41 | #js {:pem ca 42 | :ssl-target-name-override hostname 43 | :request-timeout 120000})] 44 | 45 | (fabric.channel/add-peer channel peer) 46 | 47 | peer)) 48 | 49 | (defn- connect-eventhub [client channel config] 50 | (let [ca (-> config :ca :certificate) 51 | {:keys [events hostname]} (-> config :peers first) 52 | eventhub (fabric/new-eventhub client)] 53 | 54 | (fabric.eventhub/set-peer-addr eventhub 55 | events 56 | #js {:pem ca 57 | :ssl-target-name-override hostname}) 58 | (fabric.eventhub/connect! eventhub) 59 | 60 | eventhub)) 61 | 62 | (defn connect! [{:keys [config id channelId] :as options}] 63 | 64 | (let [client (fabric/new-client) 65 | identity (:identity config)] 66 | 67 | (-> (set-state-store client ".hfc-kvstore") 68 | (p/then #(create-user client identity)) 69 | (p/then (fn [user] 70 | 71 | (let [channel (fabric.channel/new client channelId) 72 | orderer (connect-orderer client channel config) 73 | peers (->> config 74 | :peers 75 | (map #(connect-peer client channel config %))) 76 | eventhub (connect-eventhub client channel config)] 77 | 78 | (-> (fabric.channel/initialize channel) 79 | (p/then (fn [] 80 | {:client client 81 | :channel channel 82 | :orderer orderer 83 | :peers peers 84 | :eventhub eventhub 85 | :user user}))))))))) 86 | 87 | (defn disconnect! [{:keys [eventhub]}] 88 | (fabric.eventhub/disconnect! eventhub)) 89 | 90 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/example02/main.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns example02.main 7 | (:require [clojure.string :as string] 8 | [cljs.nodejs :as nodejs] 9 | [cljs.tools.cli :refer [parse-opts]] 10 | [example02.connection :as conn] 11 | [example02.api :as api] 12 | [promesa.core :as p :include-macros true])) 13 | 14 | (nodejs/enable-util-print!) 15 | 16 | (def fs (nodejs/require "fs")) 17 | (def pathlib (nodejs/require "path")) 18 | (def readyaml (nodejs/require "read-yaml")) 19 | 20 | (def _commands 21 | [["install" 22 | {:fn api/install}] 23 | ["instantiate" 24 | {:fn api/instantiate 25 | :default-args #js {:partyA #js {:entity "A" 26 | :value 100} 27 | :partyB #js {:entity "B" 28 | :value 200}}}] 29 | ["make-payment" 30 | {:fn api/make-payment 31 | :default-args #js {:partySrc "A" 32 | :partyDst "B" 33 | :amount 10}}] 34 | ["delete-account" 35 | {:fn api/delete-account 36 | :default-args #js {:id "A"}}] 37 | ["check-balance" 38 | {:fn api/check-balance 39 | :default-args #js {:id "A"}}]]) 40 | 41 | (def commands (into {} _commands)) 42 | (defn print-commands [] (->> commands keys vec print-str)) 43 | 44 | (def options 45 | [[nil "--config CONFIG" "path/to/client.config"] 46 | ["-p" "--path ID" "path/to/chaincode.car ('install' only)"] 47 | ["-i" "--chaincodeId ID" "ChaincodeID" 48 | :default "mycc"] 49 | ["-v" "--version VERSION" "Chaincode version" 50 | :default "1"] 51 | [nil "--channelId ID" "Channel ID" 52 | :default "mychannel"] 53 | ["-c" "--command CMD" (str "One of " (print-commands)) 54 | :default "check-balance" 55 | :validate [#(contains? commands %) (str "Supported commands: " (print-commands))]] 56 | ["-a" "--args ARGS" "JSON formatted arguments to submit"] 57 | ["-h" "--help"]]) 58 | 59 | (defn exit [status msg & rest] 60 | (do 61 | (apply println msg rest) 62 | status)) 63 | 64 | (defn prep-usage [msg] (->> msg flatten (string/join \newline))) 65 | 66 | (defn usage [options-summary] 67 | (prep-usage ["Usage: example02 [options]" 68 | "" 69 | "Options Summary:" 70 | options-summary 71 | ""])) 72 | 73 | (defn- exist? [path] 74 | (try 75 | (.statSync fs path) 76 | (catch js/Object e 77 | nil))) 78 | 79 | (defn -main [& args] 80 | ;; Initialize the protobuf layer, etc 81 | (api/init (.resolve pathlib js/__dirname ".." "protos")) 82 | 83 | (let [{:keys [options arguments errors summary]} (parse-opts args options) 84 | {:keys [config command args]} options] 85 | (cond 86 | 87 | (:help options) 88 | (exit 0 (usage summary)) 89 | 90 | (not= errors nil) 91 | (exit -1 "Error: " (string/join errors)) 92 | 93 | (nil? config) 94 | (exit -1 "Error: --config required (see --help)") 95 | 96 | (not (exist? config)) 97 | (exit -1 (str "Error: config \"" config "\" not found")) 98 | 99 | :else 100 | (let [desc (commands command) 101 | _args (if (empty? arguments) 102 | (:default-args desc) 103 | (->> arguments first (.parse js/JSON))) 104 | _config (-> (.sync readyaml config) 105 | (js->clj :keywordize-keys true))] 106 | 107 | (p/alet [context (p/await (conn/connect! (assoc options :config _config))) 108 | params (-> options 109 | (assoc :args _args) 110 | (merge context))] 111 | 112 | (println (str "Running " command "(" (.stringify js/JSON _args) ")")) 113 | 114 | ;; Run the subcommand funtion 115 | (-> ((:fn desc) params) 116 | (p/catch #(println "Error:" %)) 117 | (p/then #(conn/disconnect! context)))))))) 118 | 119 | (set! *main-cli-fn* -main) 120 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/example02/protobuf.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns example02.protobuf 7 | (:require [cljs.nodejs :as nodejs]) 8 | (:refer-clojure :exclude [get])) 9 | 10 | (def path (nodejs/require "path")) 11 | (def pb (nodejs/require "protobufjs")) 12 | 13 | (def builder (.newBuilder pb)) 14 | 15 | (defn- load [dir name] 16 | (let [path (.resolve path dir (str name ".proto"))] 17 | (.loadProtoFile pb path builder) 18 | (.build builder name))) 19 | 20 | (def protos (atom {})) 21 | 22 | (defn get [name] 23 | (@protos name)) 24 | 25 | (defn init [dir interfaces] 26 | (let [entries (->> interfaces 27 | (map #(vector % (load dir %))) 28 | (into {}))] 29 | (swap! protos merge entries))) 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/example02/rpc.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns example02.rpc 7 | (:require [cljs.nodejs :as nodejs] 8 | [fabric-sdk.core :as fabric] 9 | [fabric-sdk.channel :as fabric.channel] 10 | [fabric-sdk.eventhub :as fabric.eventhub] 11 | [promesa.core :as p :include-macros true])) 12 | 13 | (defn- create-base-request [{:keys [client peers channelId chaincodeId]}] 14 | (let [txid (fabric/new-txnid client)] 15 | 16 | {:chaincodeType "car" 17 | :targets peers 18 | :chainId channelId 19 | :chaincodeId chaincodeId 20 | :txId txid})) 21 | 22 | (defn- create-request [{:keys [func args] :as options}] 23 | (-> (create-base-request options) 24 | (assoc :fcn func :args #js [(.toBuffer args)]))) 25 | 26 | (defn- decodejs [js] 27 | (js->clj js :keywordize-keys true)) 28 | 29 | (defn- verify-results [[results proposal header :as response]] 30 | (doseq [result results] 31 | (let [retval (-> result decodejs :response :status)] 32 | (when-not (= retval 200) 33 | (throw result)))) 34 | 35 | response) 36 | 37 | (defn- register-tx-event [eventhub txid] 38 | (p/promise 39 | (fn [resolve reject] 40 | (fabric.eventhub/register-tx-event eventhub txid resolve)))) 41 | 42 | (defn- send-transaction [{:keys [channel response]}] 43 | (let [[results proposal header] response] 44 | (fabric.channel/send-transaction channel 45 | #js {:proposalResponses results 46 | :proposal proposal 47 | :header header}))) 48 | 49 | (defn- forward-endorsements [{:keys [eventhub request tmo] :as options}] 50 | (let [txid (-> request decodejs :txId)] 51 | (-> (p/all [(register-tx-event eventhub txid) 52 | (send-transaction options)]) 53 | (p/timeout tmo)))) 54 | 55 | (defn install [{:keys [client channel path version] :as options}] 56 | (let [request (-> (create-base-request options) 57 | (assoc :chaincodeVersion version 58 | :chaincodePath path) 59 | clj->js)] 60 | 61 | (when (not path) 62 | (fabric.channel/set-dev-mode channel true)) 63 | 64 | (-> (fabric/install-chaincode client request) 65 | (p/then verify-results)))) 66 | 67 | (defn instantiate [{:keys [client channel version] :as options}] 68 | (let [request (-> (create-request options) 69 | (assoc :chaincodeVersion version) 70 | clj->js)] 71 | 72 | (-> (fabric.channel/send-instantiate-proposal channel request) 73 | (p/then verify-results) 74 | (p/then #(forward-endorsements (assoc options 75 | :request request 76 | :response % 77 | :tmo 120000)))))) 78 | 79 | (defn transaction [{:keys [client channel] :as options}] 80 | (let [request (-> options create-request clj->js)] 81 | 82 | (-> (fabric.channel/send-transaction-proposal channel request) 83 | (p/then verify-results) 84 | (p/then #(forward-endorsements (assoc options 85 | :request request 86 | :response % 87 | :tmo 30000)))))) 88 | 89 | (defn query [{:keys [client channel] :as options}] 90 | (let [request (-> options create-request clj->js)] 91 | (fabric.channel/query-by-chaincode channel request))) 92 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/fabric_sdk/channel.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns fabric-sdk.channel 7 | (:require-macros [fabric-sdk.macros :as m]) 8 | (:require [promesa.core :as p :include-macros true])) 9 | 10 | (defn new [client name] 11 | (.newChannel client name)) 12 | 13 | (defn initialize [channel] 14 | (m/pwrap (.initialize channel))) 15 | 16 | (defn add-peer [channel instance] 17 | (.addPeer channel instance)) 18 | 19 | (defn add-orderer [channel instance] 20 | (.addOrderer channel instance)) 21 | 22 | (defn set-dev-mode [channel enabled] 23 | (.setDevMode channel enabled)) 24 | 25 | (defn send-instantiate-proposal [channel request] 26 | (m/pwrap (.sendInstantiateProposal channel request))) 27 | 28 | (defn send-transaction-proposal [channel request] 29 | (m/pwrap (.sendTransactionProposal channel request))) 30 | 31 | (defn send-transaction [channel request] 32 | (m/pwrap (.sendTransaction channel request))) 33 | 34 | (defn query-by-chaincode [channel request] 35 | (m/pwrap (.queryByChaincode channel request))) 36 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/fabric_sdk/core.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns fabric-sdk.core 7 | (:require-macros [fabric-sdk.macros :as m]) 8 | (:require [cljs.nodejs :as nodejs] 9 | [promesa.core :as p :include-macros true])) 10 | 11 | (def hfc (nodejs/require "fabric-client")) 12 | 13 | (defn new-client [] 14 | (new hfc)) 15 | 16 | (defn new-default-kv-store [path] 17 | (m/pwrap (.newDefaultKeyValueStore hfc #js {:path path}))) 18 | 19 | (defn set-state-store [client store] 20 | (.setStateStore client store)) 21 | 22 | (defn install-chaincode [client request] 23 | (m/pwrap (.installChaincode client request))) 24 | 25 | (defn get-user-context [client username] 26 | (m/pwrap (.getUserContext client username))) 27 | 28 | (defn set-user-context [client user] 29 | (m/pwrap (.setUserContext client user))) 30 | 31 | (defn create-user [client spec] 32 | (m/pwrap (.createUser client spec))) 33 | 34 | (defn new-orderer [client url opts] 35 | (.newOrderer client url opts)) 36 | 37 | (defn new-peer [client url opts] 38 | (.newPeer client url opts)) 39 | 40 | (defn new-eventhub [client] 41 | (.newEventHub client)) 42 | 43 | (defn new-txnid [client] 44 | (.newTransactionID client)) 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/fabric_sdk/eventhub.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns fabric-sdk.eventhub 7 | (:require-macros [fabric-sdk.macros :as m]) 8 | (:require [cljs.nodejs :as nodejs] 9 | [promesa.core :as p :include-macros true])) 10 | 11 | (defn set-peer-addr [instance addr opts] 12 | (.setPeerAddr instance addr opts)) 13 | 14 | (defn connect! [instance] 15 | (.connect instance)) 16 | 17 | (defn disconnect! [instance] 18 | (.disconnect instance)) 19 | 20 | (defn register-tx-event [instance txid cb] 21 | (.registerTxEvent instance (.getTransactionID txid) cb)) 22 | 23 | (defn unregister-tx-event [instance txid] 24 | (.unregisterTxEvent instance txid)) 25 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/fabric_sdk/macros.clj: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns fabric-sdk.macros 7 | (:require [promesa.core :as promesa])) 8 | 9 | (defmacro wrapped-resolve [expr exec result] 10 | `(do 11 | (comment (println "expr:" ~expr "resolved with" ~result)) 12 | (~exec ~result))) 13 | 14 | (defmacro wrapped-reject [expr exec result] 15 | `(do 16 | (comment (println "expr:" ~expr "rejected with" ~result)) 17 | (~exec ~result))) 18 | 19 | (defmacro pwrap 20 | ;; Implements an interop wrapper between JS promise and promesa 21 | [expr] 22 | `(promesa/promise 23 | (fn [resolve# reject#] 24 | (-> ~expr 25 | (.then #(wrapped-resolve '~expr resolve# %) 26 | #(wrapped-reject '~expr reject# %)))))) 27 | -------------------------------------------------------------------------------- /examples/example02/client/cljs/src/fabric_sdk/user.cljs: -------------------------------------------------------------------------------- 1 | ;;----------------------------------------------------------------------------- 2 | ;; Copyright 2017 Greg Haskins 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;;----------------------------------------------------------------------------- 6 | (ns fabric-sdk.user 7 | (:require-macros [fabric-sdk.macros :as m]) 8 | (:require [cljs.nodejs :as nodejs] 9 | [promesa.core :as p :include-macros true])) 10 | 11 | (def user (nodejs/require "fabric-client/lib/User.js")) 12 | 13 | (defn new [username client] 14 | (new user username client)) 15 | 16 | (defn enrolled? [user] 17 | (and user (.isEnrolled user))) 18 | 19 | (defn set-enrollment [user enrollment mspid] 20 | (m/pwrap (.setEnrollment user enrollment.key enrollment.certificate mspid))) 21 | -------------------------------------------------------------------------------- /examples/example02/client/nodejs/client.config: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by fabric.git/examples/cluster. DO NOT EDIT! 3 | # 4 | ca: 5 | url: https://172.18.0.7:7054 6 | certificate: | 7 | -----BEGIN CERTIFICATE----- 8 | MIICLTCCAdOgAwIBAgIQbZqIuH0sxAe5sOEv0aHulDAKBggqhkjOPQQDAjBjMQsw 9 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy 10 | YW5jaXNjbzERMA8GA1UEChMIb3JnMS5uZXQxFDASBgNVBAMTC2NhLm9yZzEubmV0 11 | MB4XDTE3MDYxMDAyMDUzNloXDTI3MDYwODAyMDUzNlowYzELMAkGA1UEBhMCVVMx 12 | EzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xETAP 13 | BgNVBAoTCG9yZzEubmV0MRQwEgYDVQQDEwtjYS5vcmcxLm5ldDBZMBMGByqGSM49 14 | AgEGCCqGSM49AwEHA0IABM5lOq+s74ewepmPIxkcpvN0Invb9e22jCHfGsWlxL0i 15 | cVIHTdlaE8oD4XDtwdnjQTjji1+Nc2BEORI8h25RDwajaTBnMA4GA1UdDwEB/wQE 16 | AwIBpjAZBgNVHSUEEjAQBgRVHSUABggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/ 17 | MCkGA1UdDgQiBCBrkiWWM7/ttPLO3BM5dZLW8MJBFp51O2/meAhnpyvL3jAKBggq 18 | hkjOPQQDAgNIADBFAiEA2v1aIR8hzhfZuT00AK7cRnuREBspMzBITo17whAZPkQC 19 | IBpkXzLkW8gkSwDa2GICV+QherD8ULoIeugiFiT2jl3a 20 | -----END CERTIFICATE----- 21 | 22 | orderer: 23 | url: grpcs://172.18.0.3:7050 24 | hostname: orderer 25 | ca: | 26 | -----BEGIN CERTIFICATE----- 27 | MIICOTCCAd+gAwIBAgIQAuJaGGTw7AVlD2xsFaZNNzAKBggqhkjOPQQDAjBpMQsw 28 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy 29 | YW5jaXNjbzEUMBIGA1UEChMLb3JkZXJlci5uZXQxFzAVBgNVBAMTDmNhLm9yZGVy 30 | ZXIubmV0MB4XDTE3MDYxMDAyMDUzNloXDTI3MDYwODAyMDUzNlowaTELMAkGA1UE 31 | BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz 32 | Y28xFDASBgNVBAoTC29yZGVyZXIubmV0MRcwFQYDVQQDEw5jYS5vcmRlcmVyLm5l 33 | dDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKnGqAQxgKs1fIw9psv126uogyAX 34 | xNLUFOtt0P3NtY+0ThtpFaYz5n4EN5cvMfjfBEsoKq1XApxKmR8siKkxoVajaTBn 35 | MA4GA1UdDwEB/wQEAwIBpjAZBgNVHSUEEjAQBgRVHSUABggrBgEFBQcDATAPBgNV 36 | HRMBAf8EBTADAQH/MCkGA1UdDgQiBCDxJaXPT40u0OY78WSM5yNMAWJl6qDCu4rJ 37 | Tk6iJyLspjAKBggqhkjOPQQDAgNIADBFAiEAuWk3RjJYIqD36Hhnf+gbUGeyaBIf 38 | F8OMZKg0K3g4gFUCIB88GcERpLoYALQfIH21QXkXQfZa33N25HRGNqAlsQxO 39 | -----END CERTIFICATE----- 40 | 41 | peers: 42 | - api: grpcs://172.18.0.2:7051 43 | events: grpcs://172.18.0.2:7053 44 | hostname: peer1 45 | - api: grpcs://172.18.0.4:7051 46 | events: grpcs://172.18.0.4:7053 47 | hostname: peer2 48 | - api: grpcs://172.18.0.5:7051 49 | events: grpcs://172.18.0.5:7053 50 | hostname: peer3 51 | - api: grpcs://172.18.0.6:7051 52 | events: grpcs://172.18.0.6:7053 53 | hostname: peer4 54 | 55 | identity: 56 | principal: Admin@org1.net 57 | mspid: Org1MSP 58 | privatekey: | 59 | -----BEGIN PRIVATE KEY----- 60 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIiUNEYZ8dVBzU+jE 61 | NYMixzeqk68Pu7lZxfQsJGtvK2ahRANCAARlTUeEVsOclueRBacRCVRVd1t7O3kD 62 | BnshXhEyM0qOrtC4sVnqIAuQ6tN+tD1F+aN5ryRtF6h608/P+wYzOrBb 63 | -----END PRIVATE KEY----- 64 | certificate: | 65 | -----BEGIN CERTIFICATE----- 66 | MIICFjCCAb2gAwIBAgIRAN5coFIxZO/llH5axPB4xL8wCgYIKoZIzj0EAwIwYzEL 67 | MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG 68 | cmFuY2lzY28xETAPBgNVBAoTCG9yZzEubmV0MRQwEgYDVQQDEwtjYS5vcmcxLm5l 69 | dDAeFw0xNzA2MTAwMjA1MzZaFw0yNzA2MDgwMjA1MzZaMFMxCzAJBgNVBAYTAlVT 70 | MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRcw 71 | FQYDVQQDDA5BZG1pbkBvcmcxLm5ldDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA 72 | BGVNR4RWw5yW55EFpxEJVFV3W3s7eQMGeyFeETIzSo6u0LixWeogC5Dq0360PUX5 73 | o3mvJG0XqHrTz8/7BjM6sFujYjBgMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAK 74 | BggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIGuSJZYzv+208s7c 75 | Ezl1ktbwwkEWnnU7b+Z4CGenK8veMAoGCCqGSM49BAMCA0cAMEQCID6/7BaU5mgk 76 | 7cOC46IFcMyy7ZNMacusZdhmhVZA6pJcAiAsCUSK7JoR/7rWiALb+O0HQFxzoIY9 77 | x931TuD1P0leyQ== 78 | -----END CERTIFICATE----- 79 | -------------------------------------------------------------------------------- /examples/example02/client/nodejs/client.js: -------------------------------------------------------------------------------- 1 | /* 2 | # 3 | # Copyright Greg Haskins All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | */ 8 | var program = require('commander'); 9 | var pb = require("protobufjs"); 10 | 11 | var builder = pb.newBuilder({ convertFieldsToCamelCase: true }); 12 | 13 | pb.loadProtoFile("./protos/appinit.proto", builder); 14 | var init = builder.build("appinit"); 15 | 16 | pb.loadProtoFile("./protos/org.hyperledger.chaincode.example02.proto", builder); 17 | var app = builder.build("org.hyperledger.chaincode.example02"); 18 | 19 | var path = require('path'); 20 | var ReadYaml = require('read-yaml'); 21 | 22 | var hfc = require('fabric-client'); 23 | var hfcutils = require('fabric-client/lib/utils.js'); 24 | var utils = require('./lib/util.js'); 25 | var Peer = require('fabric-client/lib/Peer.js'); 26 | var Orderer = require('fabric-client/lib/Orderer.js'); 27 | var EventHub = require('fabric-client/lib/EventHub.js'); 28 | var User = require('fabric-client/lib/User.js'); 29 | 30 | var client; 31 | var chain; 32 | var peers = []; 33 | var eventhub; 34 | 35 | var channelId = 'mychannel'; 36 | 37 | var config = ReadYaml.sync('client.config'); 38 | 39 | function createBaseRequest() { 40 | var tx_id = client.newTransactionID(); 41 | 42 | // send proposal to endorser 43 | var request = { 44 | chaincodeType: 'car', 45 | targets: peers, 46 | chainId: channelId, 47 | chaincodeId: 'mycc', 48 | txId: tx_id 49 | }; 50 | 51 | return request; 52 | } 53 | 54 | function createRequest(fcn, args) { 55 | var request = createBaseRequest(); 56 | 57 | request.fcn = fcn; 58 | request.args = [args.toBuffer()]; 59 | 60 | return request; 61 | } 62 | 63 | function connect() { 64 | client = new hfc(); 65 | 66 | return utils.setStateStore(client, ".hfc-kvstore") 67 | .then(() => { 68 | chain = client.newChannel(channelId); 69 | 70 | chain.addOrderer(client.newOrderer(config.orderer.url, { 71 | pem: config.orderer.ca, 72 | 'ssl-target-name-override': config.orderer.hostname 73 | })); 74 | 75 | for (var i in config.peers) { 76 | var p = config.peers[i] 77 | peer = client.newPeer(p.api, { 78 | pem: config.ca.certificate, 79 | 'ssl-target-name-override': p.hostname, 80 | 'request-timeout': 120000 81 | }); 82 | peers.push(peer); 83 | chain.addPeer(peer); 84 | } 85 | 86 | var userSpec = { 87 | username: config.identity.principal, 88 | mspid: config.identity.mspid, 89 | cryptoContent: { 90 | privateKeyPEM: config.identity.privatekey, 91 | signedCertPEM: config.identity.certificate 92 | }}; 93 | return client.createUser(userSpec); 94 | }) 95 | .then((user) => { 96 | var peer1 = config.peers[0] 97 | eventhub = client.newEventHub(); 98 | eventhub.setPeerAddr(peer1.events, { 99 | pem: config.ca.certificate, 100 | 'ssl-target-name-override': peer1.hostname 101 | }); 102 | eventhub.connect(); 103 | 104 | return chain.initialize() 105 | .then(() => { 106 | 107 | return user; 108 | }); 109 | }); 110 | } 111 | 112 | function disconnect() { 113 | return new Promise((resolve, reject) => { 114 | eventhub.disconnect(); 115 | resolve(); 116 | }); 117 | } 118 | 119 | function sendInstall(path, version) { 120 | 121 | var request = createBaseRequest(); 122 | if (path) { 123 | request.chaincodePath = path; 124 | } else { 125 | chain.setDevMode(true); 126 | } 127 | 128 | console.log(version); 129 | request.chaincodeVersion = "1"; 130 | 131 | // send proposal to endorser 132 | return client.installChaincode(request); 133 | } 134 | 135 | function sendInstantiate(args) { 136 | 137 | var request = createRequest('init', new init.Init(args)); 138 | request.chaincodeVersion = "1"; 139 | 140 | // send proposal to endorser 141 | return chain.sendInstantiateProposal(request) 142 | .then((response) => { 143 | return utils.processResponse(chain, eventhub, request, response, 120000); 144 | }); 145 | } 146 | 147 | function sendTransaction(fcn, args) { 148 | 149 | var request = createRequest(fcn, args); 150 | 151 | return chain.sendTransactionProposal(request) 152 | .then((response) => { 153 | return utils.processResponse(chain, eventhub, request, response, 20000); 154 | }); 155 | } 156 | 157 | function sendQuery(fcn, args) { 158 | var request = createRequest(fcn, args); 159 | return chain.queryByChaincode(request); 160 | } 161 | 162 | function makePayment(args) { 163 | return sendTransaction('org.hyperledger.chaincode.example02/fcn/1', 164 | new app.PaymentParams(args)); 165 | } 166 | 167 | function checkBalance(args) { 168 | return sendQuery('org.hyperledger.chaincode.example02/fcn/3', 169 | new app.Entity(args)) 170 | .then((results) => { 171 | return app.BalanceResult.decode(results[0]); 172 | }); 173 | } 174 | 175 | program 176 | .version('0.0.1'); 177 | 178 | program 179 | .command('install') 180 | .option("-p, --path ", "Path to chaincode.car") 181 | .option("-v, --version ", "Version of chaincode to install") 182 | .action((options) => { 183 | return connect() 184 | .then(() => { 185 | return sendInstall(options.path, options.version); 186 | }) 187 | .then(() => { 188 | return disconnect(); 189 | }) 190 | .catch((err) => { 191 | console.log("error:" + err); 192 | }); 193 | }); 194 | 195 | program 196 | .command('instantiate') 197 | .action(() => { 198 | return connect() 199 | .then(() => { 200 | return sendInstantiate({ 201 | 'partyA': {'entity':'A', 'value':100}, 202 | 'partyB': {'entity':'B', 'value':200} 203 | }); 204 | }) 205 | .then(() => { 206 | return disconnect(); 207 | }) 208 | .catch((err) => { 209 | console.log("error:" + err); 210 | }); 211 | }); 212 | 213 | program 214 | .command('makepayment ') 215 | .action((partySrc, partyDst, amount) => { 216 | return connect() 217 | .then(() => { 218 | return makePayment({ 219 | 'partySrc': partySrc, 220 | 'partyDst': partyDst, 221 | 'amount': parseInt(amount) 222 | }); 223 | }) 224 | .then(() => { 225 | return disconnect(); 226 | }) 227 | .catch((err) => { 228 | console.log("error:" + err); 229 | }); 230 | }); 231 | 232 | program 233 | .command('checkbalance ') 234 | .action((id) => { 235 | return connect() 236 | .then(() => { 237 | return checkBalance({'id':id}); 238 | }) 239 | .then((result) => { 240 | console.log("balance:" + result.balance); 241 | return disconnect(); 242 | }) 243 | .catch((err) => { 244 | console.log("error:" + err); 245 | }); 246 | }); 247 | 248 | 249 | program.parse(process.argv); 250 | -------------------------------------------------------------------------------- /examples/example02/client/nodejs/lib/util.js: -------------------------------------------------------------------------------- 1 | var hfc = require('fabric-client'); 2 | var utils = require('fabric-client/lib/utils.js'); 3 | var User = require('fabric-client/lib/User.js'); 4 | 5 | function registerTxEvent(eventhub, txid, tmo) { 6 | return new Promise((resolve, reject) => { 7 | 8 | var handle = setTimeout(() => { 9 | eventhub.unregisterTxEvent(txid); 10 | reject("Timeout!"); 11 | }, tmo); 12 | 13 | eventhub.registerTxEvent(txid, (tx, code) => { 14 | resolve(code); 15 | eventhub.unregisterTxEvent(txid); 16 | clearTimeout(handle); 17 | }); 18 | }); 19 | } 20 | 21 | function sendTransaction(chain, results) { 22 | var proposalResponses = results[0]; 23 | var proposal = results[1]; 24 | var header = results[2]; 25 | var all_good = true; 26 | 27 | for(var i in proposalResponses) { 28 | let one_good = false; 29 | 30 | if (proposalResponses && 31 | proposalResponses[0].response && 32 | proposalResponses[0].response.status === 200) { 33 | 34 | one_good = true; 35 | } 36 | all_good = all_good & one_good; 37 | } 38 | 39 | if (all_good) { 40 | var request = { 41 | proposalResponses: proposalResponses, 42 | proposal: proposal, 43 | header: header 44 | }; 45 | return chain.sendTransaction(request); 46 | } else { 47 | return Promise.reject("bad result:" + results); 48 | } 49 | } 50 | 51 | module.exports = { 52 | setStateStore: (client, path) => { 53 | return new Promise((resolve, reject) => { 54 | return hfc.newDefaultKeyValueStore({path: path}) 55 | .then((store) => { 56 | client.setStateStore(store); 57 | resolve(true); 58 | }); 59 | }); 60 | }, 61 | 62 | getUser: (client, cop, mspid, username, password) => { 63 | return client.getUserContext(username, true) 64 | .then((user) => { 65 | if (user && user.isEnrolled()) { 66 | return Promise.resolve(user); 67 | } else { 68 | // need to enroll it with COP server 69 | return cop.enroll({ 70 | enrollmentID: username, 71 | enrollmentSecret: password 72 | }).then((enrollment) => { 73 | var member = new User(username, client); 74 | return member.setEnrollment(enrollment.key, 75 | enrollment.certificate, 76 | mspid) 77 | .then(() => { 78 | return client.setUserContext(member); 79 | }); 80 | }); 81 | } 82 | }); 83 | }, 84 | 85 | processResponse: (chain, eventhub, request, response, tmo) => { 86 | return Promise.all( 87 | [registerTxEvent(eventhub, request.txId.getTransactionID(), tmo), 88 | sendTransaction(chain, response)]); 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /examples/example02/client/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chaintool-example02", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "client.js", 6 | "dependencies": { 7 | "protobufjs":">=5.0.3", 8 | "commander":"2.9.0", 9 | "winston":"2.3.1", 10 | "read-yaml":"1.1.0", 11 | "fabric-client":"1.0.0-beta" 12 | }, 13 | "files": [ 14 | "protos", 15 | "lib" 16 | ], 17 | "scripts": { 18 | "test": "echo \"Error: no test specified\" && exit 1" 19 | }, 20 | "author": "", 21 | "license": "Apache-2.0" 22 | } 23 | -------------------------------------------------------------------------------- /examples/example02/client/nodejs/protos/appinit.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by chaintool. DO NOT EDIT!! 3 | // 4 | 5 | syntax = "proto3"; 6 | 7 | package appinit; 8 | 9 | message Init { 10 | Party partyA = 1; 11 | Party partyB = 2; 12 | } 13 | 14 | message Party { 15 | string entity = 1; 16 | int32 value = 2; 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/example02/client/nodejs/protos/org.hyperledger.chaincode.example02.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by chaintool. DO NOT EDIT!! 3 | // 4 | 5 | syntax = "proto3"; 6 | 7 | package org.hyperledger.chaincode.example02; 8 | 9 | message BalanceResult { 10 | int32 balance = 1; 11 | } 12 | 13 | message Entity { 14 | string id = 1; 15 | } 16 | 17 | message PaymentParams { 18 | string partySrc = 1; 19 | string partyDst = 2; 20 | int32 amount = 3; 21 | } 22 | 23 | 24 | // 25 | // Available RPC functions exported by this interface 26 | // 27 | // void MakePayment(PaymentParams) -> org.hyperledger.chaincode.example02/txn/1 28 | // void DeleteAccount(Entity) -> org.hyperledger.chaincode.example02/txn/2 29 | // BalanceResult CheckBalance(Entity) -> org.hyperledger.chaincode.example02/query/1 30 | -------------------------------------------------------------------------------- /examples/helloworld/chaincode.yaml: -------------------------------------------------------------------------------- 1 | # ---------------------------------- 2 | # chaincode "hello world" 3 | # ---------------------------------- 4 | # 5 | # Copyright Greg Haskins All Rights Reserved 6 | # 7 | # SPDX-License-Identifier: Apache-2.0 8 | # 9 | 10 | Schema: 1 11 | Name: org.hyperledger.chaincode.helloworld 12 | Version: 0.1-SNAPSHOT 13 | 14 | Platform: 15 | Name: org.hyperledger.chaincode.golang 16 | Version: 1 17 | 18 | Provides: [self] 19 | -------------------------------------------------------------------------------- /examples/helloworld/src/chaincode/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Greg Haskins 2017. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | 12 | "hyperledger/cci/appinit" 13 | "hyperledger/ccs" 14 | 15 | "org/hyperledger/chaincode/helloworld" 16 | 17 | "github.com/hyperledger/fabric/core/chaincode/shim" 18 | ) 19 | 20 | type ChaincodeExample struct { 21 | } 22 | 23 | func (t *ChaincodeExample) Init(stub shim.ChaincodeStubInterface, param *appinit.Init) error { 24 | 25 | return nil 26 | } 27 | 28 | func main() { 29 | self := &ChaincodeExample{} 30 | hello := &helloworld.Interface{} 31 | interfaces := ccs.Interfaces{ 32 | "org.hyperledger.chaincode.helloworld": hello, 33 | "appinit": self, 34 | } 35 | 36 | err := ccs.Start(interfaces) // Our one instance implements both Transactions and Queries interfaces 37 | if err != nil { 38 | fmt.Printf("Error starting example chaincode: %s", err) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/helloworld/src/interfaces/appinit.cci: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | message Init { 8 | } 9 | -------------------------------------------------------------------------------- /examples/helloworld/src/interfaces/org.hyperledger.chaincode.helloworld.cci: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | functions { 8 | string Hello(string) = 1; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/helloworld/src/org/hyperledger/chaincode/helloworld/core.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Greg Haskins 2017. All Rights Reserved. 3 | 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | package helloworld 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/hyperledger/fabric/core/chaincode/shim" 13 | ) 14 | 15 | type Interface struct { 16 | } 17 | 18 | func (t *Interface) Hello(stub shim.ChaincodeStubInterface, name *string) (string, error) { 19 | 20 | return fmt.Sprintf("Hello, %s", *name), nil 21 | } 22 | -------------------------------------------------------------------------------- /examples/invoker/chaincode.yaml: -------------------------------------------------------------------------------- 1 | 2 | # ----------------------------------------------------------------------- 3 | # chaincode invoker 4 | # ----------------------------------------------------------------------- 5 | # 6 | # 7 | # Copyright Greg Haskins All Rights Reserved 8 | # 9 | # SPDX-License-Identifier: Apache-2.0 10 | # 11 | # This example demonstrates chaincode to chaincode invoke. This example 12 | # consumes the services of our example02 chaincode 13 | # 14 | 15 | Schema: 1 16 | Name: org.hyperledger.chaincode.example.invoker 17 | Version: 0.1-SNAPSHOT 18 | 19 | Platform: 20 | Name: org.hyperledger.chaincode.golang 21 | Version: 1 22 | 23 | Provides: [org.hyperledger.chaincode.example02] 24 | Consumes: [org.hyperledger.chaincode.example02] 25 | -------------------------------------------------------------------------------- /examples/invoker/src/chaincode/chaincode.go: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "hyperledger/cci/appinit" 11 | "hyperledger/cci/org/hyperledger/chaincode/example02" 12 | "hyperledger/ccs" 13 | 14 | "github.com/hyperledger/fabric/core/chaincode/shim" 15 | ) 16 | 17 | type ChaincodeExample struct { 18 | } 19 | 20 | // Called to initialize the chaincode 21 | func (t *ChaincodeExample) Init(stub shim.ChaincodeStubInterface, param *appinit.Init) error { 22 | 23 | var err error 24 | 25 | // Write the state to the ledger 26 | err = stub.PutState("ProxyAddress", []byte(param.Address)) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return nil 32 | } 33 | 34 | // Transaction makes payment of X units from A to B 35 | func (t *ChaincodeExample) MakePayment(stub shim.ChaincodeStubInterface, param *example02.PaymentParams) error { 36 | 37 | var err error 38 | 39 | // Get the state from the ledger 40 | addr, err := stub.GetState("ProxyAddress") 41 | if err != nil { 42 | return err 43 | } 44 | 45 | return example02.MakePayment(stub, string(addr), param) 46 | } 47 | 48 | // Deletes an entity from state 49 | func (t *ChaincodeExample) DeleteAccount(stub shim.ChaincodeStubInterface, param *example02.Entity) error { 50 | 51 | var err error 52 | 53 | // Get the state from the ledger 54 | addr, err := stub.GetState("ProxyAddress") 55 | if err != nil { 56 | return err 57 | } 58 | 59 | return example02.DeleteAccount(stub, string(addr), param) 60 | } 61 | 62 | // Query callback representing the query of a chaincode 63 | func (t *ChaincodeExample) CheckBalance(stub shim.ChaincodeStubInterface, param *example02.Entity) (*example02.BalanceResult, error) { 64 | 65 | var err error 66 | 67 | // Get the state from the ledger 68 | addr, err := stub.GetState("ProxyAddress") 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | return example02.CheckBalance(stub, string(addr), param) 74 | } 75 | 76 | func main() { 77 | self := &ChaincodeExample{} 78 | interfaces := ccs.Interfaces{ 79 | "org.hyperledger.chaincode.example02": self, 80 | "appinit": self, 81 | } 82 | err := ccs.Start(interfaces) // Our one instance implements both Transactions and Queries interfaces 83 | if err != nil { 84 | fmt.Printf("Error starting example chaincode: %s", err) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /examples/invoker/src/interfaces/appinit.cci: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Copyright Greg Haskins All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | message Init { 8 | string address = 1; 9 | } 10 | -------------------------------------------------------------------------------- /examples/invoker/src/interfaces/org.hyperledger.chaincode.example02.cci: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | message PaymentParams { 8 | string partySrc = 1; 9 | string partyDst = 2; 10 | int32 amount = 3; 11 | } 12 | 13 | message Entity { 14 | string id = 1; 15 | } 16 | 17 | message BalanceResult { 18 | int32 balance = 1; 19 | } 20 | 21 | functions { 22 | void MakePayment(PaymentParams) = 1; 23 | void DeleteAccount(Entity) = 2; 24 | BalanceResult CheckBalance(Entity) = 3; 25 | } 26 | -------------------------------------------------------------------------------- /examples/parameterless/chaincode.yaml: -------------------------------------------------------------------------------- 1 | # ---------------------------------- 2 | # chaincode example "parameterless" 3 | # ---------------------------------- 4 | # 5 | # Copyright (C) 2016 - Hyperledger 6 | # All rights reserved 7 | # 8 | 9 | Schema: 1 10 | Name: org.hyperledger.chaincode.example.parameterless 11 | Version: 0.1-SNAPSHOT 12 | 13 | Platform: 14 | Name: org.hyperledger.chaincode.golang 15 | Version: 1 16 | 17 | Provides: [self] 18 | Consumes: [org.hyperledger.chaincode.example.parameterless] 19 | -------------------------------------------------------------------------------- /examples/parameterless/src/chaincode/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "hyperledger/cci/appinit" 11 | app "hyperledger/cci/org/hyperledger/chaincode/example/parameterless" 12 | "hyperledger/ccs" 13 | 14 | "github.com/hyperledger/fabric/core/chaincode/shim" 15 | ) 16 | 17 | type ChaincodeExample struct { 18 | } 19 | 20 | // Called to initialize the chaincode 21 | func (t *ChaincodeExample) Init(stub shim.ChaincodeStubInterface, param *appinit.Init) error { 22 | 23 | return nil 24 | } 25 | 26 | func (t *ChaincodeExample) TestParameterless(stub shim.ChaincodeStubInterface) (*app.MyReturnType, error) { 27 | return nil, nil 28 | } 29 | 30 | func main() { 31 | self := &ChaincodeExample{} 32 | interfaces := ccs.Interfaces{ 33 | "org.hyperledger.chaincode.example.parameterless": self, 34 | "appinit": self, 35 | } 36 | 37 | err := ccs.Start(interfaces) // Our one instance implements both Transactions and Queries interfaces 38 | if err != nil { 39 | fmt.Printf("Error starting example chaincode: %s", err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/parameterless/src/interfaces/appinit.cci: -------------------------------------------------------------------------------- 1 | message Init { 2 | } 3 | -------------------------------------------------------------------------------- /examples/parameterless/src/interfaces/org.hyperledger.chaincode.example.parameterless.cci: -------------------------------------------------------------------------------- 1 | 2 | message MyReturnType { 3 | string foo = 1; 4 | } 5 | 6 | functions { 7 | MyReturnType TestParameterless() = 1; 8 | } 9 | -------------------------------------------------------------------------------- /examples/sample_syscc/chaincode.yaml: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------- 3 | # chaincode example02 4 | # ---------------------------------- 5 | # 6 | # 7 | # Copyright Greg Haskins All Rights Reserved 8 | # 9 | # SPDX-License-Identifier: Apache-2.0 10 | # 11 | # 12 | 13 | Schema: 1 14 | Name: org.hyperledger.chaincode.system.sample 15 | Version: 0.1-SNAPSHOT 16 | 17 | Platform: 18 | Name: org.hyperledger.chaincode.system 19 | Version: 1 20 | 21 | Provides: [self] # 'self' is a keyword that means there should be $name.cci (e.g. org.hyperledger.chaincode.system.sample.cci) 22 | -------------------------------------------------------------------------------- /examples/sample_syscc/interfaces/appinit.cci: -------------------------------------------------------------------------------- 1 | 2 | message Party { 3 | string entity = 1; 4 | int32 value = 2; 5 | } 6 | 7 | message Init { 8 | Party partyA = 1; 9 | Party partyB = 2; 10 | } 11 | -------------------------------------------------------------------------------- /examples/sample_syscc/interfaces/org.hyperledger.chaincode.system.sample.cci: -------------------------------------------------------------------------------- 1 | 2 | message PaymentParams { 3 | string partySrc = 1; 4 | string partyDst = 2; 5 | int32 amount = 3; 6 | } 7 | 8 | message Entity { 9 | string id = 1; 10 | } 11 | 12 | message BalanceResult { 13 | int32 balance = 1; 14 | } 15 | 16 | functions { 17 | void MakePayment(PaymentParams) = 1; 18 | void DeleteAccount(Entity) = 2; 19 | BalanceResult CheckBalance(Entity) = 3; 20 | } 21 | -------------------------------------------------------------------------------- /examples/sample_syscc/sample_syscc.go: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | package sample_syscc 6 | 7 | import ( 8 | "errors" 9 | "github.com/hyperledger/fabric/core/chaincode/shim" 10 | ) 11 | 12 | // SampleSysCC example simple Chaincode implementation 13 | type SampleSysCC struct { 14 | } 15 | 16 | func (t *SampleSysCC) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) { 17 | var key, val string // Entities 18 | 19 | if len(args) != 2 { 20 | return nil, errors.New("need 2 args (key and a value).") 21 | } 22 | 23 | // Initialize the chaincode 24 | key = args[0] 25 | val = args[1] 26 | // Write the state to the ledger 27 | err := stub.PutState(key, []byte(val)) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | return nil, nil 33 | } 34 | 35 | // Transaction makes payment of X units from A to B 36 | func (t *SampleSysCC) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) { 37 | var key, val string // Entities 38 | 39 | if len(args) != 2 { 40 | return nil, errors.New("need 2 args (key and a value).") 41 | } 42 | 43 | // Initialize the chaincode 44 | key = args[0] 45 | val = args[1] 46 | 47 | _, err := stub.GetState(key) 48 | if err != nil { 49 | jsonResp := "{\"Error\":\"Failed to get val for " + key + "\"}" 50 | return nil, errors.New(jsonResp) 51 | } 52 | 53 | // Write the state to the ledger 54 | err = stub.PutState(key, []byte(val)) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | return nil, nil 60 | } 61 | 62 | // Query callback representing the query of a chaincode 63 | func (t *SampleSysCC) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) { 64 | if function != "getval" { 65 | return nil, errors.New("Invalid query function name. Expecting \"getval\"") 66 | } 67 | var key string // Entities 68 | var err error 69 | 70 | if len(args) != 1 { 71 | return nil, errors.New("Incorrect number of arguments. Expecting key to query") 72 | } 73 | 74 | key = args[0] 75 | 76 | // Get the state from the ledger 77 | valbytes, err := stub.GetState(key) 78 | if err != nil { 79 | jsonResp := "{\"Error\":\"Failed to get state for " + key + "\"}" 80 | return nil, errors.New(jsonResp) 81 | } 82 | 83 | if valbytes == nil { 84 | jsonResp := "{\"Error\":\"Nil val for " + key + "\"}" 85 | return nil, errors.New(jsonResp) 86 | } 87 | 88 | return valbytes, nil 89 | } 90 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Greg Haskins All Rights Reserved 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | site_name: Hyperledger Fabric Chaintool 8 | site_url: http://fabric-chaintool.readthedocs.io 9 | theme: readthedocs 10 | repo_url: https://github.com/hyperledger/fabric-chaintool.git 11 | site_description: 'Welcome to the Hyperledger fabric chaintool documentation' 12 | strict: true 13 | 14 | pages: 15 | - Introduction: index.md 16 | - Getting Started: getting-started.md 17 | - Interface Definition Language: interface.md 18 | - Command Reference: command-reference.md 19 | - Application Development: application-development.md 20 | - Client Development: client-development.md 21 | - Chaincode Platforms: 22 | - org.hyperledger.chaincode.golang: platforms/golang.md 23 | - org.hyperledger.chaincode.system: platforms/system.md 24 | - Metadata: metadata.md 25 | 26 | markdown_extensions: 27 | - toc: 28 | permalink: true 29 | - admonition 30 | - extra 31 | - tables 32 | - toc 33 | - fenced_code 34 | - smarty 35 | - footnotes 36 | 37 | copyright:

Contributed to the Hyperledger Project under the Apache Software License 2.0 38 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; Copyright Greg Haskins All Rights Reserved 3 | ;; 4 | ;; SPDX-License-Identifier: Apache-2.0 5 | ;; 6 | 7 | (defproject chaintool "1.1.4-SNAPSHOT" 8 | :description "Hyperledger Fabric chaincode tool" 9 | :url "https://github.com/hyperledger/fabric-chaintool" 10 | :license {:name "Apache License" 11 | :url "http://www.apache.org/licenses/LICENSE-2.0.txt"} 12 | :min-lein-version "2.0.0" 13 | :lein-release {:deploy-via :shell :shell ["true"]} 14 | :javac-options ["-target" "1.8" "-source" "1.8"] 15 | :jvm-opts ["-server"] 16 | :java-source-paths ["src"] 17 | :plugins [[lein-bin "0.3.5"] 18 | [lein-licenses "0.2.1"]] 19 | :dependencies [[org.clojure/clojure "1.9.0"] 20 | [org.clojure/tools.cli "0.3.5"] 21 | [org.clojure/algo.generic "0.1.2"] 22 | [org.clojure/data.codec "0.1.1"] 23 | [instaparse "1.4.8"] 24 | [clojure-tools "1.1.3"] 25 | [org.antlr/ST4 "4.0.8"] 26 | [me.raynes/conch "0.8.0"] 27 | [me.raynes/fs "1.4.6"] 28 | [org.clojars.ghaskins/protobuf "3.4.0-1"] 29 | [commons-io/commons-io "2.6"] 30 | [org.tukaani/xz "1.8"] 31 | [org.apache.commons/commons-compress "1.16.1"] 32 | [com.github.jponge/lzma-java "1.3"] 33 | [pandect "0.5.4"] 34 | [doric "0.9.0"] 35 | [circleci/clj-yaml "0.5.6"] 36 | [slingshot "0.12.2"] 37 | [clj-http "3.8.0"] 38 | [cheshire "5.8.0"]] 39 | :main ^:skip-aot chaintool.core 40 | :bin {:name "chaintool" 41 | :bin-path "target" 42 | :bootclasspath true} 43 | :target-path "target/%s" 44 | 45 | ;; nREPL by default starts in the :main namespace, we want to start in `user` 46 | ;; because that's where our development helper functions like (run) and 47 | ;; (browser-repl) live. 48 | :repl-options {:init-ns user} 49 | 50 | :profiles {:dev {:dependencies [[org.clojure/tools.nrepl "0.2.13"] 51 | [org.clojure/tools.namespace "0.2.11"]] 52 | :plugins [[cider/cider-nrepl "0.15.1"]] 53 | :source-paths ["dev"]} 54 | :uberjar {:aot :all}}) 55 | -------------------------------------------------------------------------------- /resources/generators/proto.stg: -------------------------------------------------------------------------------- 1 | emitfield(field) ::= " = ;" 2 | 3 | emitmsg(msg) ::= 4 | << 5 | message { 6 | 7 | } 8 | >> 9 | 10 | emitenum(enum) ::= 11 | << 12 | enum { 13 | }; separator="\n"> 14 | } 15 | >> 16 | 17 | emitoneof(oneof) ::= 18 | << 19 | oneof { 20 | }; separator="\n"> 21 | } 22 | >> 23 | 24 | emitdef(def) ::= 25 | << 26 | 27 | 28 | 29 | 30 | >> 31 | 32 | emitdefs(defs) ::= "}; separator=\"\n\">" 33 | 34 | protobuf(package, definitions, functions) ::= 35 | << 36 | // 37 | // Generated by chaintool. DO NOT EDIT!! 38 | // 39 | 40 | syntax = "proto3"; 41 | 42 | package ; 43 | 44 | 45 | 46 | 47 | // 48 | // Available RPC functions exported by this interface 49 | // 50 | 51 | () -> }; separator="\n"> 52 | 53 | >> 54 | -------------------------------------------------------------------------------- /resources/metadata/org.hyperledger.chaintool.meta.cci: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Copyright Greg Haskins All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | message InterfaceDescriptor { 8 | string name = 1; 9 | bytes data = 2; 10 | } 11 | 12 | message Interfaces { 13 | repeated InterfaceDescriptor descriptors = 1; 14 | } 15 | 16 | message GetInterfacesParams { 17 | bool IncludeContent = 1; 18 | } 19 | 20 | message GetInterfaceParams { 21 | string name = 1; 22 | } 23 | 24 | message GetFactsParams { 25 | } 26 | 27 | message Facts { 28 | message Fact { 29 | string name = 1; 30 | string value = 2; 31 | } 32 | 33 | repeated Fact facts = 1; 34 | } 35 | 36 | functions { 37 | Interfaces GetInterfaces(GetInterfacesParams) = 1; 38 | InterfaceDescriptor GetInterface(GetInterfaceParams) = 2; 39 | Facts GetFacts(GetFactsParams) = 3; 40 | } 41 | -------------------------------------------------------------------------------- /resources/parsers/interface/grammar.bnf: -------------------------------------------------------------------------------- 1 | interface ::= ( message | enum )* functions? 2 | 3 | message ::= <"message"> ident messageBody 4 | 5 | enum ::= <"enum"> ident <"{"> ( enumField | <";"> )* <"}"> 6 | 7 | enumField ::= enumName <"="> enumValue <";"> 8 | ::= ident 9 | ::= intLit 10 | 11 | functions ::= <"functions"> functionblock 12 | 13 | ::= <"{"> function* <"}"> 14 | function ::= rettype functionName <"("> param? <")"> <"="> index <";"> 15 | functionName := ident 16 | rettype ::= ident 17 | param ::= ident 18 | 19 | optionBody ::= ident ( "." ident )* "=" constant 20 | 21 | ::= <"{"> ( field | enum | message | oneof )* <"}"> 22 | 23 | field ::= modifier? type fieldName <"="> index ( <"["> fieldOption ( <","> fieldOption )* <"]"> )? <";"> 24 | 25 | fieldName ::= ident 26 | fieldOption ::= optionBody | "default" "=" constant 27 | 28 | modifier ::= "required" | "repeated" 29 | 30 | type ::= scalar / userType / map / oneof 31 | 32 | scalar ::= "double" | "float" | "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string" | "bytes" 33 | 34 | userType ::= #"[A-Za-z_\.]*" 35 | 36 | map = <"map"> <"<"> keyType <","> type <">"> 37 | keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" | 38 | "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string" 39 | 40 | oneof ::= <"oneof"> ident <"{"> (field | <";">)* <"}"> 41 | 42 | constant ::= ident | intLit | floatLit | strLit | boolLit 43 | 44 | ::= #"[A-Za-z_][\w_]*" 45 | 46 | camelIdent ::= #"[A-Z][\w_]*" 47 | 48 | index ::= intLit 49 | 50 | ::= decInt | hexInt | octInt 51 | 52 | ::= #"\d+" 53 | 54 | ::= #"0[xX]([A-Fa-f0-9])+" 55 | 56 | ::= #"0[0-7]+" 57 | 58 | ::= #"\d+(\.\d+)?([Ee][\+-]?\d+)?" 59 | 60 | ::= "true" | "false" 61 | 62 | ::= quote ( hexEscape | octEscape | charEscape )* quote 63 | 64 | ::= #"[\"']" 65 | 66 | ::= #"\\[Xx][A-Fa-f0-9]{1,2}" 67 | 68 | ::= #"\\0?[0-7]{1,3}" 69 | 70 | ::= #"\\[abfnrtv\\\?'\"]" 71 | -------------------------------------------------------------------------------- /resources/parsers/interface/skip.bnf: -------------------------------------------------------------------------------- 1 | skipper ::= (whitespace | blankline | ( wlcomment / comment))* 2 | 3 | whitespace ::= #"[ \t]" 4 | blankline ::= #"^\n" 5 | wlcomment ::= #'^' comment 6 | comment ::= ('//' | '#') (!'\n' #'.')* '\n' 7 | -------------------------------------------------------------------------------- /resources/parsers/proto/grammar.bnf: -------------------------------------------------------------------------------- 1 | proto ::= syntax? package? ( message | enum )* 2 | 3 | syntax ::= <"syntax"> <"="> <"\""> ident <"\""><";"> 4 | package ::= <"package"> packageName <";"> 5 | 6 | packageName ::= #"[A-Za-z_.]*" 7 | 8 | message ::= <"message"> ident messageBody 9 | 10 | enum ::= <"enum"> ident <"{"> ( enumField | <";"> )* <"}"> 11 | 12 | enumField ::= enumName <"="> enumValue <";"> 13 | ::= ident 14 | ::= intLit 15 | 16 | optionBody ::= ident ( "." ident )* "=" constant 17 | 18 | ::= <"{"> ( field | enum | message | oneof )* <"}"> 19 | 20 | field ::= modifier? type fieldName <"="> index ( <"["> fieldOption ( <","> fieldOption )* <"]"> )? <";"> 21 | 22 | fieldName ::= ident 23 | fieldOption ::= optionBody | "default" "=" constant 24 | 25 | modifier ::= "required" | "repeated" 26 | 27 | type ::= scalar / userType / map / oneof 28 | 29 | scalar ::= "double" | "float" | "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string" | "bytes" 30 | 31 | userType ::= ( "."? ident )+ 32 | 33 | map = <"map"> <"<"> keyType <","> type <">"> 34 | keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" | 35 | "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string" 36 | 37 | oneof ::= <"oneof"> ident <"{"> (field | <";">)* <"}"> 38 | 39 | constant ::= ident | intLit | floatLit | strLit | boolLit 40 | 41 | ::= #"[A-Za-z_][\w_]*" 42 | 43 | camelIdent ::= #"[A-Z][\w_]*" 44 | 45 | index ::= intLit 46 | 47 | ::= decInt | hexInt | octInt 48 | 49 | ::= #"\d+" 50 | 51 | ::= #"0[xX]([A-Fa-f0-9])+" 52 | 53 | ::= #"0[0-7]+" 54 | 55 | ::= #"\d+(\.\d+)?([Ee][\+-]?\d+)?" 56 | 57 | ::= "true" | "false" 58 | 59 | ::= quote ( hexEscape | octEscape | charEscape )* quote 60 | 61 | ::= #"[\"']" 62 | 63 | ::= #"\\[Xx][A-Fa-f0-9]{1,2}" 64 | 65 | ::= #"\\0?[0-7]{1,3}" 66 | 67 | ::= #"\\[abfnrtv\\\?'\"]" 68 | -------------------------------------------------------------------------------- /resources/parsers/proto/skip.bnf: -------------------------------------------------------------------------------- 1 | skipper ::= (whitespace | blankline | ( wlcomment / comment))* 2 | 3 | whitespace ::= #"[ \t]" 4 | blankline ::= #"^\n" 5 | wlcomment ::= #'^' comment 6 | comment ::= ('//' | '#') (!'\n' #'.')* '\n' 7 | -------------------------------------------------------------------------------- /resources/proto/car.proto: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright London Stock Exchange Group 2016 All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | /* 19 | ************************************************************************************* 20 | 21 | We create a new archive format called CAR (chaincode archive) to package up 22 | chaincode assets. Our first approach was to reuse an existing archive format/tooling 23 | (e.g. tar, zip, etc). However, most had problems with determinisim. For instance, 24 | tar injects things like UIDs and timestamps into the binary, which could result 25 | in a different binary each time regardless of code content. 26 | 27 | The "ar" tool from binutils showed some promise with explicit support for determinism, 28 | but its focus on elf containers seemed inappropriate. In the end, we realized it 29 | might be more straightforward to leverage our existing investment in protobufs and 30 | build our own custom format. It's not all that complicated anyway (famous last words). 31 | 32 | The current implementation is somewhat naive in that it assumes that the compressed 33 | contents of the archive will have no problem fitting in memory and/or a single 34 | protobuf object (defaults to 64MB max, I think). This is ok. For one, chaincode 35 | packages by definition should strive to be as small as possible since they 36 | need to be distributed around and persisted on the ledger. We certainly don't want to 37 | encourage anyone to submit really huge applications by making it easy to package one 38 | up ;). If this ever changes, the fundamental nature of protobufs should allow us 39 | to be creative in enhancing the capabilities without breaking compatibility. 40 | 41 | ---- Schema ----- 42 | 43 | current definition {:magic "org.hyperledger.chaincode-archive" :version 1} 44 | 45 | A given archive file consists of two primary protobuf objects: 46 | 47 | [[delim] CompatibilityHeader, [delim] Archive] 48 | 49 | that are written with the java .writeDelimitedTo() (https://goo.gl/8NeMkT) function. 50 | The delimimter is a simple size indicator, the specifications of which are left 51 | as an exercise for the reader. The rest of the specification should be 52 | understandable to someone fluent in protobuf definitions, as per below. 53 | 54 | A few notes: 55 | 56 | 1) Generally speaking, for a given {:magic :version} tuple, the 57 | CompatibilityHeader should never, ever, ever be modified in a way that breaks 58 | backwards/forward compatibility. It is, however, ok to extend it using standard 59 | protobuf techniques should that ever be necessary. Most enhancements should be 60 | done via the "features" facility. For example: 61 | 62 | features = ["org.hyperledger.car.enhancement23", "com.acme.gizmo.awesome"] 63 | 64 | may indicate that certain features are present. Components that understand 65 | what that means may react to the presence of the feature flag. Those that 66 | do not should be able to safely ignore it (generally relying on protobuf 67 | techniques to avoid incongruent structures) 68 | 2) If breaking compatibility is unvoidable, you _must_ modify either one or both 69 | of the {:magic :version} tuple. Changing either one is a statment of gross 70 | incompatibility with orthogonal version tuples. This is generally only done 71 | in extreme circumstances as an alternative to leaving software with no 72 | deterministic method to detect a problem. 73 | 3) There is an intentional level of indirection between Archive and Payload to 74 | facilitate ease in generating a comprehensive signature. Achive.payload is 75 | expected to be the raw binary serialized form of Achive::Payload. 76 | 4) The top-level Archive object should generally strive to remain as lightweight 77 | as possible to encapsulate as many fields as possible under the signature 78 | algorithm. 79 | 80 | *************************************************************************************** 81 | */ 82 | 83 | syntax = "proto2"; 84 | package chaintool.car.abi; 85 | 86 | message CompatibilityHeader { 87 | required string magic = 1; 88 | required int32 version = 2; 89 | repeated string features = 3; 90 | } 91 | 92 | message Archive { 93 | message Signature { 94 | enum Type { 95 | NONE = 1; 96 | RSA = 2; 97 | } 98 | 99 | optional Type type = 1; 100 | optional string description = 2; 101 | optional bytes data = 3; 102 | } 103 | 104 | message Payload { 105 | message Compression { 106 | enum Type { 107 | NONE = 1; 108 | GZIP = 2; 109 | LZMA = 3; 110 | BZIP2 = 4; 111 | XZ = 5; 112 | } 113 | 114 | optional Type type = 1 [default = NONE]; 115 | optional string description = 2; 116 | } 117 | 118 | message Entries { 119 | optional string path = 1; 120 | optional uint64 size = 2; 121 | optional string sha1 = 3; 122 | optional bytes data = 16; 123 | } 124 | 125 | optional Compression compression = 1; 126 | repeated Entries entries = 16; 127 | } 128 | 129 | optional Signature signature = 1; 130 | optional bytes payload = 2; 131 | } 132 | -------------------------------------------------------------------------------- /scripts/changelog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright IBM Corp All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | if [ "$#" -ne 2 ]; then 9 | echo "Prepends a changelog into the current CHANGELOG.md at the root of the project" 10 | echo "Note: must be run from root directory of repository clone" 11 | echo "Usage: ./scripts/changelog.sh " 12 | exit 1 13 | fi 14 | 15 | echo "## $2\n$(date)" >> CHANGELOG.new 16 | echo "" >> CHANGELOG.new 17 | git log $1..HEAD --oneline | grep -v Merge | sed -e "s/\[\(FAB-[0-9]*\)\]/\[\1\](https:\/\/jira.hyperledger.org\/browse\/\1\)/" -e "s/ \(FAB-[0-9]*\)/ \[\1\](https:\/\/jira.hyperledger.org\/browse\/\1\)/" -e "s/\([0-9|a-z]*\)/* \[\1\](https:\/\/github.com\/hyperledger\/fabric\/commit\/\1)/" >> CHANGELOG.new 18 | echo "" >> CHANGELOG.new 19 | cat CHANGELOG.md >> CHANGELOG.new 20 | mv -f CHANGELOG.new CHANGELOG.md 21 | -------------------------------------------------------------------------------- /src/chaintool/ast.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.ast 16 | (:require [clojure.zip :as zip]) 17 | (:refer-clojure :exclude [find])) 18 | 19 | ;;----------------------------------------------------------------- 20 | ;; find a specific element in the AST 21 | ;;----------------------------------------------------------------- 22 | (defn find [term ast] 23 | (loop [loc ast] 24 | (cond 25 | 26 | (or (nil? loc) (zip/end? loc)) 27 | nil 28 | 29 | (= (zip/node loc) term) 30 | (zip/up loc) 31 | 32 | :else 33 | (recur (zip/next loc))))) 34 | -------------------------------------------------------------------------------- /src/chaintool/build/core.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | (ns chaintool.build.core 15 | (:require [chaintool.platforms.core :as platforms.core] 16 | [chaintool.platforms.api :as platforms.api]) 17 | (:refer-clojure :exclude [compile])) 18 | 19 | 20 | (defn- run [fcn {:keys [config] :as params}] 21 | (when-let [platform (platforms.core/find config)] 22 | (fcn platform params))) 23 | 24 | ;; generate platform output (shim, protobufs, etc) 25 | (defn compile [params] 26 | (run platforms.api/build params)) 27 | 28 | ;; display environment variables used for build 29 | (defn env [params] 30 | (run platforms.api/env params)) 31 | -------------------------------------------------------------------------------- /src/chaintool/car/ls.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.car.ls 16 | (:require [chaintool.car.read :as car] 17 | [clojure.java.io :as io] 18 | [doric.core :as doric] 19 | [pandect.algo.sha3-512 :refer :all])) 20 | 21 | (defn platform-version [config] 22 | (str (->> config :Platform :Name) " version " (->> config :Platform :Version))) 23 | 24 | (defn ls [file] 25 | (let [{:keys [payload config]} (with-open [is (io/input-stream file)] (car/read is)) 26 | entries (:entries payload)] 27 | 28 | (println (doric/table [{:name :size} {:name :sha1 :title "SHA1"} {:name :path}] entries)) 29 | (println "Platform: " (platform-version config)) 30 | (println "Digital Signature: none") 31 | (println "Raw Data Size: " (->> entries (map :size) (reduce +)) "bytes") 32 | (println "Archive Size: " (.length file) "bytes") 33 | (println "Compression Alg: " (get-in payload [:compression :description])) 34 | (println "Chaincode SHA3: " (sha3-512 file)))) 35 | -------------------------------------------------------------------------------- /src/chaintool/car/read.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.car.read 16 | (:require [flatland.protobuf.core :as fl] 17 | [chaintool.util :as util] 18 | [chaintool.car.types :refer :all] 19 | [chaintool.codecs :as codecs] 20 | [chaintool.config.parser :as config.parser] 21 | [chaintool.config.util :as config.util] 22 | [pandect.algo.sha1 :refer :all]) 23 | (:refer-clojure :exclude [read])) 24 | 25 | ;;-------------------------------------------------------------------------------------- 26 | ;; read-protobuf - reads a single protobuf message from a delimited stream 27 | ;; 28 | ;; our protobuf file consists of two primary message types, delimited by a size. 29 | ;; (fl/protobuf-seq) is designed to return an infinite lazy sequence of one type 30 | ;; so we need to feed this through (first) to extract only one. 31 | ;;-------------------------------------------------------------------------------------- 32 | (defn read-protobuf [t is] (->> is (fl/protobuf-seq t) first)) 33 | 34 | ;;-------------------------------------------------------------------------------------- 35 | ;; read-XX - read one instance of a specific type of protobuf message [Header|Archive] 36 | ;;-------------------------------------------------------------------------------------- 37 | (defn read-header [is] (read-protobuf Header is)) 38 | (defn read-archive [is] (read-protobuf Archive is)) 39 | 40 | ;;-------------------------------------------------------------------------------------- 41 | ;; make-input-stream - factory function for creating an input-stream for a specific entry 42 | ;; 43 | ;; We install the necessary decompressor such that the output of this stream represents 44 | ;; raw, uncompressed original data 45 | ;;-------------------------------------------------------------------------------------- 46 | (defn make-input-stream [type entry] 47 | (let [is (->> entry :data .newInput)] 48 | (codecs/decompressor type is))) 49 | 50 | ;;-------------------------------------------------------------------------------------- 51 | ;; import-header - imports and validates a Header object from the input stream 52 | ;; 53 | ;; evaluates to the parsed header if successful, or throws an exception otherwise 54 | ;;-------------------------------------------------------------------------------------- 55 | (defn import-header [is] 56 | (if-let [header (read-header is)] 57 | (let [compat (select-keys header [:magic :version])] 58 | (if (= compat CompatVersion) 59 | (:features header) 60 | (util/abort -1 (str "Incompatible header detected (expected: " CompatVersion " got: " compat ")")))) 61 | (util/abort -1 (str "Failed to read archive header")))) 62 | 63 | ;;-------------------------------------------------------------------------------------- 64 | ;; import-archive - imports an Archive object from the input stream 65 | ;;-------------------------------------------------------------------------------------- 66 | (defn import-archive [is] 67 | (read-archive is)) ;; FIXME - check digitial signature 68 | 69 | ;;-------------------------------------------------------------------------------------- 70 | ;; import-entry - validates an entry object 71 | ;; 72 | ;; returns the proper input-stream-factory when sucessful, or throws an exception otherwise 73 | ;;-------------------------------------------------------------------------------------- 74 | (defn import-entry [compression entry] 75 | (let [type (:description compression) 76 | factory #(make-input-stream type entry)] 77 | 78 | ;; verify the SHA1 79 | (with-open [is (factory)] 80 | (let [sha (sha1 is)] 81 | (when (not= sha (:sha1 entry)) 82 | (util/abort -1 (str (:path entry) ": hash verification failure (expected: " (:sha1 entry) ", got: " sha ")"))))) 83 | 84 | ;; and inject our stream factory 85 | {:entry entry :input-stream-factory factory})) 86 | 87 | ;;-------------------------------------------------------------------------------------- 88 | ;; import-payload - imports a Payload object from Archive::Payload field 89 | ;; 90 | ;; We separate Archive from Payload to delineate signature boundaries. Everything within 91 | ;; Payload is expected to be optionally digitally signed (and thus verified upon import) 92 | ;;-------------------------------------------------------------------------------------- 93 | (defn import-payload [archive] (->> archive :payload .newInput (fl/protobuf-load-stream Payload))) 94 | 95 | ;;-------------------------------------------------------------------------------------- 96 | ;; synth-index - synthesize an index of entries, keyed by :path 97 | ;; 98 | ;; Takes a payload object and constructs a map of entries, keyed by their path. Each 99 | ;; entry is fully verified and processed (such as attaching an input-stream filter) 100 | ;;-------------------------------------------------------------------------------------- 101 | (defn synth-index [payload] 102 | (let [compression (:compression payload)] 103 | (->> (:entries payload) (map #(vector (:path %) (import-entry compression %))) (into {})))) 104 | 105 | ;;-------------------------------------------------------------------------------------- 106 | ;; entry-stream - instantiate an input-stream from the factory 107 | ;; 108 | ;; This allows a caller to obtain a simple input-stream interface to our entries where 109 | ;; all the details such as compression are already factored in. Therefore, this stream 110 | ;; is suitable to any number of tasks such as interpreting results, verifying contents, 111 | ;; or writing data to files 112 | ;;-------------------------------------------------------------------------------------- 113 | (defn entry-stream [entry] 114 | (let [factory (:input-stream-factory entry)] 115 | (factory))) 116 | 117 | (defn read [is] 118 | (let [features (import-header is) 119 | archive (import-archive is) 120 | payload (import-payload archive) 121 | index (synth-index payload)] 122 | 123 | (with-open [config-stream (->> config.util/configname index entry-stream)] 124 | (let [config (->> config-stream slurp config.parser/from-string)] 125 | {:features features :payload payload :index index :config config})))) 126 | -------------------------------------------------------------------------------- /src/chaintool/car/types.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | (ns chaintool.car.types 15 | (:import [chaintool.car.abi 16 | Car$CompatibilityHeader 17 | Car$Archive 18 | Car$Archive$Signature 19 | Car$Archive$Payload 20 | Car$Archive$Payload$Compression 21 | Car$Archive$Payload$Entries]) 22 | (:require [flatland.protobuf.core :as fl])) 23 | 24 | (def Header (fl/protodef Car$CompatibilityHeader)) 25 | (def Archive (fl/protodef Car$Archive)) 26 | (def Signature (fl/protodef Car$Archive$Signature)) 27 | (def Payload (fl/protodef Car$Archive$Payload)) 28 | (def Compression (fl/protodef Car$Archive$Payload$Compression)) 29 | (def Entries (fl/protodef Car$Archive$Payload$Entries)) 30 | 31 | (def CompatVersion {:magic "org.hyperledger.chaincode-archive" :version 1}) 32 | -------------------------------------------------------------------------------- /src/chaintool/car/unpack.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.car.unpack 16 | (:require [clojure.java.io :as io] 17 | [chaintool.car.read :as car.read])) 18 | 19 | ;;-------------------------------------------------------------------------------------- 20 | ;; unpack - given a (pre-read) index of entries and an outputdir, unpack each element 21 | ;;-------------------------------------------------------------------------------------- 22 | (defn unpack [index outputdir verbose] 23 | (dorun 24 | (for [[path item] index] 25 | (let [entry (:entry item) 26 | outputfile (io/file outputdir path)] 27 | 28 | ;; ensure our output path exists 29 | (io/make-parents outputfile) 30 | 31 | ;; walk each entry and stream it out to the filesystem 32 | (with-open [is (car.read/entry-stream item) 33 | os (io/output-stream outputfile)] 34 | 35 | ;; we optionally may report out status to stdout 36 | (when (= verbose :true) 37 | (println (:sha1 entry) (:path entry) (str "(" (:size entry) " bytes)"))) 38 | 39 | ;; stream it out, pulling the input stream through any appropriate decompressor transparently 40 | (io/copy is os)))))) 41 | -------------------------------------------------------------------------------- /src/chaintool/car/write.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.car.write 16 | (:require [chaintool.car.types :refer :all] 17 | [chaintool.codecs :as codecs] 18 | [chaintool.util :as util] 19 | [clojure.java.io :as io] 20 | [clojure.string :as string] 21 | [flatland.protobuf.core :as fl] 22 | [pandect.algo.sha1 :refer :all]) 23 | (:import (org.apache.commons.io.input TeeInputStream) 24 | (org.apache.commons.io.output ByteArrayOutputStream)) 25 | (:refer-clojure :exclude [import])) 26 | 27 | ;;-------------------------------------------------------------------------------------- 28 | ;; import - takes a file handle and returns a tuple containing [sha1 size data] 29 | ;; 30 | ;; sha1: a string containing the computed sha1 of the raw uncompressed file contents 31 | ;; size: the raw uncompressed size of the file as it existed on the filesystem 32 | ;; data: a byte-array containing the compressed binary data imported from the filesystem 33 | ;;-------------------------------------------------------------------------------------- 34 | (defn import [file compressiontype] 35 | (let [os (ByteArrayOutputStream.) 36 | [sha size] (with-open [is (io/input-stream file) ;; FIXME - validate maximum file size supported 37 | compressor (codecs/compressor compressiontype os) 38 | tee (TeeInputStream. is compressor)] 39 | [(sha1 tee) (.length file)])] ;; FIXME - prefer to get the length from the stream 40 | [sha size (.toByteArray os)])) 41 | 42 | ;;-------------------------------------------------------------------------------------- 43 | ;; buildentry - builds a protobuf "Entry" object based on the tuple as emitted by (convertfile) 44 | ;;-------------------------------------------------------------------------------------- 45 | (defn buildentry [{:keys [path handle]} compressiontype] 46 | (let [[sha size payload] (import handle compressiontype)] 47 | (fl/protobuf Entries :path path :size size :sha1 sha :data payload))) 48 | 49 | ;;-------------------------------------------------------------------------------------- 50 | ;; buildentries - builds a list of protobuf "Entry" objects based on an input list 51 | ;; of {handle path} tuples. The input file list is sorted by :path to ensure 52 | ;; deterministic results 53 | ;;-------------------------------------------------------------------------------------- 54 | (defn buildentries [files compressiontype] 55 | (->> files 56 | (sort-by :path) 57 | (map #(buildentry % compressiontype)))) 58 | 59 | ;;-------------------------------------------------------------------------------------- 60 | ;; buildcompression - builds a protobuf "Compression" object based on the requested type 61 | ;; after validating that the type is a supported option. 62 | ;;-------------------------------------------------------------------------------------- 63 | (defn buildcompression [type] 64 | (if (codecs/codec-types type) 65 | (fl/protobuf Compression :type (string/upper-case type) :description type))) 66 | 67 | (defn write [files compressiontype outputfile] 68 | (if-let [compression (buildcompression compressiontype)] 69 | (let [header (fl/protobuf Header :magic (:magic CompatVersion) :version (:version CompatVersion)) 70 | entries (buildentries files compressiontype) 71 | payload (fl/protobuf Payload :compression compression :entries entries) 72 | archive (fl/protobuf Archive :payload (fl/protobuf-dump payload))] 73 | 74 | ;; ensure the path exists 75 | (io/make-parents outputfile) 76 | 77 | ;; emit our output 78 | (with-open [os (io/output-stream outputfile :truncate true)] 79 | (fl/protobuf-write os header archive))) 80 | 81 | ;; else 82 | (util/abort -1 (str "Unknown compression type: \"" compressiontype "\"")))) 83 | -------------------------------------------------------------------------------- /src/chaintool/codecs.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.codecs 16 | (:import (lzma.streams LzmaOutputStream$Builder) 17 | (org.apache.commons.compress.compressors.bzip2 BZip2CompressorInputStream 18 | BZip2CompressorOutputStream) 19 | (org.apache.commons.compress.compressors.gzip GzipCompressorInputStream 20 | GzipCompressorOutputStream 21 | GzipParameters) 22 | (org.apache.commons.compress.compressors.lzma LZMACompressorInputStream) 23 | (org.apache.commons.compress.compressors.xz XZCompressorInputStream 24 | XZCompressorOutputStream) 25 | (org.apache.commons.io.input ProxyInputStream) 26 | (org.apache.commons.io.output ProxyOutputStream))) 27 | 28 | ;;-------------------------------------------------------------------------------------- 29 | ;; compression support 30 | ;;-------------------------------------------------------------------------------------- 31 | (def codec-descriptors 32 | [{:name "none" 33 | :output #(ProxyOutputStream. %) 34 | :input #(ProxyInputStream. %)} 35 | 36 | {:name "gzip" 37 | :output #(let [params (GzipParameters.)] (.setCompressionLevel params 9) (GzipCompressorOutputStream. % params)) 38 | :input #(GzipCompressorInputStream. %)} 39 | 40 | {:name "lzma" 41 | :output #(-> (LzmaOutputStream$Builder. %) .build) 42 | :input #(LZMACompressorInputStream. %)} 43 | 44 | {:name "bzip2" 45 | :output #(BZip2CompressorOutputStream. %) 46 | :input #(BZip2CompressorInputStream. %)} 47 | 48 | {:name "xz" 49 | :output #(XZCompressorOutputStream. % 6) 50 | :input #(XZCompressorInputStream. %)}]) 51 | 52 | (def codec-types (->> codec-descriptors (map #(vector (:name %) %)) (into {}))) 53 | 54 | (defn compressor [type os] ((->> type codec-types :output) os)) 55 | (defn decompressor [type is] ((->> type codec-types :input) is)) 56 | -------------------------------------------------------------------------------- /src/chaintool/config/parser.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.config.parser 16 | (:require [chaintool.util :as util] 17 | [clj-yaml.core :as yaml])) 18 | 19 | (def supported-schema 1) 20 | 21 | (defn from-string [data] 22 | (let [config (yaml/parse-string data) 23 | schema (:Schema config)] 24 | (if (not= schema supported-schema) 25 | (util/abort -1 (str "Unsuported configuration schema (read:" schema " expected:" supported-schema ")")) 26 | config))) 27 | 28 | (defn from-file [file] (->> file slurp from-string)) 29 | -------------------------------------------------------------------------------- /src/chaintool/config/util.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.config.util 16 | (:require [chaintool.util :as util] 17 | [clojure.java.io :as io] 18 | [clojure.zip :as zip] 19 | [chaintool.config.parser :as config.parser]) 20 | (:refer-clojure :exclude [load find])) 21 | 22 | (def configname "chaincode.yaml") 23 | 24 | (defn load [path] 25 | (let [file (io/file path configname)] 26 | (cond 27 | 28 | (not (.isFile file)) 29 | (util/abort -1 (str (.getCanonicalPath file) " not found")) 30 | 31 | :else 32 | (config.parser/from-file file)))) 33 | 34 | (defn load-from-options [options] 35 | (let [path (:path options) 36 | config (load path)] 37 | [path config])) 38 | 39 | (defn compositename [config] 40 | (let [name (:Name config) 41 | version (:Version config)] 42 | (str name "-" version))) 43 | -------------------------------------------------------------------------------- /src/chaintool/core.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.core 16 | (:require [chaintool.subcommands.build :as buildcmd] 17 | [chaintool.subcommands.buildcar :as buildcarcmd] 18 | [chaintool.subcommands.deps :as depscmd] 19 | [chaintool.subcommands.clean :as cleancmd] 20 | [chaintool.subcommands.inspect :as inspectcmd] 21 | [chaintool.subcommands.ls :as lscmd] 22 | [chaintool.subcommands.package :as packagecmd] 23 | [chaintool.subcommands.proto :as protocmd] 24 | [chaintool.subcommands.unpack :as unpackcmd] 25 | [chaintool.subcommands.env :as envcmd] 26 | [chaintool.util :as util] 27 | [clojure.string :as string] 28 | [clojure.tools.cli :refer [parse-opts]] 29 | [slingshot.slingshot :as slingshot]) 30 | (:gen-class)) 31 | 32 | (defn option-merge [& args] (vec (apply concat args))) 33 | 34 | ;; options common to all modes, top-level as well as subcommands 35 | (def common-options 36 | [["-h" "--help"]]) 37 | 38 | (def toplevel-options 39 | (option-merge [["-v" "--version" "Print the version and exit"]] 40 | common-options)) 41 | 42 | ;; these options are common to subcommands that are expected to operate on a chaincode tree 43 | (def common-path-options 44 | (option-merge [["-p" "--path PATH" "path to chaincode project" :default "./"]] 45 | common-options)) 46 | 47 | (def subcommand-descriptors 48 | [{:name "build" :desc "Build the chaincode project" 49 | :handler buildcmd/run 50 | :options (option-merge [["-o" "--output NAME" "path to the output destination"]] 51 | common-path-options)} 52 | 53 | {:name "buildcar" :desc "Build the chaincode project from a CAR file" 54 | :handler buildcarcmd/run 55 | :arguments "path/to/file.car" 56 | :validate (fn [options arguments] (= (count arguments) 1)) 57 | :options (option-merge [["-o" "--output NAME" "path to the output destination"]] 58 | common-options)} 59 | 60 | {:name "deps" :desc "Download any missing dependencies for the project" 61 | :handler depscmd/run 62 | :options common-path-options} 63 | 64 | {:name "clean" :desc "Clean the chaincode project" 65 | :handler cleancmd/run 66 | :options common-path-options} 67 | 68 | {:name "package" :desc "Package the chaincode into a CAR file for deployment" 69 | :handler packagecmd/run 70 | :options (option-merge [["-o" "--output NAME" "path to the output destination"] 71 | ["-c" "--compress NAME" "compression algorithm to use" :default "gzip"]] 72 | common-path-options)} 73 | 74 | {:name "unpack" :desc "Unpackage a CAR file" 75 | :handler unpackcmd/run 76 | :arguments "path/to/file.car" 77 | :validate (fn [options arguments] (= (count arguments) 1)) 78 | :options (option-merge [["-d" "--directory NAME" "path to the output destination"]] 79 | common-options)} 80 | 81 | {:name "ls" :desc "List the contents of a CAR file" 82 | :handler lscmd/run 83 | :arguments "path/to/file.car" 84 | :validate (fn [options arguments] (= (count arguments) 1)) 85 | :options common-options} 86 | 87 | {:name "env" :desc "Display variables used in the build environment" 88 | :handler envcmd/run 89 | :options common-path-options} 90 | 91 | {:name "proto" :desc "Compiles a CCI file to a .proto" 92 | :handler protocmd/run 93 | :arguments "path/to/file.cci" 94 | :validate (fn [options arguments] (>= (count arguments) 1)) 95 | :options (option-merge [["-o" "--output NAME" "path to the output destination"]] 96 | common-options)} 97 | 98 | {:name "inspect" :desc "Retrieves metadata from a running instance" 99 | :handler inspectcmd/run 100 | :validate (fn [options arguments] (:name options)) 101 | :options (option-merge [[nil "--host HOST" "The API hostname of the running fabric" 102 | :default "localhost"] 103 | [nil "--port PORT" "The API port of the running fabric" 104 | :default 5000 105 | :parse-fn #(Integer/parseInt %) 106 | :validate [#(< 0 % 65536) "Must be a number between 0 and 65536"]] 107 | ["-n" "--name NAME" "The name of the chaincode instance"] 108 | ["-i" "--interfaces PATH" "retrieve interfaces from endpoint and saves them to PATH"]] 109 | common-options)}]) 110 | 111 | ;; N.B. the resulting map values are vectors each with a single map as an element 112 | ;; 113 | (def subcommands (group-by :name subcommand-descriptors)) 114 | 115 | (defn exit [status msg & rest] 116 | (do 117 | (apply println msg rest) 118 | status)) 119 | 120 | (defn version [] (str "chaintool version: v" util/app-version)) 121 | 122 | (defn prep-usage [msg] (->> msg flatten (string/join \newline))) 123 | 124 | (defn usage [options-summary] 125 | (prep-usage [(version) 126 | "" 127 | "Usage: chaintool [general-options] action [action-options]" 128 | "" 129 | "General Options:" 130 | options-summary 131 | "" 132 | "Actions:" 133 | (map (fn [[_ [{:keys [name desc]}]]] (str " " name " -> " desc)) subcommands) 134 | "" 135 | "(run \"chaintool -h\" for action specific help)"])) 136 | 137 | (defn subcommand-usage [subcommand options-summary] 138 | (prep-usage [(version) 139 | "" 140 | (str "Description: chaintool " (:name subcommand) " - " (:desc subcommand)) 141 | "" 142 | (str "Usage: chaintool " (:name subcommand) " [options] " (when-let [arguments (:arguments subcommand)] arguments)) 143 | "" 144 | "Command Options:" 145 | options-summary 146 | ""])) 147 | 148 | (defn -app [& args] 149 | (let [{:keys [options arguments errors summary]} (parse-opts args toplevel-options :in-order true)] 150 | (cond 151 | 152 | (:help options) 153 | (exit 0 (usage summary)) 154 | 155 | (not= errors nil) 156 | (exit -1 "Error: " (string/join errors)) 157 | 158 | (:version options) 159 | (exit 0 (version)) 160 | 161 | (zero? (count arguments)) 162 | (exit -1 (usage summary)) 163 | 164 | :else 165 | (if-let [[subcommand] (subcommands (first arguments))] 166 | (let [{:keys [options arguments errors summary]} (parse-opts (rest arguments) (:options subcommand))] 167 | (cond 168 | 169 | (:help options) 170 | (exit 0 (subcommand-usage subcommand summary)) 171 | 172 | (not= errors nil) 173 | (exit -1 "Error: " (string/join errors)) 174 | 175 | (and (:validate subcommand) (not ((:validate subcommand) options arguments))) 176 | (exit -1 (subcommand-usage subcommand summary)) 177 | 178 | :else 179 | (slingshot/try+ 180 | ((:handler subcommand) options arguments) 181 | (exit 0 "") 182 | (catch [:type :chaintoolabort] {:keys [msg retval]} 183 | (exit retval (str "Error: " msg)))))) 184 | 185 | ;; unrecognized subcommand 186 | (exit 1 (usage summary)))))) 187 | 188 | (defn -main [& args] 189 | (System/exit (apply -app args))) 190 | -------------------------------------------------------------------------------- /src/chaintool/inspect/core.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.inspect.core 16 | (:import [org.hyperledger.chaintool.meta 17 | OrgHyperledgerChaintoolMeta$GetInterfacesParams 18 | OrgHyperledgerChaintoolMeta$Interfaces 19 | OrgHyperledgerChaintoolMeta$InterfaceDescriptor 20 | OrgHyperledgerChaintoolMeta$GetFactsParams 21 | OrgHyperledgerChaintoolMeta$Facts]) 22 | (:require [clojure.java.io :as io] 23 | [clj-http.client :as http] 24 | [flatland.protobuf.core :as fl] 25 | [clojure.data.codec.base64 :as base64] 26 | [cheshire.core :as json] 27 | [chaintool.codecs :as codecs] 28 | [chaintool.util :as util] 29 | [doric.core :as doric])) 30 | 31 | (def GetInterfacesParams (fl/protodef OrgHyperledgerChaintoolMeta$GetInterfacesParams)) 32 | (def Interfaces (fl/protodef OrgHyperledgerChaintoolMeta$Interfaces)) 33 | (def InterfaceDescriptor (fl/protodef OrgHyperledgerChaintoolMeta$InterfaceDescriptor)) 34 | (def GetFactsParams (fl/protodef OrgHyperledgerChaintoolMeta$GetFactsParams)) 35 | (def Facts (fl/protodef OrgHyperledgerChaintoolMeta$Facts)) 36 | 37 | (defn- encode [item] (-> item fl/protobuf-dump base64/encode (String. "UTF-8"))) 38 | (defn- decode [type item] (->> item .getBytes base64/decode (fl/protobuf-load type))) 39 | 40 | (defn- url [{:keys [host port]}] 41 | (str "http://" host ":" port "/chaincode")) 42 | 43 | ;;-------------------------------------------------------------------------------------- 44 | ;; post - performs a synchronous http/jsonrpc to the server, evaluating to the response 45 | ;;-------------------------------------------------------------------------------------- 46 | (defn- post [{:keys [method name func args] :as options}] 47 | (let [body {:jsonrpc "2.0" 48 | :method method 49 | :params {:type 3 50 | :chaincodeID {:name name} 51 | :ctorMsg {:function func 52 | :args [(encode args)]}} 53 | :id "1"}] 54 | 55 | (http/post (url options) 56 | {:content-type :json 57 | :accept :json 58 | :form-params body}))) 59 | 60 | ;;-------------------------------------------------------------------------------------- 61 | ;; invokes a "query" operation on top of (post) and evaluates to a successful response 62 | ;; or throws an exception 63 | ;;-------------------------------------------------------------------------------------- 64 | (defn- query [args] 65 | (let [{:keys [body]} (post (assoc args :method "query")) 66 | response (-> body (json/parse-string true) (select-keys [:result :error]))] 67 | 68 | (if (= (-> response :result :status) "OK") 69 | (->> response :result :message) 70 | ;; else 71 | (util/abort -1 (str response))))) 72 | 73 | ;;-------------------------------------------------------------------------------------- 74 | ;; get-* operations invoke specific query operations 75 | ;;-------------------------------------------------------------------------------------- 76 | (defn- get-interfaces 77 | "gets all interface names declared, optionally with a request to include cci content" 78 | [{:keys [host port] :as options}] 79 | (let [response (query (assoc options 80 | :func "org.hyperledger.chaintool.meta/query/1" 81 | :args (fl/protobuf GetInterfacesParams :IncludeContent (some? (:interfaces options)))))] 82 | 83 | (decode Interfaces response))) 84 | 85 | (defn- get-facts 86 | "gets all fact name/value pairs from the running instance" 87 | [{:keys [host port] :as options}] 88 | (let [response (query (assoc options 89 | :func "org.hyperledger.chaintool.meta/query/3" 90 | :args (fl/protobuf GetFactsParams)))] 91 | 92 | (decode Facts response))) 93 | 94 | ;;-------------------------------------------------------------------------------------- 95 | (defn run 96 | "main entrypoint for inspection function" 97 | [options] 98 | 99 | (println "Connecting to" (url options)) 100 | 101 | (let [{:keys [facts]} (get-facts options) 102 | interfaces (get-interfaces options)] 103 | 104 | (println (doric/table [{:name :name :title "Fact"} {:name :value}] facts)) 105 | 106 | (println "Exported Interfaces:") 107 | (dorun (for [{:keys [name data]} (:descriptors interfaces)] 108 | (do 109 | (println "\t-" name) 110 | (when-let [path (:interfaces options)] 111 | (let [is (->> data .newInput (codecs/decompressor "gzip")) 112 | file (io/file path (str name ".cci"))] 113 | 114 | (io/make-parents file) 115 | (with-open [os (io/output-stream file)] 116 | (io/copy is os))))))))) 117 | -------------------------------------------------------------------------------- /src/chaintool/platforms/api.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.platforms.api) 16 | 17 | (defprotocol Platform 18 | ;; Displays environment variables relevant to the build environment 19 | (env [this params]) 20 | ;; Compiles the platform 21 | (build [this params]) 22 | ;; Downloads any missing deps for the platform 23 | (deps [this params]) 24 | ;; Cleans any previous builds of the platform 25 | (clean [this params]) 26 | ;; Packages the chaincode project according to the platform 27 | (package [this params])) 28 | -------------------------------------------------------------------------------- /src/chaintool/platforms/core.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.platforms.core 16 | (:require [chaintool.platforms.golang.system :as syscc] 17 | [chaintool.platforms.golang.userspace :as golang] 18 | [chaintool.util :as util]) 19 | (:refer-clojure :exclude [find])) 20 | 21 | (def factories 22 | {"org.hyperledger.chaincode.golang" golang/factory 23 | "org.hyperledger.chaincode.system" syscc/factory}) 24 | 25 | (defn find [config] 26 | (let [platform (:Platform config) 27 | name (:Name platform) 28 | version (:Version platform)] 29 | (if-let [factory (factories name)] 30 | (factory version) 31 | (util/abort -1 (str "Unknown platform type: \"" name "\""))))) 32 | -------------------------------------------------------------------------------- /src/chaintool/platforms/golang/system.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.platforms.golang.system 16 | (:require [chaintool.platforms.api :as platforms.api] 17 | [chaintool.platforms.golang.core :refer :all] 18 | [chaintool.util :as util] 19 | [clojure.java.io :as io] 20 | [clojure.java.shell :refer [sh]] 21 | [clojure.string :as string] 22 | [clojure.tools.file-utils :as fileutils]) 23 | (:refer-clojure :exclude [compile])) 24 | 25 | (defn get-package-name [path] 26 | (let [ {:keys [out err exit]} (sh "go" "list" :dir path)] 27 | (if (zero? exit) 28 | (string/trim-newline out) 29 | (util/abort -1 (str "\"go list\" returned: " err))))) 30 | 31 | (defn subtract-paths [fqpath relpath] 32 | (->> (string/replace (str fqpath) relpath "") io/file .getCanonicalPath str)) 33 | 34 | (defn get-fqp [path] 35 | (->> path io/file .getCanonicalPath)) 36 | 37 | (defn compute-gopath [path pkgname] 38 | (->> pkgname pkg-to-relpath (subtract-paths (get-fqp path)))) 39 | 40 | ;;----------------------------------------------------------------- 41 | ;; Supports "org.hyperledger.chaincode.system" platform, a golang 42 | ;; based environment for system chaincode applications. 43 | ;;----------------------------------------------------------------- 44 | (deftype GolangSystemPlatform [] 45 | platforms.api/Platform 46 | 47 | ;;----------------------------------------------------------------- 48 | ;; env - Emits the GOPATH used for building system chaincode 49 | ;;----------------------------------------------------------------- 50 | (env [_ _]) 51 | 52 | ;;----------------------------------------------------------------- 53 | ;; build - generates all golang platform artifacts within the 54 | ;; default location in the build area 55 | ;;----------------------------------------------------------------- 56 | (build [_ {:keys [path config output]}] 57 | (let [builddir "vendor" 58 | opath (io/file path builddir) 59 | pkgname (get-package-name path)] 60 | 61 | ;; ensure we clean up any previous runs 62 | (fileutils/recursive-delete opath) 63 | 64 | ;; run our code generator 65 | (generate {:base "hyperledger" 66 | :package pkgname 67 | :ipath (io/file path "interfaces") 68 | :opath opath 69 | :config config}) 70 | 71 | (println "Compilation complete"))) 72 | 73 | ;;----------------------------------------------------------------- 74 | ;; deps - not supported for system chaincode 75 | ;;----------------------------------------------------------------- 76 | (deps [_ {:keys [path]}] 77 | (util/abort -1 "unsupported platform operation: deps")) 78 | 79 | ;;----------------------------------------------------------------- 80 | ;; clean - cleans up any artifacts from a previous build, if any 81 | ;;----------------------------------------------------------------- 82 | (clean [_ {:keys [path]}] 83 | (fileutils/recursive-delete (io/file path "vendor"))) 84 | 85 | ;;----------------------------------------------------------------- 86 | ;; package - not supported for system chaincode 87 | ;;----------------------------------------------------------------- 88 | (package [_ _] 89 | (util/abort -1 "unsupported platform operation: package"))) 90 | 91 | (defn factory [version] 92 | (if (= version 1) 93 | (GolangSystemPlatform.) 94 | (util/abort -1 (str "Version " version " not supported")))) 95 | -------------------------------------------------------------------------------- /src/chaintool/protobuf/generate.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.protobuf.generate 16 | (:require [chaintool.build.interface :as intf] 17 | [chaintool.util :as util] 18 | [clojure.zip :as zip]) 19 | (:import (java.util ArrayList) 20 | (org.stringtemplate.v4 STGroupFile)) 21 | (:refer-clojure :exclude [compile])) 22 | 23 | ;; types to map to java objects that string template expects. 24 | ;; 25 | 26 | (deftype Field [^String modifier ^String type ^String name ^String index]) 27 | (deftype Definition [^String type ^String name ^ArrayList entries]) 28 | (deftype Entry [^Definition message ^Definition enum ^Field field ^Definition oneof]) 29 | (deftype Function [^String key ^String rettype ^String name ^String param]) 30 | 31 | (defn- typeconvert [[prefix name :as inType]] 32 | (cond 33 | 34 | (= prefix :map) (let [[_ keyType valueType] inType] 35 | (str "map" "<" (second keyType) "," (-> valueType second second) ">")) 36 | :else name)) 37 | 38 | (defn- find-toplevel-definitions [ast] 39 | (loop [loc (->> ast zip/down zip/right) defs []] 40 | (cond 41 | 42 | (nil? loc) 43 | defs 44 | 45 | :else 46 | (let [type (->> loc zip/down zip/node)] 47 | (recur (zip/right loc) 48 | (if (or (= type :message) (= type :enum)) 49 | (conj defs loc) 50 | defs)))))) 51 | 52 | (defn- getallfunctions [ast] 53 | (let [{:keys [functions]} (intf/getallfunctions ast)] 54 | (-> functions 55 | vals 56 | flatten))) 57 | 58 | ;;----------------------------------------------------------------- 59 | ;; buildX - build our ST friendly objects from the AST 60 | ;;----------------------------------------------------------------- 61 | (defn- build-msgfield [ast] 62 | (let [field (zip/down ast) 63 | {:keys [modifier type fieldName index]} (intf/getattrs field)] 64 | (->Entry nil nil (->Field modifier (typeconvert type) fieldName index) nil))) 65 | 66 | (defn- build-oneoffield [ast] 67 | (let [field (zip/down ast) 68 | {:keys [modifier type fieldName index]} (intf/getattrs field)] 69 | (->Field modifier (typeconvert type) fieldName index))) 70 | 71 | (defn- build-enumfield [ast] 72 | (let [field (zip/down ast) 73 | name (->> field zip/right zip/node) 74 | index (->> field zip/right zip/right zip/node)] 75 | (->Field nil nil name index))) 76 | 77 | (declare build-message) 78 | (declare build-enum) 79 | (declare build-oneof) 80 | (defn- build-subentry [ast] 81 | (let [type (->> ast zip/down zip/node)] 82 | (case type 83 | :message (build-message ast) 84 | :enum (build-enum ast) 85 | :field (build-msgfield ast) 86 | :oneof (build-oneof ast)))) 87 | 88 | (defn- build-message [ast] 89 | (let [elem (zip/down ast) 90 | name (->> elem zip/right zip/node) 91 | first (->> elem zip/right zip/right) 92 | entries (loop [loc first entries {} index 0] 93 | (cond 94 | 95 | (nil? loc) 96 | entries 97 | 98 | :else 99 | (recur (zip/right loc) (assoc entries index (build-subentry loc)) (inc index))))] 100 | (->Entry (->Definition "message" name entries) nil nil nil))) 101 | 102 | (defn- build-enum [ast] 103 | (let [elem (zip/down ast) 104 | name (->> elem zip/right zip/node) 105 | first (->> elem zip/right zip/right) 106 | entries (loop [loc first entries {} index 0] 107 | (cond 108 | 109 | (nil? loc) 110 | entries 111 | 112 | :else 113 | (recur (zip/right loc) (assoc entries index (build-enumfield loc)) (inc index))))] 114 | (->Entry nil (->Definition "enum" name entries) nil nil))) 115 | 116 | (defn- build-oneof [ast] 117 | (let [elem (zip/down ast) 118 | name (->> elem zip/right zip/node) 119 | first (->> elem zip/right zip/right) 120 | entries (loop [loc first entries {} index 0] 121 | (cond 122 | 123 | (nil? loc) 124 | entries 125 | 126 | :else 127 | (recur (zip/right loc) (assoc entries index (build-oneoffield loc)) (inc index))))] 128 | (->Entry nil nil nil (->Definition "oneof" name entries)))) 129 | 130 | (defn- build-toplevel-entry [ast] 131 | (let [type (->> ast zip/down zip/node)] 132 | (case type 133 | :message (build-message ast) 134 | :enum (build-enum ast)))) 135 | 136 | (defn- build-toplevel-entries [ast] 137 | (->> ast 138 | find-toplevel-definitions 139 | (mapv build-toplevel-entry) 140 | (interleave (range)) 141 | (partition 2) 142 | (mapv vec) 143 | (into {}))) 144 | 145 | (defn- buildfunction [name {:keys [rettype functionName param index] :as ast}] 146 | (let [key (str name "/fcn/" index)] 147 | (->Function key rettype functionName param))) 148 | 149 | (defn- buildfunctions [name ast] 150 | (let [funcs (map #(buildfunction name %) (getallfunctions ast))] 151 | (into {} (map #(vector (.key %) %) funcs)))) 152 | 153 | ;;----------------------------------------------------------------- 154 | ;; to-string - compiles the interface into a protobuf 155 | ;; specification in a string, suitable for writing to a file or 156 | ;; passing to protoc 157 | ;;----------------------------------------------------------------- 158 | (defn to-string [package [name ast]] 159 | (let [definitions (build-toplevel-entries ast) 160 | functions (buildfunctions name ast) 161 | stg (STGroupFile. "generators/proto.stg") 162 | template (.getInstanceOf stg "protobuf")] 163 | 164 | (.add template "package" (if (nil? package) name package)) 165 | (.add template "definitions" definitions) 166 | (.add template "functions" functions) 167 | (.render template))) 168 | 169 | ;;----------------------------------------------------------------- 170 | ;; to-file - generates a protobuf specification and writes 171 | ;; it to a file 172 | ;;----------------------------------------------------------------- 173 | (defn to-file [filename package interface] 174 | (util/truncate-file filename (to-string package interface))) 175 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/build.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | (ns chaintool.subcommands.build 15 | (:require [clojure.java.io :as io] 16 | [chaintool.config.util :as config.util] 17 | [chaintool.build.core :as build.core])) 18 | 19 | (defn getoutput [options path config] 20 | (if-let [output (:output options)] 21 | (io/file output) 22 | (io/file path "build/bin" (config.util/compositename config)))) 23 | 24 | (defn run [options args] 25 | (let [[path config] (config.util/load-from-options options) 26 | output (getoutput options path config)] 27 | (println "Build using configuration for " path) 28 | (build.core/compile {:path path :config config :output output}))) 29 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/buildcar.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | (ns chaintool.subcommands.buildcar 15 | (:require [clojure.java.io :as io] 16 | [me.raynes.fs :as fs] 17 | [clojure.tools.file-utils :as fileutils] 18 | [chaintool.util :as util] 19 | [chaintool.config.util :as config.util] 20 | [chaintool.car.read :as car.read] 21 | [chaintool.car.unpack :as car.unpack] 22 | [chaintool.build.core :as build.core])) 23 | 24 | (defn getoutput [options] 25 | (if-let [output (:output options)] 26 | (io/file output) 27 | (util/abort -1 "Missing -o output (see -h for details)"))) 28 | 29 | (defn run [options args] 30 | (let [output (getoutput options) 31 | file (io/file (first args)) 32 | {:keys [index config]} (with-open [is (io/input-stream file)] (car.read/read is)) 33 | workingdir (fs/temp-dir "buildcar-")] 34 | 35 | (car.unpack/unpack index workingdir :false) 36 | (let [config (config.util/load workingdir)] 37 | (println "Building CAR" (.getCanonicalPath file)) 38 | (build.core/compile {:path workingdir :config config :output output}) 39 | (fileutils/recursive-delete (io/file workingdir))))) 40 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/clean.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | (ns chaintool.subcommands.clean 15 | (:require [chaintool.config.util :as config.util] 16 | [chaintool.platforms.core :as platforms.core] 17 | [chaintool.platforms.api :as platforms.api])) 18 | 19 | (defn run [options args] 20 | (let [[path config] (config.util/load-from-options options)] 21 | (when-let [platform (platforms.core/find config)] 22 | (println "Cleaning project found at " path) 23 | (platforms.api/clean platform {:path path})))) 24 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/deps.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright Greg Haskins, 2017 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | (ns chaintool.subcommands.deps 15 | (:require [chaintool.config.util :as config.util] 16 | [chaintool.platforms.core :as platforms.core] 17 | [chaintool.platforms.api :as platforms.api])) 18 | 19 | (defn run [options args] 20 | (let [[path config] (config.util/load-from-options options)] 21 | (when-let [platform (platforms.core/find config)] 22 | (platforms.api/deps platform {:path path :config config})))) 23 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/env.clj: -------------------------------------------------------------------------------- 1 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 2 | ;; you may not use this file except in compliance with the License. 3 | ;; You may obtain a copy of the License at 4 | ;; 5 | ;; http://www.apache.org/licenses/LICENSE-2.0 6 | ;; 7 | ;; Unless required by applicable law or agreed to in writing, software 8 | ;; distributed under the License is distributed on an "AS IS" BASIS, 9 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | ;; See the License for the specific language governing permissions and 11 | ;; limitations under the License. 12 | 13 | (ns chaintool.subcommands.env 14 | (:require [chaintool.config.util :as config.util] 15 | [chaintool.build.core :as build.core])) 16 | 17 | (defn run [options args] 18 | (let [[path config] (config.util/load-from-options options)] 19 | (build.core/env {:path path :config config}))) 20 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/inspect.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.subcommands.inspect 16 | (:require [chaintool.inspect.core :as inspect] 17 | [clojure.java.io :as io])) 18 | 19 | (defn getoutputdir [options] 20 | (if-let [path (:interfaces options)] 21 | (io/file path) 22 | (io/file "."))) 23 | 24 | (defn run [options args] 25 | (let [output (getoutputdir options)] 26 | (inspect/run (assoc options :output output)))) 27 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/ls.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.subcommands.ls 16 | (:require [chaintool.car.ls :as car] 17 | [clojure.java.io :as io])) 18 | 19 | (defn run [options args] 20 | (let [file (io/file (first args))] 21 | (car/ls file))) 22 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/package.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | (ns chaintool.subcommands.package 15 | (:require [chaintool.config.util :as config] 16 | [chaintool.platforms.core :as platforms.core] 17 | [chaintool.platforms.api :as platforms.api] 18 | [clojure.java.io :as io] 19 | [clojure.tools.cli :refer [parse-opts]])) 20 | 21 | (defn getoutputfile [options path config] 22 | (if-let [output (:output options)] 23 | (io/file output) 24 | (io/file path "build" (str (config/compositename config) ".car")))) 25 | 26 | (defn run [options args] 27 | (let [[path config] (config/load-from-options options) 28 | compressiontype (:compress options) 29 | outputfile (getoutputfile options path config) 30 | platform (platforms.core/find config)] 31 | 32 | (platforms.api/package platform {:path path 33 | :config config 34 | :compressiontype compressiontype 35 | :outputfile outputfile}))) 36 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/proto.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.subcommands.proto 16 | (:require [chaintool.build.interface :as intf] 17 | [chaintool.protobuf.generate :as pb] 18 | [chaintool.util :as util] 19 | [clojure.java.io :as io] 20 | [me.raynes.fs :as fs] 21 | [clojure.string :as str])) 22 | 23 | (defn getoutputfile [options input name] 24 | (if-let [output (:output options)] 25 | (io/file (str output (when-not (str/ends-with? output "/") "/") name ".proto")) 26 | (io/file (str name ".proto")))) 27 | 28 | (defn run [options args] 29 | (doseq [raw_input args] 30 | (let [input (io/file raw_input) 31 | name (fs/base-name input true) 32 | output (getoutputfile options input name) 33 | intf (intf/compileintf {:path (.getCanonicalPath input) :data (util/safe-slurp input)})] 34 | (pb/to-file output name [name intf])))) 35 | -------------------------------------------------------------------------------- /src/chaintool/subcommands/unpack.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.subcommands.unpack 16 | (:require [chaintool.config.util :as config] 17 | [chaintool.util :as util] 18 | [chaintool.car.read :as car.read] 19 | [chaintool.car.unpack :as car.unpack] 20 | [clojure.java.io :as io])) 21 | 22 | (defn getoutputdir [options config] 23 | (if-let [dir (:directory options)] 24 | (io/file dir) 25 | (io/file "./" (str (config/compositename config))))) 26 | 27 | (defn run [options args] 28 | (let [file (io/file (first args)) 29 | {:keys [index config]} (with-open [is (io/input-stream file)] (car.read/read is)) 30 | outputdir (getoutputdir options config)] 31 | 32 | (when (.exists outputdir) 33 | (util/abort -1 (str "output directory " (.getCanonicalPath outputdir) " exists"))) 34 | 35 | (println "Unpacking CAR to:" (.getCanonicalPath outputdir)) 36 | (println) 37 | (car.unpack/unpack index outputdir :true))) 38 | -------------------------------------------------------------------------------- /src/chaintool/util.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.util 16 | (:require [clojure.java.io :as io] 17 | [slingshot.slingshot :as slingshot])) 18 | 19 | (def app-version (System/getProperty "chaintool.version")) 20 | 21 | (defn truncate-file [filename content] 22 | 23 | ;; ensure the path exists 24 | (io/make-parents filename) 25 | 26 | ;; and blast it out to the filesystem 27 | (spit filename content :truncate true)) 28 | 29 | ;; throws an exception that should unwind us all the way to the core/main 30 | ;; function and exit cleanly with an error message rather than a stacktrace, etc 31 | (defn abort [retval msg] 32 | (slingshot/throw+ {:type :chaintoolabort :retval retval :msg msg})) 33 | 34 | (defn safe-slurp [file] 35 | (if (.exists file) 36 | (slurp file) 37 | (abort -1 (str (.getCanonicalPath file) " not found")))) 38 | -------------------------------------------------------------------------------- /test/chaintool/build/test_interface.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.build.test_interface 16 | (:require [clojure.test :refer :all] 17 | [chaintool.build.interface :refer :all] 18 | [chaintool.ast :as ast] 19 | [slingshot.slingshot :as slingshot] 20 | [clojure.pprint :refer [pprint]] 21 | [clojure.zip :as zip]) 22 | (:refer-clojure :exclude [compile])) 23 | 24 | (def example1-cci 25 | " 26 | # This is a comment 27 | // So is this 28 | 29 | message ActiveMessage { 30 | // mid-message comment 31 | string param1 = 1; # trailing comment 32 | int32 param2 = 2; 33 | int64 param3 = 3; 34 | } 35 | 36 | //message CommentedMessage { 37 | // int32 param1 = 1; 38 | //} 39 | ") 40 | 41 | (def example1-expected-result 42 | [[:interface [:message "ActiveMessage" [:field [:type [:scalar "string"]] [:fieldName "param1"] [:index "1"]] [:field [:type [:scalar "int32"]] [:fieldName "param2"] [:index "2"]] [:field [:type [:scalar "int64"]] [:fieldName "param3"] [:index "3"]]]] nil]) 43 | 44 | (def example2-cci 45 | " 46 | message NestedMessage { 47 | message Entry { 48 | string key = 1; 49 | int32 value = 2; 50 | } 51 | 52 | repeated Entry entries = 1; 53 | } 54 | 55 | ") 56 | 57 | (def example2-expected-result 58 | [[:interface [:message "NestedMessage" [:message "Entry" [:field [:type [:scalar "string"]] [:fieldName "key"] [:index "1"]] [:field [:type [:scalar "int32"]] [:fieldName "value"] [:index "2"]]] [:field [:modifier "repeated"] [:type [:userType "Entry"]] [:fieldName "entries"] [:index "1"]]]] nil]) 59 | 60 | (deftest test-parser-output 61 | (is (= example1-expected-result (parse example1-cci))) 62 | (is (= example2-expected-result (parse example2-cci)))) 63 | 64 | (def map-example-cci 65 | " 66 | message MapMessage { 67 | map testmap = 1; 68 | } 69 | " 70 | ) 71 | 72 | (def map-example-expected-result 73 | [[:interface [:message "MapMessage" [:field [:type [:map [:keyType "string"] [:type [:scalar "string"]]]] [:fieldName "testmap"] [:index "1"]]]] nil]) 74 | 75 | 76 | (deftest test-map-parser-output 77 | (is (= map-example-expected-result (parse map-example-cci)))) 78 | 79 | (def oneof-example-cci 80 | " 81 | message OneofTest { 82 | 83 | oneof Test { 84 | string account = 4; 85 | double loan = 5; 86 | } 87 | 88 | } 89 | " 90 | ) 91 | 92 | (def oneof-example-expected-result 93 | [[ :interface [ :message "OneofTest" [ :oneof "Test" [ :field [ :type [ :scalar "string" ] ] [ :fieldName "account" ] [ :index "4" ] ] [ :field [ :type [ :scalar "double" ] ] [ :fieldName "loan" ] [ :index "5" ] ] ] ] ] nil]) 94 | 95 | (deftest test-oneof-parser-output 96 | (is (= oneof-example-expected-result (parse oneof-example-cci)))) 97 | 98 | (def example-undefined-type-cci 99 | " 100 | message BadMessage { 101 | message Entry { 102 | string key = 1; 103 | UnknownType value = 2; 104 | } 105 | 106 | repeated Entry entries = 1; 107 | } 108 | 109 | ") 110 | 111 | (deftest test-parser-validation 112 | (let [intf (parse example-undefined-type-cci)] 113 | (is (some? (verify-intf intf))))) 114 | 115 | (def example-type-resolution 116 | " 117 | message Party { 118 | string entity = 1; 119 | int32 value = 2; 120 | } 121 | 122 | message Init { 123 | Party partyA = 1; 124 | Party partyB = 2; 125 | } 126 | ") 127 | 128 | (deftest test-type-resolution 129 | (let [intf (parse example-type-resolution)] 130 | (is (nil? (verify-intf intf))))) 131 | 132 | (def example-conflicting-index 133 | " 134 | message Conflict { 135 | string foo = 1; 136 | int32 bar = 2; 137 | int32 baz = 1; 138 | } 139 | 140 | ") 141 | 142 | (deftest test-conflict-detection 143 | (let [intf (parse example-conflicting-index)] 144 | (is (some? (verify-intf intf))))) 145 | 146 | (def example-enum 147 | " 148 | enum MyEnum { 149 | ZERO = 0; 150 | ONE = 1; 151 | TWO = 2; 152 | } 153 | 154 | ") 155 | 156 | (deftest test-enum 157 | (let [intf (parse example-enum)] 158 | (is (nil? (verify-intf intf))))) 159 | 160 | (def example-conflicting-enum 161 | " 162 | enum ConflictingEnum { 163 | ZERO = 0; 164 | ONE = 1; 165 | TWO = 1; 166 | } 167 | 168 | ") 169 | 170 | (deftest test-conflicting-enum 171 | (let [intf (parse example-conflicting-enum)] 172 | (is (some? (verify-intf intf))))) 173 | 174 | (def example-bad-default-enum 175 | " 176 | enum BadDefaultEnum { 177 | ZERO = 1; 178 | ONE = 2; 179 | TWO = 3; 180 | } 181 | 182 | ") 183 | 184 | (deftest test-bad-default-enum 185 | (let [intf (parse example-bad-default-enum)] 186 | (is (some? (verify-intf intf))))) 187 | 188 | (def example-no-parameters 189 | " 190 | functions { 191 | string Parameterless() = 1; 192 | } 193 | 194 | ") 195 | 196 | (deftest test-no-parameters 197 | (let [intf (parse example-no-parameters)] 198 | (is (nil? (verify-intf intf))))) 199 | 200 | (def example-comment-before-msg 201 | " 202 | message Foo {} 203 | message Bar { 204 | // This comment should be fine 205 | string baz = 1; 206 | // This comment should not break the parser 207 | Foo my_foo = 2; 208 | } 209 | ") 210 | 211 | (deftest test-comment-before-msg 212 | (let [intf (parse example-comment-before-msg)] 213 | (is (nil? (verify-intf intf))))) 214 | 215 | (def fabct-53-msg ;; https://jira.hyperledger.org/browse/FABCT-53 216 | "message Foo { 217 | enum MyEnum { a = 0;} 218 | } 219 | 220 | message Bar { 221 | Foo.MyEnum my_field = 1; 222 | } 223 | ") 224 | 225 | (defn find-usertype [x] 226 | (->> (ast/find :userType x) zip/node second)) 227 | 228 | (deftest test-fabct-53 229 | (testing "Ensure we can cross-reference nested definitions" 230 | (let [intf (parse fabct-53-msg)] 231 | (is (nil? (verify-intf intf))) 232 | (is (= (find-usertype intf) "Foo.MyEnum"))))) 233 | -------------------------------------------------------------------------------- /test/chaintool/platforms/golang/test_core.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.platforms.golang.test_core 16 | (:require [clojure.test :refer :all] 17 | [chaintool.platforms.golang.core :refer :all]) 18 | (:refer-clojure :exclude [compile])) 19 | 20 | (deftest test-buildgopath 21 | (let [gopath (buildgopath "foo")] 22 | (is (some? gopath)))) 23 | -------------------------------------------------------------------------------- /test/chaintool/platforms/golang/test_system.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.platforms.golang.test_system 16 | (:require [clojure.test :refer :all] 17 | [chaintool.platforms.golang.system :refer :all]) 18 | (:refer-clojure :exclude [compile])) 19 | 20 | (deftest test-standalone-gopath-computation 21 | (let [gopath (compute-gopath "/local-dev/git/chaintool/testdata/sample_syscc" 22 | "_/local-dev/git/chaintool/testdata/sample_syscc")] 23 | (is (= gopath "/")))) 24 | 25 | (deftest test-gopath-computation 26 | (let [gopath (compute-gopath "/opt/gopath/src/github.com/hyperledger/fabric/core/system_chaincode/sample" 27 | "github.com/hyperledger/fabric/core/system_chaincode/sample")] 28 | (is (= gopath "/opt/gopath/src")))) 29 | -------------------------------------------------------------------------------- /test/chaintool/protobuf/test_generate.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright London Stock Exchange Group 2016 All Rights Reserved. 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | (ns chaintool.protobuf.test_generate 16 | (:require [clojure.test :refer :all] 17 | [clojure.java.io :as io] 18 | [clojure.zip :as zip] 19 | [instaparse.core :as insta] 20 | [chaintool.ast :as ast] 21 | [chaintool.protobuf.generate :as pb] 22 | [slingshot.slingshot :as slingshot]) 23 | (:refer-clojure :exclude [compile])) 24 | 25 | (def skipper (insta/parser (io/resource "parsers/proto/skip.bnf"))) 26 | (def grammar (insta/parser (io/resource "parsers/proto/grammar.bnf") :auto-whitespace skipper)) 27 | 28 | (defn- parse [intf] 29 | (let [result (insta/add-line-and-column-info-to-metadata intf (grammar intf))] 30 | (if (insta/failure? result) 31 | (let [{:keys [line column text]} result] 32 | (str "could not parse \"" text "\": line=" line " column=" column)) 33 | (zip/vector-zip result)))) 34 | 35 | (defn- find 36 | "Finds an arbitrary item in the tree based on (pred)" 37 | [ast pred] 38 | (loop [loc ast] 39 | (cond 40 | 41 | (or (nil? loc) (zip/end? loc)) 42 | nil 43 | 44 | (pred loc) 45 | loc 46 | 47 | :else 48 | (recur (zip/next loc))))) 49 | 50 | (defn- tree-depth 51 | "Counts the zipper depth of the node represented by ast" 52 | [ast] 53 | (loop [loc ast depth 0] 54 | (cond 55 | 56 | (or (nil? loc) (zip/end? loc)) 57 | depth 58 | 59 | :else 60 | (recur (zip/up loc) (inc depth))))) 61 | 62 | (defn- round-about 63 | "takes an AST, exports it to protobuf, and then reparses the protobuf back to a new AST" 64 | [ast] 65 | (let [pb (->> ["fictional.interface" ast] (pb/to-string "fictional.package"))] 66 | (parse pb))) 67 | 68 | (def nested-input 69 | (zip/vector-zip 70 | [:interface 71 | [:message "NestedMessage" 72 | [:message "Entry" 73 | [:field [:type [:scalar "string"]] [:fieldName "key"] [:index "1"]] 74 | [:field [:type [:scalar "int32"]] [:fieldName "value"] [:index "2"]]] 75 | [:field [:modifier "repeated"] [:type [:userType "Entry"]] [:fieldName "entries"] [:index "1"]] 76 | [:message "Level1" 77 | [:message "Level2" 78 | [:message "Level3" 79 | [:message "Level4"]]]]]])) 80 | 81 | (deftest nested-messages 82 | (let [result (round-about nested-input) 83 | level4 (find result (fn [loc] (= (zip/node loc) "Level4")))] 84 | (is level4) 85 | (is (= (tree-depth level4) 7)))) 86 | 87 | (def enum-input 88 | (zip/vector-zip 89 | [:interface [:enum "MyEnum" [:enumField "ZERO" 0] [:enumField "ONE" 1] [:enumField "TWO" 2]]])) 90 | 91 | (deftest enum-test 92 | (let [result (round-about enum-input)] 93 | (is (= (->> result zip/down zip/node) :proto)) 94 | (is (= (->> result (ast/find :syntax) zip/down zip/right zip/node) "proto3")) 95 | (is (= (->> result (ast/find :enum) zip/down zip/right zip/right zip/node) [:enumField "ZERO" "0"])))) 96 | 97 | (def map-input 98 | (zip/vector-zip 99 | [:interface 100 | [:message "MapMessage" 101 | [:field 102 | [:type 103 | [:map 104 | [:keyType "string"] 105 | [:type [:scalar "string"]]]] 106 | [:fieldName "testmap"] [:index "1"]]]])) 107 | 108 | (deftest map-test 109 | (let [result (round-about map-input)] 110 | (is (= (->> result zip/down zip/node) :proto )) 111 | (is (= (->> result (ast/find :syntax) zip/down zip/right zip/node) "proto3") 112 | (is (= (->> result (ast/find :map) zip/down zip/right zip/node) [:keyType "string"]))))) 113 | 114 | (def parameterless-function 115 | (zip/vector-zip 116 | [:interface [:functions [:function [:rettype "string"] [:functionName "Parameterless"] [:index "1"]]]])) 117 | 118 | (deftest parameterless-test 119 | (let [result (round-about parameterless-function)] 120 | (some? result))) 121 | --------------------------------------------------------------------------------