diff --git a/.github/settings.yml b/.github/settings.yml
new file mode 100644
index 0000000..85a3d0d
--- /dev/null
+++ b/.github/settings.yml
@@ -0,0 +1,14 @@
+repository:
+ name: fabric-samples
+ description: null
+ homepage: https://wiki.hyperledger.org/display/fabric
+ default_branch: master
+ has_downloads: true
+ has_issues: false
+ has_projects: false
+ has_wiki: false
+ archived: false
+ private: false
+ allow_squash_merge: true
+ allow_merge_commit: false
+ allow_rebase_merge: true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e4f9fd4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+# Emacs backup files
+*~
+*#
+.#*
+# Vim file artifacts
+.*.sw*
+# installed platform-specific binaries
+/bin
+/config
+.DS_Store
+.project
+# omit Go vendor directories
+vendor/
+.vscode
+.gradle
+.idea
+# Dependency directories
+node_modules/
+# Ignore Gradle build output directory
+build
+package-lock.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..2f6bf03
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,2660 @@
+## "v2.1.0", "v2.0.1", "v2.0.0"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+
+## "v1.4.6"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+
+## "v1.4.5"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+
+## "v1.4.4"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+
+## "v1.4.3"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+
+## "v1.4.2"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+
+## "v1.4.1"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+
+## "v1.4.0"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [8a458b5](https://github.com/hyperledger/fabric-samples/commit/8a458b5) [FAB-12056](https://jira.hyperledger.org/browse/FAB-12056) Private marbles cc use transient data
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+* [461b6ab](https://github.com/hyperledger/fabric-samples/commit/461b6ab) FABC-781 Remove fabric-ca sample
+* [e9b9477](https://github.com/hyperledger/fabric-samples/commit/e9b9477) [FAB-13372](https://jira.hyperledger.org/browse/FAB-13372) Fabric-Samples return error msg
+* [e3da220](https://github.com/hyperledger/fabric-samples/commit/e3da220) [FAB-13433](https://jira.hyperledger.org/browse/FAB-13433) - Update Jenkinsfile configuration
+* [33db64e](https://github.com/hyperledger/fabric-samples/commit/33db64e) Configure Stale ProBot
+* [5cd277f](https://github.com/hyperledger/fabric-samples/commit/5cd277f) [FAB-11951](https://jira.hyperledger.org/browse/FAB-11951) Interest-rate swap example for SBE
+* [9567985](https://github.com/hyperledger/fabric-samples/commit/9567985) [FAB-13407](https://jira.hyperledger.org/browse/FAB-13407) Align fabric-samples with 1.4.0-rc2 release
+* [c7572aa](https://github.com/hyperledger/fabric-samples/commit/c7572aa) [FAB-13305](https://jira.hyperledger.org/browse/FAB-13305) Update scripts to pull latest artifacts
+* [ab46e35](https://github.com/hyperledger/fabric-samples/commit/ab46e35) [FAB-13283](https://jira.hyperledger.org/browse/FAB-13283) Update sample code for commercial paper
+* [f677821](https://github.com/hyperledger/fabric-samples/commit/f677821) [FAB-13232](https://jira.hyperledger.org/browse/FAB-13232) fix peer node start command
+* [6a7472e](https://github.com/hyperledger/fabric-samples/commit/6a7472e) [FAB-13126](https://jira.hyperledger.org/browse/FAB-13126) Align fabric-samples with 1.4.0-rc1 release
+* [445ccbc](https://github.com/hyperledger/fabric-samples/commit/445ccbc) [FAB-12880](https://jira.hyperledger.org/browse/FAB-12880) Move old prog model samples for FabCar
+* [5be62b5](https://github.com/hyperledger/fabric-samples/commit/5be62b5) [FAB-13207](https://jira.hyperledger.org/browse/FAB-13207) Remove incorrect discovery options
+* [fdbd92d](https://github.com/hyperledger/fabric-samples/commit/fdbd92d) [FAB-13206](https://jira.hyperledger.org/browse/FAB-13206) Remove dependencies on fabric-client
+* [eff0046](https://github.com/hyperledger/fabric-samples/commit/eff0046) [FAB-12877](https://jira.hyperledger.org/browse/FAB-12877) Add fabcar app using new prog model (JS)
+* [c184196](https://github.com/hyperledger/fabric-samples/commit/c184196) [FAB-12878](https://jira.hyperledger.org/browse/FAB-12878) Add fabcar app using new prog model (TS)
+* [e1a39e6](https://github.com/hyperledger/fabric-samples/commit/e1a39e6) [FAB-12724](https://jira.hyperledger.org/browse/FAB-12724) Upgrade from 1.3.x to 1.4.0
+* [c21bbba](https://github.com/hyperledger/fabric-samples/commit/c21bbba) Update samples to use new logging env variables
+* [7ad9f19](https://github.com/hyperledger/fabric-samples/commit/7ad9f19) [FAB-13011](https://jira.hyperledger.org/browse/FAB-13011) add kafka consensus type to byfn sample
+* [33f064f](https://github.com/hyperledger/fabric-samples/commit/33f064f) [FAB-13170](https://jira.hyperledger.org/browse/FAB-13170) Add memberOnlyRead to marbles sample
+* [928b72b](https://github.com/hyperledger/fabric-samples/commit/928b72b) [FAB-12875](https://jira.hyperledger.org/browse/FAB-12875) Add automated tests for fabcar sample
+* [5c087f1](https://github.com/hyperledger/fabric-samples/commit/5c087f1) [FAB-13046](https://jira.hyperledger.org/browse/FAB-13046) Update TypeScript contract dependencies
+* [3748983](https://github.com/hyperledger/fabric-samples/commit/3748983) [FAB-12879](https://jira.hyperledger.org/browse/FAB-12879) Update fabcar script for new contracts
+* [4fb3b57](https://github.com/hyperledger/fabric-samples/commit/4fb3b57) [FAB-12852](https://jira.hyperledger.org/browse/FAB-12852) Add fabcar contract w/ new prog model (TS)
+* [9facb42](https://github.com/hyperledger/fabric-samples/commit/9facb42) [FAB-12851](https://jira.hyperledger.org/browse/FAB-12851) Add fabcar contract w/ new prog model (JS)
+* [e67fcf1](https://github.com/hyperledger/fabric-samples/commit/e67fcf1) [FAB-12322](https://jira.hyperledger.org/browse/FAB-12322) Update commercial-paper sample
+* [fd6e2c4](https://github.com/hyperledger/fabric-samples/commit/fd6e2c4) [FAB-12703](https://jira.hyperledger.org/browse/FAB-12703) Fix misspelling "lauches"
+* [c05f172](https://github.com/hyperledger/fabric-samples/commit/c05f172) [FAB-12608](https://jira.hyperledger.org/browse/FAB-12608) Update pipeline script
+* [286861e](https://github.com/hyperledger/fabric-samples/commit/286861e) [FAB-12371](https://jira.hyperledger.org/browse/FAB-12371)Fix the abac sample to use new cid package
+* [24c5e47](https://github.com/hyperledger/fabric-samples/commit/24c5e47) [FAB-12026](https://jira.hyperledger.org/browse/FAB-12026) pagination samples for node marbles02
+* [6dc5ce5](https://github.com/hyperledger/fabric-samples/commit/6dc5ce5) [FAB-12587](https://jira.hyperledger.org/browse/FAB-12587) Fix for Query Block by block hash API
+* [df311ce](https://github.com/hyperledger/fabric-samples/commit/df311ce) [FAB-12173](https://jira.hyperledger.org/browse/FAB-12173) balance-transfer: Update anchor peers
+* [c925148](https://github.com/hyperledger/fabric-samples/commit/c925148) [FAB-12415](https://jira.hyperledger.org/browse/FAB-12415) samples for 1.3.0 (master cleanup)
+* [3a12c60](https://github.com/hyperledger/fabric-samples/commit/3a12c60) [FAB-12275](https://jira.hyperledger.org/browse/FAB-12275) Fix the warn in creating genesis block
+* [c6f6324](https://github.com/hyperledger/fabric-samples/commit/c6f6324) [FAB-12257](https://jira.hyperledger.org/browse/FAB-12257) allow balance-transfer for doscovery
+* [4445e8d](https://github.com/hyperledger/fabric-samples/commit/4445e8d) [FAB-12272](https://jira.hyperledger.org/browse/FAB-12272) Increase MAX_RETRY to 10
+* [33d333f](https://github.com/hyperledger/fabric-samples/commit/33d333f) [FAB-12190](https://jira.hyperledger.org/browse/FAB-12190) Update stable version in CI scripts
+* [4089786](https://github.com/hyperledger/fabric-samples/commit/4089786) [FAB-11867](https://jira.hyperledger.org/browse/FAB-11867) Develop Apps:Sample pt 2 -- application
+* [d776651](https://github.com/hyperledger/fabric-samples/commit/d776651) [FAB-11723](https://jira.hyperledger.org/browse/FAB-11723) Developing Apps: Sample pt 1 -- contract
+
+## "v1.3.0"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [8a458b5](https://github.com/hyperledger/fabric-samples/commit/8a458b5) [FAB-12056](https://jira.hyperledger.org/browse/FAB-12056) Private marbles cc use transient data
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+* [461b6ab](https://github.com/hyperledger/fabric-samples/commit/461b6ab) FABC-781 Remove fabric-ca sample
+* [e9b9477](https://github.com/hyperledger/fabric-samples/commit/e9b9477) [FAB-13372](https://jira.hyperledger.org/browse/FAB-13372) Fabric-Samples return error msg
+* [e3da220](https://github.com/hyperledger/fabric-samples/commit/e3da220) [FAB-13433](https://jira.hyperledger.org/browse/FAB-13433) - Update Jenkinsfile configuration
+* [33db64e](https://github.com/hyperledger/fabric-samples/commit/33db64e) Configure Stale ProBot
+* [5cd277f](https://github.com/hyperledger/fabric-samples/commit/5cd277f) [FAB-11951](https://jira.hyperledger.org/browse/FAB-11951) Interest-rate swap example for SBE
+* [9567985](https://github.com/hyperledger/fabric-samples/commit/9567985) [FAB-13407](https://jira.hyperledger.org/browse/FAB-13407) Align fabric-samples with 1.4.0-rc2 release
+* [c7572aa](https://github.com/hyperledger/fabric-samples/commit/c7572aa) [FAB-13305](https://jira.hyperledger.org/browse/FAB-13305) Update scripts to pull latest artifacts
+* [ab46e35](https://github.com/hyperledger/fabric-samples/commit/ab46e35) [FAB-13283](https://jira.hyperledger.org/browse/FAB-13283) Update sample code for commercial paper
+* [f677821](https://github.com/hyperledger/fabric-samples/commit/f677821) [FAB-13232](https://jira.hyperledger.org/browse/FAB-13232) fix peer node start command
+* [6a7472e](https://github.com/hyperledger/fabric-samples/commit/6a7472e) [FAB-13126](https://jira.hyperledger.org/browse/FAB-13126) Align fabric-samples with 1.4.0-rc1 release
+* [445ccbc](https://github.com/hyperledger/fabric-samples/commit/445ccbc) [FAB-12880](https://jira.hyperledger.org/browse/FAB-12880) Move old prog model samples for FabCar
+* [5be62b5](https://github.com/hyperledger/fabric-samples/commit/5be62b5) [FAB-13207](https://jira.hyperledger.org/browse/FAB-13207) Remove incorrect discovery options
+* [fdbd92d](https://github.com/hyperledger/fabric-samples/commit/fdbd92d) [FAB-13206](https://jira.hyperledger.org/browse/FAB-13206) Remove dependencies on fabric-client
+* [eff0046](https://github.com/hyperledger/fabric-samples/commit/eff0046) [FAB-12877](https://jira.hyperledger.org/browse/FAB-12877) Add fabcar app using new prog model (JS)
+* [c184196](https://github.com/hyperledger/fabric-samples/commit/c184196) [FAB-12878](https://jira.hyperledger.org/browse/FAB-12878) Add fabcar app using new prog model (TS)
+* [e1a39e6](https://github.com/hyperledger/fabric-samples/commit/e1a39e6) [FAB-12724](https://jira.hyperledger.org/browse/FAB-12724) Upgrade from 1.3.x to 1.4.0
+* [c21bbba](https://github.com/hyperledger/fabric-samples/commit/c21bbba) Update samples to use new logging env variables
+* [7ad9f19](https://github.com/hyperledger/fabric-samples/commit/7ad9f19) [FAB-13011](https://jira.hyperledger.org/browse/FAB-13011) add kafka consensus type to byfn sample
+* [33f064f](https://github.com/hyperledger/fabric-samples/commit/33f064f) [FAB-13170](https://jira.hyperledger.org/browse/FAB-13170) Add memberOnlyRead to marbles sample
+* [928b72b](https://github.com/hyperledger/fabric-samples/commit/928b72b) [FAB-12875](https://jira.hyperledger.org/browse/FAB-12875) Add automated tests for fabcar sample
+* [5c087f1](https://github.com/hyperledger/fabric-samples/commit/5c087f1) [FAB-13046](https://jira.hyperledger.org/browse/FAB-13046) Update TypeScript contract dependencies
+* [3748983](https://github.com/hyperledger/fabric-samples/commit/3748983) [FAB-12879](https://jira.hyperledger.org/browse/FAB-12879) Update fabcar script for new contracts
+* [4fb3b57](https://github.com/hyperledger/fabric-samples/commit/4fb3b57) [FAB-12852](https://jira.hyperledger.org/browse/FAB-12852) Add fabcar contract w/ new prog model (TS)
+* [9facb42](https://github.com/hyperledger/fabric-samples/commit/9facb42) [FAB-12851](https://jira.hyperledger.org/browse/FAB-12851) Add fabcar contract w/ new prog model (JS)
+* [e67fcf1](https://github.com/hyperledger/fabric-samples/commit/e67fcf1) [FAB-12322](https://jira.hyperledger.org/browse/FAB-12322) Update commercial-paper sample
+* [fd6e2c4](https://github.com/hyperledger/fabric-samples/commit/fd6e2c4) [FAB-12703](https://jira.hyperledger.org/browse/FAB-12703) Fix misspelling "lauches"
+* [c05f172](https://github.com/hyperledger/fabric-samples/commit/c05f172) [FAB-12608](https://jira.hyperledger.org/browse/FAB-12608) Update pipeline script
+* [286861e](https://github.com/hyperledger/fabric-samples/commit/286861e) [FAB-12371](https://jira.hyperledger.org/browse/FAB-12371)Fix the abac sample to use new cid package
+* [24c5e47](https://github.com/hyperledger/fabric-samples/commit/24c5e47) [FAB-12026](https://jira.hyperledger.org/browse/FAB-12026) pagination samples for node marbles02
+* [6dc5ce5](https://github.com/hyperledger/fabric-samples/commit/6dc5ce5) [FAB-12587](https://jira.hyperledger.org/browse/FAB-12587) Fix for Query Block by block hash API
+* [df311ce](https://github.com/hyperledger/fabric-samples/commit/df311ce) [FAB-12173](https://jira.hyperledger.org/browse/FAB-12173) balance-transfer: Update anchor peers
+* [c925148](https://github.com/hyperledger/fabric-samples/commit/c925148) [FAB-12415](https://jira.hyperledger.org/browse/FAB-12415) samples for 1.3.0 (master cleanup)
+* [3a12c60](https://github.com/hyperledger/fabric-samples/commit/3a12c60) [FAB-12275](https://jira.hyperledger.org/browse/FAB-12275) Fix the warn in creating genesis block
+* [c6f6324](https://github.com/hyperledger/fabric-samples/commit/c6f6324) [FAB-12257](https://jira.hyperledger.org/browse/FAB-12257) allow balance-transfer for doscovery
+* [4445e8d](https://github.com/hyperledger/fabric-samples/commit/4445e8d) [FAB-12272](https://jira.hyperledger.org/browse/FAB-12272) Increase MAX_RETRY to 10
+* [33d333f](https://github.com/hyperledger/fabric-samples/commit/33d333f) [FAB-12190](https://jira.hyperledger.org/browse/FAB-12190) Update stable version in CI scripts
+* [edee638](https://github.com/hyperledger/fabric-samples/commit/edee638) [FAB-12184](https://jira.hyperledger.org/browse/FAB-12184) Prepare fabric-samples for 1.3.0-rc1
+* [514d456](https://github.com/hyperledger/fabric-samples/commit/514d456) [FAB-12170](https://jira.hyperledger.org/browse/FAB-12170) Fix dependency check in java chaincode
+* [9f80e47](https://github.com/hyperledger/fabric-samples/commit/9f80e47) [FAB-12119](https://jira.hyperledger.org/browse/FAB-12119) Fix groupId in java chaincodes
+* [3237229](https://github.com/hyperledger/fabric-samples/commit/3237229) [FAB-12073](https://jira.hyperledger.org/browse/FAB-12073) Fix Org3 peers CouchDB config.
+* [eece3d8](https://github.com/hyperledger/fabric-samples/commit/eece3d8) [FAB-12106](https://jira.hyperledger.org/browse/FAB-12106) Update fabric-ca build scripts
+* [f62952f](https://github.com/hyperledger/fabric-samples/commit/f62952f) [FABC-131] Change fabric-ca sample to build images
+* [4089786](https://github.com/hyperledger/fabric-samples/commit/4089786) [FAB-11867](https://jira.hyperledger.org/browse/FAB-11867) Develop Apps:Sample pt 2 -- application
+* [9ee57c6](https://github.com/hyperledger/fabric-samples/commit/9ee57c6) [FAB-11778](https://jira.hyperledger.org/browse/FAB-11778) Upgrade to v1.3.x from v1.2.x in byfn
+* [e7a1b76](https://github.com/hyperledger/fabric-samples/commit/e7a1b76) [FAB-9386](https://jira.hyperledger.org/browse/FAB-9386) Remove Marbles sample reference to Fauxton
+* [9c6acee](https://github.com/hyperledger/fabric-samples/commit/9c6acee) [FAB-12022](https://jira.hyperledger.org/browse/FAB-12022) Fix CI by increasing couchdb timeout
+* [4030ebd](https://github.com/hyperledger/fabric-samples/commit/4030ebd) [FAB-11397](https://jira.hyperledger.org/browse/FAB-11397) Adding java cc
+* [d776651](https://github.com/hyperledger/fabric-samples/commit/d776651) [FAB-11723](https://jira.hyperledger.org/browse/FAB-11723) Developing Apps: Sample pt 1 -- contract
+* [cbbbc78](https://github.com/hyperledger/fabric-samples/commit/cbbbc78) [FAB-11577](https://jira.hyperledger.org/browse/FAB-11577) Fix balance transfer to install Chaincode
+* [bfdc0b6](https://github.com/hyperledger/fabric-samples/commit/bfdc0b6) [FAB-11518](https://jira.hyperledger.org/browse/FAB-11518)
+* [5930dfc](https://github.com/hyperledger/fabric-samples/commit/5930dfc) [FAB-11488](https://jira.hyperledger.org/browse/FAB-11488) Update CI script
+* [ca6959c](https://github.com/hyperledger/fabric-samples/commit/ca6959c) [FAB-11311](https://jira.hyperledger.org/browse/FAB-11311) Update fabric image version
+* [0ca9e6e](https://github.com/hyperledger/fabric-samples/commit/0ca9e6e) FABN-833 Update Jenkinsfile to capture build artifacts
+* [a4a15cb](https://github.com/hyperledger/fabric-samples/commit/a4a15cb) [FAB-11220](https://jira.hyperledger.org/browse/FAB-11220) Samples - remove EventHub
+* [c4bdc68](https://github.com/hyperledger/fabric-samples/commit/c4bdc68) [FAB-8479](https://jira.hyperledger.org/browse/FAB-8479) Added Endorsement policy
+* [6edd320](https://github.com/hyperledger/fabric-samples/commit/6edd320) [FAB-9297](https://jira.hyperledger.org/browse/FAB-9297) fix README links and update bootstrap.sh
+* [75e2931](https://github.com/hyperledger/fabric-samples/commit/75e2931) [FAB-10811](https://jira.hyperledger.org/browse/FAB-10811) fabric-ca sample is broken on v1.2
+
+## "v1.2.1"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [8a458b5](https://github.com/hyperledger/fabric-samples/commit/8a458b5) [FAB-12056](https://jira.hyperledger.org/browse/FAB-12056) Private marbles cc use transient data
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+* [461b6ab](https://github.com/hyperledger/fabric-samples/commit/461b6ab) FABC-781 Remove fabric-ca sample
+* [e9b9477](https://github.com/hyperledger/fabric-samples/commit/e9b9477) [FAB-13372](https://jira.hyperledger.org/browse/FAB-13372) Fabric-Samples return error msg
+* [e3da220](https://github.com/hyperledger/fabric-samples/commit/e3da220) [FAB-13433](https://jira.hyperledger.org/browse/FAB-13433) - Update Jenkinsfile configuration
+* [33db64e](https://github.com/hyperledger/fabric-samples/commit/33db64e) Configure Stale ProBot
+* [5cd277f](https://github.com/hyperledger/fabric-samples/commit/5cd277f) [FAB-11951](https://jira.hyperledger.org/browse/FAB-11951) Interest-rate swap example for SBE
+* [9567985](https://github.com/hyperledger/fabric-samples/commit/9567985) [FAB-13407](https://jira.hyperledger.org/browse/FAB-13407) Align fabric-samples with 1.4.0-rc2 release
+* [c7572aa](https://github.com/hyperledger/fabric-samples/commit/c7572aa) [FAB-13305](https://jira.hyperledger.org/browse/FAB-13305) Update scripts to pull latest artifacts
+* [ab46e35](https://github.com/hyperledger/fabric-samples/commit/ab46e35) [FAB-13283](https://jira.hyperledger.org/browse/FAB-13283) Update sample code for commercial paper
+* [f677821](https://github.com/hyperledger/fabric-samples/commit/f677821) [FAB-13232](https://jira.hyperledger.org/browse/FAB-13232) fix peer node start command
+* [6a7472e](https://github.com/hyperledger/fabric-samples/commit/6a7472e) [FAB-13126](https://jira.hyperledger.org/browse/FAB-13126) Align fabric-samples with 1.4.0-rc1 release
+* [445ccbc](https://github.com/hyperledger/fabric-samples/commit/445ccbc) [FAB-12880](https://jira.hyperledger.org/browse/FAB-12880) Move old prog model samples for FabCar
+* [5be62b5](https://github.com/hyperledger/fabric-samples/commit/5be62b5) [FAB-13207](https://jira.hyperledger.org/browse/FAB-13207) Remove incorrect discovery options
+* [fdbd92d](https://github.com/hyperledger/fabric-samples/commit/fdbd92d) [FAB-13206](https://jira.hyperledger.org/browse/FAB-13206) Remove dependencies on fabric-client
+* [eff0046](https://github.com/hyperledger/fabric-samples/commit/eff0046) [FAB-12877](https://jira.hyperledger.org/browse/FAB-12877) Add fabcar app using new prog model (JS)
+* [c184196](https://github.com/hyperledger/fabric-samples/commit/c184196) [FAB-12878](https://jira.hyperledger.org/browse/FAB-12878) Add fabcar app using new prog model (TS)
+* [e1a39e6](https://github.com/hyperledger/fabric-samples/commit/e1a39e6) [FAB-12724](https://jira.hyperledger.org/browse/FAB-12724) Upgrade from 1.3.x to 1.4.0
+* [c21bbba](https://github.com/hyperledger/fabric-samples/commit/c21bbba) Update samples to use new logging env variables
+* [7ad9f19](https://github.com/hyperledger/fabric-samples/commit/7ad9f19) [FAB-13011](https://jira.hyperledger.org/browse/FAB-13011) add kafka consensus type to byfn sample
+* [33f064f](https://github.com/hyperledger/fabric-samples/commit/33f064f) [FAB-13170](https://jira.hyperledger.org/browse/FAB-13170) Add memberOnlyRead to marbles sample
+* [928b72b](https://github.com/hyperledger/fabric-samples/commit/928b72b) [FAB-12875](https://jira.hyperledger.org/browse/FAB-12875) Add automated tests for fabcar sample
+* [5c087f1](https://github.com/hyperledger/fabric-samples/commit/5c087f1) [FAB-13046](https://jira.hyperledger.org/browse/FAB-13046) Update TypeScript contract dependencies
+* [3748983](https://github.com/hyperledger/fabric-samples/commit/3748983) [FAB-12879](https://jira.hyperledger.org/browse/FAB-12879) Update fabcar script for new contracts
+* [4fb3b57](https://github.com/hyperledger/fabric-samples/commit/4fb3b57) [FAB-12852](https://jira.hyperledger.org/browse/FAB-12852) Add fabcar contract w/ new prog model (TS)
+* [9facb42](https://github.com/hyperledger/fabric-samples/commit/9facb42) [FAB-12851](https://jira.hyperledger.org/browse/FAB-12851) Add fabcar contract w/ new prog model (JS)
+* [e67fcf1](https://github.com/hyperledger/fabric-samples/commit/e67fcf1) [FAB-12322](https://jira.hyperledger.org/browse/FAB-12322) Update commercial-paper sample
+* [fd6e2c4](https://github.com/hyperledger/fabric-samples/commit/fd6e2c4) [FAB-12703](https://jira.hyperledger.org/browse/FAB-12703) Fix misspelling "lauches"
+* [c05f172](https://github.com/hyperledger/fabric-samples/commit/c05f172) [FAB-12608](https://jira.hyperledger.org/browse/FAB-12608) Update pipeline script
+* [286861e](https://github.com/hyperledger/fabric-samples/commit/286861e) [FAB-12371](https://jira.hyperledger.org/browse/FAB-12371)Fix the abac sample to use new cid package
+* [24c5e47](https://github.com/hyperledger/fabric-samples/commit/24c5e47) [FAB-12026](https://jira.hyperledger.org/browse/FAB-12026) pagination samples for node marbles02
+* [6dc5ce5](https://github.com/hyperledger/fabric-samples/commit/6dc5ce5) [FAB-12587](https://jira.hyperledger.org/browse/FAB-12587) Fix for Query Block by block hash API
+* [df311ce](https://github.com/hyperledger/fabric-samples/commit/df311ce) [FAB-12173](https://jira.hyperledger.org/browse/FAB-12173) balance-transfer: Update anchor peers
+* [c925148](https://github.com/hyperledger/fabric-samples/commit/c925148) [FAB-12415](https://jira.hyperledger.org/browse/FAB-12415) samples for 1.3.0 (master cleanup)
+* [3a12c60](https://github.com/hyperledger/fabric-samples/commit/3a12c60) [FAB-12275](https://jira.hyperledger.org/browse/FAB-12275) Fix the warn in creating genesis block
+* [c6f6324](https://github.com/hyperledger/fabric-samples/commit/c6f6324) [FAB-12257](https://jira.hyperledger.org/browse/FAB-12257) allow balance-transfer for doscovery
+* [4445e8d](https://github.com/hyperledger/fabric-samples/commit/4445e8d) [FAB-12272](https://jira.hyperledger.org/browse/FAB-12272) Increase MAX_RETRY to 10
+* [33d333f](https://github.com/hyperledger/fabric-samples/commit/33d333f) [FAB-12190](https://jira.hyperledger.org/browse/FAB-12190) Update stable version in CI scripts
+* [edee638](https://github.com/hyperledger/fabric-samples/commit/edee638) [FAB-12184](https://jira.hyperledger.org/browse/FAB-12184) Prepare fabric-samples for 1.3.0-rc1
+* [514d456](https://github.com/hyperledger/fabric-samples/commit/514d456) [FAB-12170](https://jira.hyperledger.org/browse/FAB-12170) Fix dependency check in java chaincode
+* [9f80e47](https://github.com/hyperledger/fabric-samples/commit/9f80e47) [FAB-12119](https://jira.hyperledger.org/browse/FAB-12119) Fix groupId in java chaincodes
+* [3237229](https://github.com/hyperledger/fabric-samples/commit/3237229) [FAB-12073](https://jira.hyperledger.org/browse/FAB-12073) Fix Org3 peers CouchDB config.
+* [eece3d8](https://github.com/hyperledger/fabric-samples/commit/eece3d8) [FAB-12106](https://jira.hyperledger.org/browse/FAB-12106) Update fabric-ca build scripts
+* [f62952f](https://github.com/hyperledger/fabric-samples/commit/f62952f) [FABC-131] Change fabric-ca sample to build images
+* [4089786](https://github.com/hyperledger/fabric-samples/commit/4089786) [FAB-11867](https://jira.hyperledger.org/browse/FAB-11867) Develop Apps:Sample pt 2 -- application
+* [9ee57c6](https://github.com/hyperledger/fabric-samples/commit/9ee57c6) [FAB-11778](https://jira.hyperledger.org/browse/FAB-11778) Upgrade to v1.3.x from v1.2.x in byfn
+* [e7a1b76](https://github.com/hyperledger/fabric-samples/commit/e7a1b76) [FAB-9386](https://jira.hyperledger.org/browse/FAB-9386) Remove Marbles sample reference to Fauxton
+* [9c6acee](https://github.com/hyperledger/fabric-samples/commit/9c6acee) [FAB-12022](https://jira.hyperledger.org/browse/FAB-12022) Fix CI by increasing couchdb timeout
+* [4030ebd](https://github.com/hyperledger/fabric-samples/commit/4030ebd) [FAB-11397](https://jira.hyperledger.org/browse/FAB-11397) Adding java cc
+* [d776651](https://github.com/hyperledger/fabric-samples/commit/d776651) [FAB-11723](https://jira.hyperledger.org/browse/FAB-11723) Developing Apps: Sample pt 1 -- contract
+* [cbbbc78](https://github.com/hyperledger/fabric-samples/commit/cbbbc78) [FAB-11577](https://jira.hyperledger.org/browse/FAB-11577) Fix balance transfer to install Chaincode
+* [bfdc0b6](https://github.com/hyperledger/fabric-samples/commit/bfdc0b6) [FAB-11518](https://jira.hyperledger.org/browse/FAB-11518)
+* [5930dfc](https://github.com/hyperledger/fabric-samples/commit/5930dfc) [FAB-11488](https://jira.hyperledger.org/browse/FAB-11488) Update CI script
+* [ca6959c](https://github.com/hyperledger/fabric-samples/commit/ca6959c) [FAB-11311](https://jira.hyperledger.org/browse/FAB-11311) Update fabric image version
+* [0ca9e6e](https://github.com/hyperledger/fabric-samples/commit/0ca9e6e) FABN-833 Update Jenkinsfile to capture build artifacts
+* [a4a15cb](https://github.com/hyperledger/fabric-samples/commit/a4a15cb) [FAB-11220](https://jira.hyperledger.org/browse/FAB-11220) Samples - remove EventHub
+* [c4bdc68](https://github.com/hyperledger/fabric-samples/commit/c4bdc68) [FAB-8479](https://jira.hyperledger.org/browse/FAB-8479) Added Endorsement policy
+* [6edd320](https://github.com/hyperledger/fabric-samples/commit/6edd320) [FAB-9297](https://jira.hyperledger.org/browse/FAB-9297) fix README links and update bootstrap.sh
+* [75e2931](https://github.com/hyperledger/fabric-samples/commit/75e2931) [FAB-10811](https://jira.hyperledger.org/browse/FAB-10811) fabric-ca sample is broken on v1.2
+
+## "v1.2.0"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [8a458b5](https://github.com/hyperledger/fabric-samples/commit/8a458b5) [FAB-12056](https://jira.hyperledger.org/browse/FAB-12056) Private marbles cc use transient data
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+* [461b6ab](https://github.com/hyperledger/fabric-samples/commit/461b6ab) FABC-781 Remove fabric-ca sample
+* [e9b9477](https://github.com/hyperledger/fabric-samples/commit/e9b9477) [FAB-13372](https://jira.hyperledger.org/browse/FAB-13372) Fabric-Samples return error msg
+* [e3da220](https://github.com/hyperledger/fabric-samples/commit/e3da220) [FAB-13433](https://jira.hyperledger.org/browse/FAB-13433) - Update Jenkinsfile configuration
+* [33db64e](https://github.com/hyperledger/fabric-samples/commit/33db64e) Configure Stale ProBot
+* [5cd277f](https://github.com/hyperledger/fabric-samples/commit/5cd277f) [FAB-11951](https://jira.hyperledger.org/browse/FAB-11951) Interest-rate swap example for SBE
+* [9567985](https://github.com/hyperledger/fabric-samples/commit/9567985) [FAB-13407](https://jira.hyperledger.org/browse/FAB-13407) Align fabric-samples with 1.4.0-rc2 release
+* [c7572aa](https://github.com/hyperledger/fabric-samples/commit/c7572aa) [FAB-13305](https://jira.hyperledger.org/browse/FAB-13305) Update scripts to pull latest artifacts
+* [ab46e35](https://github.com/hyperledger/fabric-samples/commit/ab46e35) [FAB-13283](https://jira.hyperledger.org/browse/FAB-13283) Update sample code for commercial paper
+* [f677821](https://github.com/hyperledger/fabric-samples/commit/f677821) [FAB-13232](https://jira.hyperledger.org/browse/FAB-13232) fix peer node start command
+* [6a7472e](https://github.com/hyperledger/fabric-samples/commit/6a7472e) [FAB-13126](https://jira.hyperledger.org/browse/FAB-13126) Align fabric-samples with 1.4.0-rc1 release
+* [445ccbc](https://github.com/hyperledger/fabric-samples/commit/445ccbc) [FAB-12880](https://jira.hyperledger.org/browse/FAB-12880) Move old prog model samples for FabCar
+* [5be62b5](https://github.com/hyperledger/fabric-samples/commit/5be62b5) [FAB-13207](https://jira.hyperledger.org/browse/FAB-13207) Remove incorrect discovery options
+* [fdbd92d](https://github.com/hyperledger/fabric-samples/commit/fdbd92d) [FAB-13206](https://jira.hyperledger.org/browse/FAB-13206) Remove dependencies on fabric-client
+* [eff0046](https://github.com/hyperledger/fabric-samples/commit/eff0046) [FAB-12877](https://jira.hyperledger.org/browse/FAB-12877) Add fabcar app using new prog model (JS)
+* [c184196](https://github.com/hyperledger/fabric-samples/commit/c184196) [FAB-12878](https://jira.hyperledger.org/browse/FAB-12878) Add fabcar app using new prog model (TS)
+* [e1a39e6](https://github.com/hyperledger/fabric-samples/commit/e1a39e6) [FAB-12724](https://jira.hyperledger.org/browse/FAB-12724) Upgrade from 1.3.x to 1.4.0
+* [c21bbba](https://github.com/hyperledger/fabric-samples/commit/c21bbba) Update samples to use new logging env variables
+* [7ad9f19](https://github.com/hyperledger/fabric-samples/commit/7ad9f19) [FAB-13011](https://jira.hyperledger.org/browse/FAB-13011) add kafka consensus type to byfn sample
+* [33f064f](https://github.com/hyperledger/fabric-samples/commit/33f064f) [FAB-13170](https://jira.hyperledger.org/browse/FAB-13170) Add memberOnlyRead to marbles sample
+* [928b72b](https://github.com/hyperledger/fabric-samples/commit/928b72b) [FAB-12875](https://jira.hyperledger.org/browse/FAB-12875) Add automated tests for fabcar sample
+* [5c087f1](https://github.com/hyperledger/fabric-samples/commit/5c087f1) [FAB-13046](https://jira.hyperledger.org/browse/FAB-13046) Update TypeScript contract dependencies
+* [3748983](https://github.com/hyperledger/fabric-samples/commit/3748983) [FAB-12879](https://jira.hyperledger.org/browse/FAB-12879) Update fabcar script for new contracts
+* [4fb3b57](https://github.com/hyperledger/fabric-samples/commit/4fb3b57) [FAB-12852](https://jira.hyperledger.org/browse/FAB-12852) Add fabcar contract w/ new prog model (TS)
+* [9facb42](https://github.com/hyperledger/fabric-samples/commit/9facb42) [FAB-12851](https://jira.hyperledger.org/browse/FAB-12851) Add fabcar contract w/ new prog model (JS)
+* [e67fcf1](https://github.com/hyperledger/fabric-samples/commit/e67fcf1) [FAB-12322](https://jira.hyperledger.org/browse/FAB-12322) Update commercial-paper sample
+* [fd6e2c4](https://github.com/hyperledger/fabric-samples/commit/fd6e2c4) [FAB-12703](https://jira.hyperledger.org/browse/FAB-12703) Fix misspelling "lauches"
+* [c05f172](https://github.com/hyperledger/fabric-samples/commit/c05f172) [FAB-12608](https://jira.hyperledger.org/browse/FAB-12608) Update pipeline script
+* [286861e](https://github.com/hyperledger/fabric-samples/commit/286861e) [FAB-12371](https://jira.hyperledger.org/browse/FAB-12371)Fix the abac sample to use new cid package
+* [24c5e47](https://github.com/hyperledger/fabric-samples/commit/24c5e47) [FAB-12026](https://jira.hyperledger.org/browse/FAB-12026) pagination samples for node marbles02
+* [6dc5ce5](https://github.com/hyperledger/fabric-samples/commit/6dc5ce5) [FAB-12587](https://jira.hyperledger.org/browse/FAB-12587) Fix for Query Block by block hash API
+* [df311ce](https://github.com/hyperledger/fabric-samples/commit/df311ce) [FAB-12173](https://jira.hyperledger.org/browse/FAB-12173) balance-transfer: Update anchor peers
+* [c925148](https://github.com/hyperledger/fabric-samples/commit/c925148) [FAB-12415](https://jira.hyperledger.org/browse/FAB-12415) samples for 1.3.0 (master cleanup)
+* [3a12c60](https://github.com/hyperledger/fabric-samples/commit/3a12c60) [FAB-12275](https://jira.hyperledger.org/browse/FAB-12275) Fix the warn in creating genesis block
+* [c6f6324](https://github.com/hyperledger/fabric-samples/commit/c6f6324) [FAB-12257](https://jira.hyperledger.org/browse/FAB-12257) allow balance-transfer for doscovery
+* [4445e8d](https://github.com/hyperledger/fabric-samples/commit/4445e8d) [FAB-12272](https://jira.hyperledger.org/browse/FAB-12272) Increase MAX_RETRY to 10
+* [33d333f](https://github.com/hyperledger/fabric-samples/commit/33d333f) [FAB-12190](https://jira.hyperledger.org/browse/FAB-12190) Update stable version in CI scripts
+* [edee638](https://github.com/hyperledger/fabric-samples/commit/edee638) [FAB-12184](https://jira.hyperledger.org/browse/FAB-12184) Prepare fabric-samples for 1.3.0-rc1
+* [514d456](https://github.com/hyperledger/fabric-samples/commit/514d456) [FAB-12170](https://jira.hyperledger.org/browse/FAB-12170) Fix dependency check in java chaincode
+* [9f80e47](https://github.com/hyperledger/fabric-samples/commit/9f80e47) [FAB-12119](https://jira.hyperledger.org/browse/FAB-12119) Fix groupId in java chaincodes
+* [3237229](https://github.com/hyperledger/fabric-samples/commit/3237229) [FAB-12073](https://jira.hyperledger.org/browse/FAB-12073) Fix Org3 peers CouchDB config.
+* [eece3d8](https://github.com/hyperledger/fabric-samples/commit/eece3d8) [FAB-12106](https://jira.hyperledger.org/browse/FAB-12106) Update fabric-ca build scripts
+* [f62952f](https://github.com/hyperledger/fabric-samples/commit/f62952f) [FABC-131] Change fabric-ca sample to build images
+* [4089786](https://github.com/hyperledger/fabric-samples/commit/4089786) [FAB-11867](https://jira.hyperledger.org/browse/FAB-11867) Develop Apps:Sample pt 2 -- application
+* [9ee57c6](https://github.com/hyperledger/fabric-samples/commit/9ee57c6) [FAB-11778](https://jira.hyperledger.org/browse/FAB-11778) Upgrade to v1.3.x from v1.2.x in byfn
+* [e7a1b76](https://github.com/hyperledger/fabric-samples/commit/e7a1b76) [FAB-9386](https://jira.hyperledger.org/browse/FAB-9386) Remove Marbles sample reference to Fauxton
+* [9c6acee](https://github.com/hyperledger/fabric-samples/commit/9c6acee) [FAB-12022](https://jira.hyperledger.org/browse/FAB-12022) Fix CI by increasing couchdb timeout
+* [4030ebd](https://github.com/hyperledger/fabric-samples/commit/4030ebd) [FAB-11397](https://jira.hyperledger.org/browse/FAB-11397) Adding java cc
+* [d776651](https://github.com/hyperledger/fabric-samples/commit/d776651) [FAB-11723](https://jira.hyperledger.org/browse/FAB-11723) Developing Apps: Sample pt 1 -- contract
+* [cbbbc78](https://github.com/hyperledger/fabric-samples/commit/cbbbc78) [FAB-11577](https://jira.hyperledger.org/browse/FAB-11577) Fix balance transfer to install Chaincode
+* [bfdc0b6](https://github.com/hyperledger/fabric-samples/commit/bfdc0b6) [FAB-11518](https://jira.hyperledger.org/browse/FAB-11518)
+* [5930dfc](https://github.com/hyperledger/fabric-samples/commit/5930dfc) [FAB-11488](https://jira.hyperledger.org/browse/FAB-11488) Update CI script
+* [ca6959c](https://github.com/hyperledger/fabric-samples/commit/ca6959c) [FAB-11311](https://jira.hyperledger.org/browse/FAB-11311) Update fabric image version
+* [0ca9e6e](https://github.com/hyperledger/fabric-samples/commit/0ca9e6e) FABN-833 Update Jenkinsfile to capture build artifacts
+* [a4a15cb](https://github.com/hyperledger/fabric-samples/commit/a4a15cb) [FAB-11220](https://jira.hyperledger.org/browse/FAB-11220) Samples - remove EventHub
+* [c4bdc68](https://github.com/hyperledger/fabric-samples/commit/c4bdc68) [FAB-8479](https://jira.hyperledger.org/browse/FAB-8479) Added Endorsement policy
+* [6edd320](https://github.com/hyperledger/fabric-samples/commit/6edd320) [FAB-9297](https://jira.hyperledger.org/browse/FAB-9297) fix README links and update bootstrap.sh
+* [75e2931](https://github.com/hyperledger/fabric-samples/commit/75e2931) [FAB-10811](https://jira.hyperledger.org/browse/FAB-10811) fabric-ca sample is broken on v1.2
+* [ad40e29](https://github.com/hyperledger/fabric-samples/commit/ad40e29) [FAB-10732](https://jira.hyperledger.org/browse/FAB-10732) BYFN upgrade to v1.2
+* [20ad472](https://github.com/hyperledger/fabric-samples/commit/20ad472) [FAB-10801](https://jira.hyperledger.org/browse/FAB-10801) format and styling for shell artifacts
+* [e95210e](https://github.com/hyperledger/fabric-samples/commit/e95210e) [FAB-10074](https://jira.hyperledger.org/browse/FAB-10074) CI Script for pipeline project type
+* [5956178](https://github.com/hyperledger/fabric-samples/commit/5956178) [FAB-6600](https://jira.hyperledger.org/browse/FAB-6600) Sample chaincode for private data
+* [21444ab](https://github.com/hyperledger/fabric-samples/commit/21444ab) [FAB-10297](https://jira.hyperledger.org/browse/FAB-10297) replace fabric-preload.sh
+* [3c32c52](https://github.com/hyperledger/fabric-samples/commit/3c32c52) [FAB-10346](https://jira.hyperledger.org/browse/FAB-10346) Ensure peers are in sync in eyfn
+* [2f30504](https://github.com/hyperledger/fabric-samples/commit/2f30504) [FAB-10340](https://jira.hyperledger.org/browse/FAB-10340) Fix broken link in chaincode-docker-devmode
+* [a603655](https://github.com/hyperledger/fabric-samples/commit/a603655) [FAB-10306](https://jira.hyperledger.org/browse/FAB-10306) Fix config in balance-transfer
+* [1cd059e](https://github.com/hyperledger/fabric-samples/commit/1cd059e) [FAB-8612](https://jira.hyperledger.org/browse/FAB-8612) Cleanup eyfn/byfn with --remove-orphans
+* [3031a8c](https://github.com/hyperledger/fabric-samples/commit/3031a8c) [FAB-10235](https://jira.hyperledger.org/browse/FAB-10235) Update BYFN to use V1_2 capability
+* [5cf1944](https://github.com/hyperledger/fabric-samples/commit/5cf1944) [FAB-10185](https://jira.hyperledger.org/browse/FAB-10185) Reorder for logical consistency
+* [e2f4f20](https://github.com/hyperledger/fabric-samples/commit/e2f4f20) [FAB-10176](https://jira.hyperledger.org/browse/FAB-10176) Fix invalid configtx.yaml documents
+* [cad2b98](https://github.com/hyperledger/fabric-samples/commit/cad2b98) [FAB-10073](https://jira.hyperledger.org/browse/FAB-10073) Fix broken link in samples/fabric-ca/README
+* [ace2e5a](https://github.com/hyperledger/fabric-samples/commit/ace2e5a) [FAB-10021](https://jira.hyperledger.org/browse/FAB-10021) Typo in balance-transfer/testAPIs.sh
+* [fa1d899](https://github.com/hyperledger/fabric-samples/commit/fa1d899) [FAB-9893](https://jira.hyperledger.org/browse/FAB-9893) Add .gitreview file for quicker Gerrit setup
+* [98561e0](https://github.com/hyperledger/fabric-samples/commit/98561e0) [FAB-9552](https://jira.hyperledger.org/browse/FAB-9552) Fix access of an un-declared variable
+* [b5dad8c](https://github.com/hyperledger/fabric-samples/commit/b5dad8c) [FAB-9317](https://jira.hyperledger.org/browse/FAB-9317) Update first-network with multi endorse
+* [146f7bc](https://github.com/hyperledger/fabric-samples/commit/146f7bc) [FAB-9572](https://jira.hyperledger.org/browse/FAB-9572) Fix README to match new version of BYFN
+* [010fcb5](https://github.com/hyperledger/fabric-samples/commit/010fcb5) [FAB-9326](https://jira.hyperledger.org/browse/FAB-9326) Clean up byfn.sh, drop "-m" option
+* [2d6386c](https://github.com/hyperledger/fabric-samples/commit/2d6386c) [FAB-8245](https://jira.hyperledger.org/browse/FAB-8245) change first network according to the fix
+* [3a5108f](https://github.com/hyperledger/fabric-samples/commit/3a5108f) [FAB-9475](https://jira.hyperledger.org/browse/FAB-9475) Fix a dead link in README
+* [77a6568](https://github.com/hyperledger/fabric-samples/commit/77a6568) [FAB-9294](https://jira.hyperledger.org/browse/FAB-9294) eliminate excess noise in BYFN
+* [41f5ab8](https://github.com/hyperledger/fabric-samples/commit/41f5ab8) [FAB-9406](https://jira.hyperledger.org/browse/FAB-9406) Typo in byfn.sh
+* [f5c2eb8](https://github.com/hyperledger/fabric-samples/commit/f5c2eb8) [FAB-9362](https://jira.hyperledger.org/browse/FAB-9362) add CONTRIBUTING.md and CODE_OF_CONDUCT.md
+* [9d518fb](https://github.com/hyperledger/fabric-samples/commit/9d518fb) [FAB-9330](https://jira.hyperledger.org/browse/FAB-9330) Refactor top-level .gitignore into subdirs
+* [a080da3](https://github.com/hyperledger/fabric-samples/commit/a080da3) [FAB-8958](https://jira.hyperledger.org/browse/FAB-8958) Add org3 removal to byfn.sh
+* [8fc0865](https://github.com/hyperledger/fabric-samples/commit/8fc0865) [FAB-9185](https://jira.hyperledger.org/browse/FAB-9185) add /config to .gitignore
+* [680ff01](https://github.com/hyperledger/fabric-samples/commit/680ff01) [FAB-8600](https://jira.hyperledger.org/browse/FAB-8600)Clear hyperledger-related containers only
+* [4f97717](https://github.com/hyperledger/fabric-samples/commit/4f97717) [FAB-8947](https://jira.hyperledger.org/browse/FAB-8947) Fabric-Samples remove package lock
+* [fcf62ad](https://github.com/hyperledger/fabric-samples/commit/fcf62ad) [FAB-8265](https://jira.hyperledger.org/browse/FAB-8265) Fixed spelling error in registerUser
+* [e4d7760](https://github.com/hyperledger/fabric-samples/commit/e4d7760) [ [FAB-8730](https://jira.hyperledger.org/browse/FAB-8730) ] hyphen breaks fabric-samples
+* [cf79cd1](https://github.com/hyperledger/fabric-samples/commit/cf79cd1) [FAB-7584](https://jira.hyperledger.org/browse/FAB-7584) Removes copy of creds to keystore
+
+## "v1.1.0"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [8a458b5](https://github.com/hyperledger/fabric-samples/commit/8a458b5) [FAB-12056](https://jira.hyperledger.org/browse/FAB-12056) Private marbles cc use transient data
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+* [461b6ab](https://github.com/hyperledger/fabric-samples/commit/461b6ab) FABC-781 Remove fabric-ca sample
+* [e9b9477](https://github.com/hyperledger/fabric-samples/commit/e9b9477) [FAB-13372](https://jira.hyperledger.org/browse/FAB-13372) Fabric-Samples return error msg
+* [e3da220](https://github.com/hyperledger/fabric-samples/commit/e3da220) [FAB-13433](https://jira.hyperledger.org/browse/FAB-13433) - Update Jenkinsfile configuration
+* [33db64e](https://github.com/hyperledger/fabric-samples/commit/33db64e) Configure Stale ProBot
+* [5cd277f](https://github.com/hyperledger/fabric-samples/commit/5cd277f) [FAB-11951](https://jira.hyperledger.org/browse/FAB-11951) Interest-rate swap example for SBE
+* [9567985](https://github.com/hyperledger/fabric-samples/commit/9567985) [FAB-13407](https://jira.hyperledger.org/browse/FAB-13407) Align fabric-samples with 1.4.0-rc2 release
+* [c7572aa](https://github.com/hyperledger/fabric-samples/commit/c7572aa) [FAB-13305](https://jira.hyperledger.org/browse/FAB-13305) Update scripts to pull latest artifacts
+* [ab46e35](https://github.com/hyperledger/fabric-samples/commit/ab46e35) [FAB-13283](https://jira.hyperledger.org/browse/FAB-13283) Update sample code for commercial paper
+* [f677821](https://github.com/hyperledger/fabric-samples/commit/f677821) [FAB-13232](https://jira.hyperledger.org/browse/FAB-13232) fix peer node start command
+* [6a7472e](https://github.com/hyperledger/fabric-samples/commit/6a7472e) [FAB-13126](https://jira.hyperledger.org/browse/FAB-13126) Align fabric-samples with 1.4.0-rc1 release
+* [445ccbc](https://github.com/hyperledger/fabric-samples/commit/445ccbc) [FAB-12880](https://jira.hyperledger.org/browse/FAB-12880) Move old prog model samples for FabCar
+* [5be62b5](https://github.com/hyperledger/fabric-samples/commit/5be62b5) [FAB-13207](https://jira.hyperledger.org/browse/FAB-13207) Remove incorrect discovery options
+* [fdbd92d](https://github.com/hyperledger/fabric-samples/commit/fdbd92d) [FAB-13206](https://jira.hyperledger.org/browse/FAB-13206) Remove dependencies on fabric-client
+* [eff0046](https://github.com/hyperledger/fabric-samples/commit/eff0046) [FAB-12877](https://jira.hyperledger.org/browse/FAB-12877) Add fabcar app using new prog model (JS)
+* [c184196](https://github.com/hyperledger/fabric-samples/commit/c184196) [FAB-12878](https://jira.hyperledger.org/browse/FAB-12878) Add fabcar app using new prog model (TS)
+* [e1a39e6](https://github.com/hyperledger/fabric-samples/commit/e1a39e6) [FAB-12724](https://jira.hyperledger.org/browse/FAB-12724) Upgrade from 1.3.x to 1.4.0
+* [c21bbba](https://github.com/hyperledger/fabric-samples/commit/c21bbba) Update samples to use new logging env variables
+* [7ad9f19](https://github.com/hyperledger/fabric-samples/commit/7ad9f19) [FAB-13011](https://jira.hyperledger.org/browse/FAB-13011) add kafka consensus type to byfn sample
+* [33f064f](https://github.com/hyperledger/fabric-samples/commit/33f064f) [FAB-13170](https://jira.hyperledger.org/browse/FAB-13170) Add memberOnlyRead to marbles sample
+* [928b72b](https://github.com/hyperledger/fabric-samples/commit/928b72b) [FAB-12875](https://jira.hyperledger.org/browse/FAB-12875) Add automated tests for fabcar sample
+* [5c087f1](https://github.com/hyperledger/fabric-samples/commit/5c087f1) [FAB-13046](https://jira.hyperledger.org/browse/FAB-13046) Update TypeScript contract dependencies
+* [3748983](https://github.com/hyperledger/fabric-samples/commit/3748983) [FAB-12879](https://jira.hyperledger.org/browse/FAB-12879) Update fabcar script for new contracts
+* [4fb3b57](https://github.com/hyperledger/fabric-samples/commit/4fb3b57) [FAB-12852](https://jira.hyperledger.org/browse/FAB-12852) Add fabcar contract w/ new prog model (TS)
+* [9facb42](https://github.com/hyperledger/fabric-samples/commit/9facb42) [FAB-12851](https://jira.hyperledger.org/browse/FAB-12851) Add fabcar contract w/ new prog model (JS)
+* [e67fcf1](https://github.com/hyperledger/fabric-samples/commit/e67fcf1) [FAB-12322](https://jira.hyperledger.org/browse/FAB-12322) Update commercial-paper sample
+* [fd6e2c4](https://github.com/hyperledger/fabric-samples/commit/fd6e2c4) [FAB-12703](https://jira.hyperledger.org/browse/FAB-12703) Fix misspelling "lauches"
+* [c05f172](https://github.com/hyperledger/fabric-samples/commit/c05f172) [FAB-12608](https://jira.hyperledger.org/browse/FAB-12608) Update pipeline script
+* [286861e](https://github.com/hyperledger/fabric-samples/commit/286861e) [FAB-12371](https://jira.hyperledger.org/browse/FAB-12371)Fix the abac sample to use new cid package
+* [24c5e47](https://github.com/hyperledger/fabric-samples/commit/24c5e47) [FAB-12026](https://jira.hyperledger.org/browse/FAB-12026) pagination samples for node marbles02
+* [6dc5ce5](https://github.com/hyperledger/fabric-samples/commit/6dc5ce5) [FAB-12587](https://jira.hyperledger.org/browse/FAB-12587) Fix for Query Block by block hash API
+* [df311ce](https://github.com/hyperledger/fabric-samples/commit/df311ce) [FAB-12173](https://jira.hyperledger.org/browse/FAB-12173) balance-transfer: Update anchor peers
+* [c925148](https://github.com/hyperledger/fabric-samples/commit/c925148) [FAB-12415](https://jira.hyperledger.org/browse/FAB-12415) samples for 1.3.0 (master cleanup)
+* [3a12c60](https://github.com/hyperledger/fabric-samples/commit/3a12c60) [FAB-12275](https://jira.hyperledger.org/browse/FAB-12275) Fix the warn in creating genesis block
+* [c6f6324](https://github.com/hyperledger/fabric-samples/commit/c6f6324) [FAB-12257](https://jira.hyperledger.org/browse/FAB-12257) allow balance-transfer for doscovery
+* [4445e8d](https://github.com/hyperledger/fabric-samples/commit/4445e8d) [FAB-12272](https://jira.hyperledger.org/browse/FAB-12272) Increase MAX_RETRY to 10
+* [33d333f](https://github.com/hyperledger/fabric-samples/commit/33d333f) [FAB-12190](https://jira.hyperledger.org/browse/FAB-12190) Update stable version in CI scripts
+* [edee638](https://github.com/hyperledger/fabric-samples/commit/edee638) [FAB-12184](https://jira.hyperledger.org/browse/FAB-12184) Prepare fabric-samples for 1.3.0-rc1
+* [514d456](https://github.com/hyperledger/fabric-samples/commit/514d456) [FAB-12170](https://jira.hyperledger.org/browse/FAB-12170) Fix dependency check in java chaincode
+* [9f80e47](https://github.com/hyperledger/fabric-samples/commit/9f80e47) [FAB-12119](https://jira.hyperledger.org/browse/FAB-12119) Fix groupId in java chaincodes
+* [3237229](https://github.com/hyperledger/fabric-samples/commit/3237229) [FAB-12073](https://jira.hyperledger.org/browse/FAB-12073) Fix Org3 peers CouchDB config.
+* [eece3d8](https://github.com/hyperledger/fabric-samples/commit/eece3d8) [FAB-12106](https://jira.hyperledger.org/browse/FAB-12106) Update fabric-ca build scripts
+* [f62952f](https://github.com/hyperledger/fabric-samples/commit/f62952f) [FABC-131] Change fabric-ca sample to build images
+* [4089786](https://github.com/hyperledger/fabric-samples/commit/4089786) [FAB-11867](https://jira.hyperledger.org/browse/FAB-11867) Develop Apps:Sample pt 2 -- application
+* [9ee57c6](https://github.com/hyperledger/fabric-samples/commit/9ee57c6) [FAB-11778](https://jira.hyperledger.org/browse/FAB-11778) Upgrade to v1.3.x from v1.2.x in byfn
+* [e7a1b76](https://github.com/hyperledger/fabric-samples/commit/e7a1b76) [FAB-9386](https://jira.hyperledger.org/browse/FAB-9386) Remove Marbles sample reference to Fauxton
+* [9c6acee](https://github.com/hyperledger/fabric-samples/commit/9c6acee) [FAB-12022](https://jira.hyperledger.org/browse/FAB-12022) Fix CI by increasing couchdb timeout
+* [4030ebd](https://github.com/hyperledger/fabric-samples/commit/4030ebd) [FAB-11397](https://jira.hyperledger.org/browse/FAB-11397) Adding java cc
+* [d776651](https://github.com/hyperledger/fabric-samples/commit/d776651) [FAB-11723](https://jira.hyperledger.org/browse/FAB-11723) Developing Apps: Sample pt 1 -- contract
+* [cbbbc78](https://github.com/hyperledger/fabric-samples/commit/cbbbc78) [FAB-11577](https://jira.hyperledger.org/browse/FAB-11577) Fix balance transfer to install Chaincode
+* [bfdc0b6](https://github.com/hyperledger/fabric-samples/commit/bfdc0b6) [FAB-11518](https://jira.hyperledger.org/browse/FAB-11518)
+* [5930dfc](https://github.com/hyperledger/fabric-samples/commit/5930dfc) [FAB-11488](https://jira.hyperledger.org/browse/FAB-11488) Update CI script
+* [ca6959c](https://github.com/hyperledger/fabric-samples/commit/ca6959c) [FAB-11311](https://jira.hyperledger.org/browse/FAB-11311) Update fabric image version
+* [0ca9e6e](https://github.com/hyperledger/fabric-samples/commit/0ca9e6e) FABN-833 Update Jenkinsfile to capture build artifacts
+* [a4a15cb](https://github.com/hyperledger/fabric-samples/commit/a4a15cb) [FAB-11220](https://jira.hyperledger.org/browse/FAB-11220) Samples - remove EventHub
+* [c4bdc68](https://github.com/hyperledger/fabric-samples/commit/c4bdc68) [FAB-8479](https://jira.hyperledger.org/browse/FAB-8479) Added Endorsement policy
+* [6edd320](https://github.com/hyperledger/fabric-samples/commit/6edd320) [FAB-9297](https://jira.hyperledger.org/browse/FAB-9297) fix README links and update bootstrap.sh
+* [75e2931](https://github.com/hyperledger/fabric-samples/commit/75e2931) [FAB-10811](https://jira.hyperledger.org/browse/FAB-10811) fabric-ca sample is broken on v1.2
+* [ad40e29](https://github.com/hyperledger/fabric-samples/commit/ad40e29) [FAB-10732](https://jira.hyperledger.org/browse/FAB-10732) BYFN upgrade to v1.2
+* [20ad472](https://github.com/hyperledger/fabric-samples/commit/20ad472) [FAB-10801](https://jira.hyperledger.org/browse/FAB-10801) format and styling for shell artifacts
+* [e95210e](https://github.com/hyperledger/fabric-samples/commit/e95210e) [FAB-10074](https://jira.hyperledger.org/browse/FAB-10074) CI Script for pipeline project type
+* [5956178](https://github.com/hyperledger/fabric-samples/commit/5956178) [FAB-6600](https://jira.hyperledger.org/browse/FAB-6600) Sample chaincode for private data
+* [21444ab](https://github.com/hyperledger/fabric-samples/commit/21444ab) [FAB-10297](https://jira.hyperledger.org/browse/FAB-10297) replace fabric-preload.sh
+* [3c32c52](https://github.com/hyperledger/fabric-samples/commit/3c32c52) [FAB-10346](https://jira.hyperledger.org/browse/FAB-10346) Ensure peers are in sync in eyfn
+* [2f30504](https://github.com/hyperledger/fabric-samples/commit/2f30504) [FAB-10340](https://jira.hyperledger.org/browse/FAB-10340) Fix broken link in chaincode-docker-devmode
+* [a603655](https://github.com/hyperledger/fabric-samples/commit/a603655) [FAB-10306](https://jira.hyperledger.org/browse/FAB-10306) Fix config in balance-transfer
+* [1cd059e](https://github.com/hyperledger/fabric-samples/commit/1cd059e) [FAB-8612](https://jira.hyperledger.org/browse/FAB-8612) Cleanup eyfn/byfn with --remove-orphans
+* [3031a8c](https://github.com/hyperledger/fabric-samples/commit/3031a8c) [FAB-10235](https://jira.hyperledger.org/browse/FAB-10235) Update BYFN to use V1_2 capability
+* [5cf1944](https://github.com/hyperledger/fabric-samples/commit/5cf1944) [FAB-10185](https://jira.hyperledger.org/browse/FAB-10185) Reorder for logical consistency
+* [e2f4f20](https://github.com/hyperledger/fabric-samples/commit/e2f4f20) [FAB-10176](https://jira.hyperledger.org/browse/FAB-10176) Fix invalid configtx.yaml documents
+* [cad2b98](https://github.com/hyperledger/fabric-samples/commit/cad2b98) [FAB-10073](https://jira.hyperledger.org/browse/FAB-10073) Fix broken link in samples/fabric-ca/README
+* [ace2e5a](https://github.com/hyperledger/fabric-samples/commit/ace2e5a) [FAB-10021](https://jira.hyperledger.org/browse/FAB-10021) Typo in balance-transfer/testAPIs.sh
+* [fa1d899](https://github.com/hyperledger/fabric-samples/commit/fa1d899) [FAB-9893](https://jira.hyperledger.org/browse/FAB-9893) Add .gitreview file for quicker Gerrit setup
+* [98561e0](https://github.com/hyperledger/fabric-samples/commit/98561e0) [FAB-9552](https://jira.hyperledger.org/browse/FAB-9552) Fix access of an un-declared variable
+* [b5dad8c](https://github.com/hyperledger/fabric-samples/commit/b5dad8c) [FAB-9317](https://jira.hyperledger.org/browse/FAB-9317) Update first-network with multi endorse
+* [146f7bc](https://github.com/hyperledger/fabric-samples/commit/146f7bc) [FAB-9572](https://jira.hyperledger.org/browse/FAB-9572) Fix README to match new version of BYFN
+* [010fcb5](https://github.com/hyperledger/fabric-samples/commit/010fcb5) [FAB-9326](https://jira.hyperledger.org/browse/FAB-9326) Clean up byfn.sh, drop "-m" option
+* [2d6386c](https://github.com/hyperledger/fabric-samples/commit/2d6386c) [FAB-8245](https://jira.hyperledger.org/browse/FAB-8245) change first network according to the fix
+* [3a5108f](https://github.com/hyperledger/fabric-samples/commit/3a5108f) [FAB-9475](https://jira.hyperledger.org/browse/FAB-9475) Fix a dead link in README
+* [77a6568](https://github.com/hyperledger/fabric-samples/commit/77a6568) [FAB-9294](https://jira.hyperledger.org/browse/FAB-9294) eliminate excess noise in BYFN
+* [41f5ab8](https://github.com/hyperledger/fabric-samples/commit/41f5ab8) [FAB-9406](https://jira.hyperledger.org/browse/FAB-9406) Typo in byfn.sh
+* [f5c2eb8](https://github.com/hyperledger/fabric-samples/commit/f5c2eb8) [FAB-9362](https://jira.hyperledger.org/browse/FAB-9362) add CONTRIBUTING.md and CODE_OF_CONDUCT.md
+* [9d518fb](https://github.com/hyperledger/fabric-samples/commit/9d518fb) [FAB-9330](https://jira.hyperledger.org/browse/FAB-9330) Refactor top-level .gitignore into subdirs
+* [a080da3](https://github.com/hyperledger/fabric-samples/commit/a080da3) [FAB-8958](https://jira.hyperledger.org/browse/FAB-8958) Add org3 removal to byfn.sh
+* [8fc0865](https://github.com/hyperledger/fabric-samples/commit/8fc0865) [FAB-9185](https://jira.hyperledger.org/browse/FAB-9185) add /config to .gitignore
+* [680ff01](https://github.com/hyperledger/fabric-samples/commit/680ff01) [FAB-8600](https://jira.hyperledger.org/browse/FAB-8600)Clear hyperledger-related containers only
+* [4f97717](https://github.com/hyperledger/fabric-samples/commit/4f97717) [FAB-8947](https://jira.hyperledger.org/browse/FAB-8947) Fabric-Samples remove package lock
+* [fcf62ad](https://github.com/hyperledger/fabric-samples/commit/fcf62ad) [FAB-8265](https://jira.hyperledger.org/browse/FAB-8265) Fixed spelling error in registerUser
+* [e4d7760](https://github.com/hyperledger/fabric-samples/commit/e4d7760) [ [FAB-8730](https://jira.hyperledger.org/browse/FAB-8730) ] hyphen breaks fabric-samples
+* [2bbb0a8](https://github.com/hyperledger/fabric-samples/commit/2bbb0a8) [FAB-8630](https://jira.hyperledger.org/browse/FAB-8630) byfn failing intermittently in CI
+* [823fb6b](https://github.com/hyperledger/fabric-samples/commit/823fb6b) [ [FAB-8679](https://jira.hyperledger.org/browse/FAB-8679) ] Permit samples to use RootCAs
+* [9f9fc7e](https://github.com/hyperledger/fabric-samples/commit/9f9fc7e) [FAB-8633](https://jira.hyperledger.org/browse/FAB-8633) Correct revoked error check
+* [f3b55c9](https://github.com/hyperledger/fabric-samples/commit/f3b55c9) [FAB-8621](https://jira.hyperledger.org/browse/FAB-8621) Remove Marbles index json data wrapper
+* [f110a6e](https://github.com/hyperledger/fabric-samples/commit/f110a6e) [FAB-8602](https://jira.hyperledger.org/browse/FAB-8602) Add volumes to first-network e2e yaml
+* [7362928](https://github.com/hyperledger/fabric-samples/commit/7362928) [FAB-8567](https://jira.hyperledger.org/browse/FAB-8567) Alt: Always use volumes for ledger (m)
+* [afb3d62](https://github.com/hyperledger/fabric-samples/commit/afb3d62) [FAB-8561](https://jira.hyperledger.org/browse/FAB-8561) Add note to readthedocs link in README
+* [10526d5](https://github.com/hyperledger/fabric-samples/commit/10526d5) [FAB-8564](https://jira.hyperledger.org/browse/FAB-8564) add debug commands to byfn
+* [e73a481](https://github.com/hyperledger/fabric-samples/commit/e73a481) [FAB-8568](https://jira.hyperledger.org/browse/FAB-8568) BYFN: Fix IMAGE_TAG for couchdb
+* [ffd7a25](https://github.com/hyperledger/fabric-samples/commit/ffd7a25) [FAB-6400](https://jira.hyperledger.org/browse/FAB-6400) Balance-transfer filtered events
+* [cba57da](https://github.com/hyperledger/fabric-samples/commit/cba57da) [FAB-8165](https://jira.hyperledger.org/browse/FAB-8165) Adding upgrade function to byfn
+* [c6166d6](https://github.com/hyperledger/fabric-samples/commit/c6166d6) [FAB-8540](https://jira.hyperledger.org/browse/FAB-8540) Add ledger persistance to first-network
+* [77e74b7](https://github.com/hyperledger/fabric-samples/commit/77e74b7) [FAB-8497](https://jira.hyperledger.org/browse/FAB-8497) Download images required for fabric-ca
+* [2bed1ef](https://github.com/hyperledger/fabric-samples/commit/2bed1ef) [FAB-8539](https://jira.hyperledger.org/browse/FAB-8539) Add version checking to first-network
+* [7b7fc09](https://github.com/hyperledger/fabric-samples/commit/7b7fc09) [FAB-8496](https://jira.hyperledger.org/browse/FAB-8496) allow modification of affiliations
+* [981efba](https://github.com/hyperledger/fabric-samples/commit/981efba) [FAB-8503](https://jira.hyperledger.org/browse/FAB-8503) Prevent CLI container from exiting
+* [c93268f](https://github.com/hyperledger/fabric-samples/commit/c93268f) [FAB-8518](https://jira.hyperledger.org/browse/FAB-8518) Not all compose files have IMAGE_TAG
+* [4f2cd8d](https://github.com/hyperledger/fabric-samples/commit/4f2cd8d) [FAB-8445](https://jira.hyperledger.org/browse/FAB-8445) Adding IMAGE_TAG option to byfn
+* [ca80163](https://github.com/hyperledger/fabric-samples/commit/ca80163) [FAB-8387](https://jira.hyperledger.org/browse/FAB-8387) Add gencrl to revoke command
+* [e379ac5](https://github.com/hyperledger/fabric-samples/commit/e379ac5) [FAB-8407](https://jira.hyperledger.org/browse/FAB-8407) Fix TLS bad cert error
+* [02ca1dc](https://github.com/hyperledger/fabric-samples/commit/02ca1dc) [FAB-7494](https://jira.hyperledger.org/browse/FAB-7494) Fix bootstrap peer config
+* [41e144f](https://github.com/hyperledger/fabric-samples/commit/41e144f) [FAB-8386](https://jira.hyperledger.org/browse/FAB-8386) eyfn fails to execute invoke on org3
+* [4ab098f](https://github.com/hyperledger/fabric-samples/commit/4ab098f) [FAB-8327](https://jira.hyperledger.org/browse/FAB-8327) Change eyfn.sh to use configtxlator cli
+* [24f35c1](https://github.com/hyperledger/fabric-samples/commit/24f35c1) [FAB-7750](https://jira.hyperledger.org/browse/FAB-7750) first network with support to [FAB-5664](https://jira.hyperledger.org/browse/FAB-5664)
+* [305d6f4](https://github.com/hyperledger/fabric-samples/commit/305d6f4) [FAB-8238](https://jira.hyperledger.org/browse/FAB-8238)wrong orderer/peer type in fabric-ca sam
+* [1d69e9e](https://github.com/hyperledger/fabric-samples/commit/1d69e9e) [FAB-7540](https://jira.hyperledger.org/browse/FAB-7540) Simplifies Reconfigure Your Network tutorial
+* [0daa8bc](https://github.com/hyperledger/fabric-samples/commit/0daa8bc) [FAB-8122](https://jira.hyperledger.org/browse/FAB-8122) Updated README.md file- balancetransfer
+* [652f074](https://github.com/hyperledger/fabric-samples/commit/652f074) [FAB-7342](https://jira.hyperledger.org/browse/FAB-7342) Enable client auth in fabric-ca sample
+* [90c2bfd](https://github.com/hyperledger/fabric-samples/commit/90c2bfd) [FAB-6934](https://jira.hyperledger.org/browse/FAB-6934) fix devmode sample for v1.1
+* [2c0c2c7](https://github.com/hyperledger/fabric-samples/commit/2c0c2c7) [FAB-7910](https://jira.hyperledger.org/browse/FAB-7910) Clean up chaincode containers in fabric-ca
+* [bbee1b2](https://github.com/hyperledger/fabric-samples/commit/bbee1b2) [FAB-7908](https://jira.hyperledger.org/browse/FAB-7908) Change hf.admin attr to admin
+* [dd12f88](https://github.com/hyperledger/fabric-samples/commit/dd12f88) [FAB-7834](https://jira.hyperledger.org/browse/FAB-7834) Add couchdb index to marbles02 sample
+* [25f6091](https://github.com/hyperledger/fabric-samples/commit/25f6091) [FAB-7836](https://jira.hyperledger.org/browse/FAB-7836) Fix "No identity type provided" Error
+* [5eb2fb2](https://github.com/hyperledger/fabric-samples/commit/5eb2fb2) [FAB-7653](https://jira.hyperledger.org/browse/FAB-7653) Fix incorrect chaincode location
+* [5a974a4](https://github.com/hyperledger/fabric-samples/commit/5a974a4) [FAB-7533](https://jira.hyperledger.org/browse/FAB-7533) Fix typo in balance-transfer sample
+* [038c496](https://github.com/hyperledger/fabric-samples/commit/038c496) [FAB-7592](https://jira.hyperledger.org/browse/FAB-7592) Give hf.Registrar attrs to admins
+* [cf79cd1](https://github.com/hyperledger/fabric-samples/commit/cf79cd1) [FAB-7584](https://jira.hyperledger.org/browse/FAB-7584) Removes copy of creds to keystore
+* [a0c1687](https://github.com/hyperledger/fabric-samples/commit/a0c1687) [FAB-7487](https://jira.hyperledger.org/browse/FAB-7487) Fix typo in node/fabcar.js
+* [1883ae9](https://github.com/hyperledger/fabric-samples/commit/1883ae9) [FAB-7527](https://jira.hyperledger.org/browse/FAB-7527) Improves BYFN
+* [e848216](https://github.com/hyperledger/fabric-samples/commit/e848216) [FAB-7511](https://jira.hyperledger.org/browse/FAB-7511) clear crypto material after clearing network
+* [54ffa5f](https://github.com/hyperledger/fabric-samples/commit/54ffa5f) [FAB-7241](https://jira.hyperledger.org/browse/FAB-7241) Fix chaincode-devmode
+* [c446510](https://github.com/hyperledger/fabric-samples/commit/c446510) [FAB-6550](https://jira.hyperledger.org/browse/FAB-6550) Sample app written in typescript
+* [69a127e](https://github.com/hyperledger/fabric-samples/commit/69a127e) [FAB-6254](https://jira.hyperledger.org/browse/FAB-6254) Fix the default CLI timeout
+* [7428f64](https://github.com/hyperledger/fabric-samples/commit/7428f64) [FAB-7160](https://jira.hyperledger.org/browse/FAB-7160) Samples - readme sample commands
+* [2474704](https://github.com/hyperledger/fabric-samples/commit/2474704) [FAB-6967](https://jira.hyperledger.org/browse/FAB-6967) Added steps to query by a revoked user
+* [948e237](https://github.com/hyperledger/fabric-samples/commit/948e237) [FAB-5913](https://jira.hyperledger.org/browse/FAB-5913)balance-transfer:Fix query response message
+* [7b76a7a](https://github.com/hyperledger/fabric-samples/commit/7b76a7a) [FAB-6632](https://jira.hyperledger.org/browse/FAB-6632) - Artifacts for BYFN reconfigure
+* [24ef9da](https://github.com/hyperledger/fabric-samples/commit/24ef9da) [FAB-6902](https://jira.hyperledger.org/browse/FAB-6902) [FAB-6904](https://jira.hyperledger.org/browse/FAB-6904) correct the sample for FAB-6904
+* [fd795d2](https://github.com/hyperledger/fabric-samples/commit/fd795d2) [FAB-6745](https://jira.hyperledger.org/browse/FAB-6745) Fix timing issue in sample
+* [a7be462](https://github.com/hyperledger/fabric-samples/commit/a7be462) [FAB-6895](https://jira.hyperledger.org/browse/FAB-6895) chaincode mounting issue
+* [7c23985](https://github.com/hyperledger/fabric-samples/commit/7c23985) [FAB-5221](https://jira.hyperledger.org/browse/FAB-5221) generateCerts should delete crypto
+* [1961835](https://github.com/hyperledger/fabric-samples/commit/1961835) [FAB-6870](https://jira.hyperledger.org/browse/FAB-6870) Update node modules versions
+* [bb3ac84](https://github.com/hyperledger/fabric-samples/commit/bb3ac84) [FAB-5363](https://jira.hyperledger.org/browse/FAB-5363) fabric-samples update balance
+* [6b2799e](https://github.com/hyperledger/fabric-samples/commit/6b2799e) [FAB-6568](https://jira.hyperledger.org/browse/FAB-6568) Fabric-Samples - update fabcar
+* [fafae55](https://github.com/hyperledger/fabric-samples/commit/fafae55) [FAB-6779](https://jira.hyperledger.org/browse/FAB-6779) Fix the error in fabric-ca
+* [caf5c33](https://github.com/hyperledger/fabric-samples/commit/caf5c33) [FAB-6050](https://jira.hyperledger.org/browse/FAB-6050) Adding fabric-ca sample
+* [44c204d](https://github.com/hyperledger/fabric-samples/commit/44c204d) [FAB-5898](https://jira.hyperledger.org/browse/FAB-5898) porting samples to node.js chaincode
+* [07a07d5](https://github.com/hyperledger/fabric-samples/commit/07a07d5) [FAB-6361](https://jira.hyperledger.org/browse/FAB-6361) Update license text in README
+* [77b4090](https://github.com/hyperledger/fabric-samples/commit/77b4090) [FAB-5992](https://jira.hyperledger.org/browse/FAB-5992) Fix error in first-network dir
+
+## "v1.0.6"
+
+* [11e4c23](https://github.com/hyperledger/fabric-samples/commit/11e4c23) Update samples to use v2.0 or later dependencies (#161)
+* [94beab7](https://github.com/hyperledger/fabric-samples/commit/94beab7) FABN-1531 Use v2.1.0 sdk-node modules
+* [8820d2f](https://github.com/hyperledger/fabric-samples/commit/8820d2f) Fix commercial-paper README
+* [aa9b577](https://github.com/hyperledger/fabric-samples/commit/aa9b577) Remove TLS enabled switch (#155)
+* [381fb46](https://github.com/hyperledger/fabric-samples/commit/381fb46) upgraded abstore golang chaincode to use contract-api package (#154)
+* [5e5d2c8](https://github.com/hyperledger/fabric-samples/commit/5e5d2c8) Update java chaincode to be compatible with doc and other implementations (#149)
+* [c572c51](https://github.com/hyperledger/fabric-samples/commit/c572c51) Organize and Standardize `ci` Directory Content (#152)
+* [aa40963](https://github.com/hyperledger/fabric-samples/commit/aa40963) Perform General Cleanup (#151)
+* [da41afa](https://github.com/hyperledger/fabric-samples/commit/da41afa) Remove left over rm -rf command from BYFN (#148)
+* [4bb48a9](https://github.com/hyperledger/fabric-samples/commit/4bb48a9) Jenkins no longer used (#145)
+* [6f984e1](https://github.com/hyperledger/fabric-samples/commit/6f984e1) Bump acorn from 6.4.0 to 6.4.1 in /fabcar/javascript (#144)
+* [b155620](https://github.com/hyperledger/fabric-samples/commit/b155620) Remove redundant invoke command from test network (#142)
+* [851933b](https://github.com/hyperledger/fabric-samples/commit/851933b) Add enrollUser files to commercial paper (#140)
+* [87600bd](https://github.com/hyperledger/fabric-samples/commit/87600bd) [FAB-17268](https://jira.hyperledger.org/browse/FAB-17268) Move fabcar sample to test network (#103)
+* [9397788](https://github.com/hyperledger/fabric-samples/commit/9397788) Wrong groupId on hyperledger fabric dependencies for java-application (#134)
+* [92555fb](https://github.com/hyperledger/fabric-samples/commit/92555fb) Update README.md (#133)
+* [59c6641](https://github.com/hyperledger/fabric-samples/commit/59c6641) Change Download Location of Fabric Binaries (#143)
+* [1f283fc](https://github.com/hyperledger/fabric-samples/commit/1f283fc) init function does not exist on fabcar (#141)
+* [defb6bb](https://github.com/hyperledger/fabric-samples/commit/defb6bb) [FAB-17656](https://jira.hyperledger.org/browse/FAB-17656) echo Generating channel.tx (#139)
+* [4c7bab0](https://github.com/hyperledger/fabric-samples/commit/4c7bab0) fix: package seletor REGEX (#135)
+* [db69c6f](https://github.com/hyperledger/fabric-samples/commit/db69c6f) Add fabcar external service sample (#136)
+* [7f5f5e6](https://github.com/hyperledger/fabric-samples/commit/7f5f5e6) [FAB-17504](https://jira.hyperledger.org/browse/FAB-17504) add Organizations..OrdererEndpoints and remove Orderer.Addresses (#125)
+* [f3fc08d](https://github.com/hyperledger/fabric-samples/commit/f3fc08d) Remove solo and kafka from test net configtx.yaml (#137)
+* [e17574d](https://github.com/hyperledger/fabric-samples/commit/e17574d) Add CA's to docker test network (#124)
+* [faac18e](https://github.com/hyperledger/fabric-samples/commit/faac18e) [FAB-17461](https://jira.hyperledger.org/browse/FAB-17461) Move off_chain_data sample to test network (#122)
+* [121a44a](https://github.com/hyperledger/fabric-samples/commit/121a44a) [FAB-17460](https://jira.hyperledger.org/browse/FAB-17460) Move High Throughput sample to test network (#112)
+* [a2f3a66](https://github.com/hyperledger/fabric-samples/commit/a2f3a66) Update docker image version
+* [e5b898c](https://github.com/hyperledger/fabric-samples/commit/e5b898c) Revert "first-network/scripts/*: Make Chaincode name configurable (#118)" (#131)
+* [9ef61e2](https://github.com/hyperledger/fabric-samples/commit/9ef61e2) first-network/scripts/*: Make Chaincode name configurable (#118)
+* [e204ebb](https://github.com/hyperledger/fabric-samples/commit/e204ebb) Remove reference to 2.0 beta (#111)
+* [3dbe116](https://github.com/hyperledger/fabric-samples/commit/3dbe116) [FAB-17456](https://jira.hyperledger.org/browse/FAB-17456) fabric-samples read ccp (#117)
+* [965ed1f](https://github.com/hyperledger/fabric-samples/commit/965ed1f) [FAB-17498](https://jira.hyperledger.org/browse/FAB-17498) Beta Images removal, test test-network (#121)
+* [403019e](https://github.com/hyperledger/fabric-samples/commit/403019e) [FAB-17495](https://jira.hyperledger.org/browse/FAB-17495) Remove Basic Network sample (#120)
+* [883ef99](https://github.com/hyperledger/fabric-samples/commit/883ef99) [FAB-17457](https://jira.hyperledger.org/browse/FAB-17457) Script correction (#119)
+* [b89ee34](https://github.com/hyperledger/fabric-samples/commit/b89ee34) Update Commercial Paper to v2.0 Lifecycle (#109)
+* [4208644](https://github.com/hyperledger/fabric-samples/commit/4208644) [FAB-17478](https://jira.hyperledger.org/browse/FAB-17478) Update commercial paper to use go api v1.0.0 (#115)
+* [0df5ed9](https://github.com/hyperledger/fabric-samples/commit/0df5ed9) [FAB-17477](https://jira.hyperledger.org/browse/FAB-17477) Update fabcar to use go api v1.0.0 (#116)
+* [571733f](https://github.com/hyperledger/fabric-samples/commit/571733f) [FAB-17447](https://jira.hyperledger.org/browse/FAB-17447) Update to 2.0.0 Libraries
+* [67b4ee7](https://github.com/hyperledger/fabric-samples/commit/67b4ee7) Add Org3 bugs in test network (#108)
+* [5b93dd0](https://github.com/hyperledger/fabric-samples/commit/5b93dd0) [FAB-17140](https://jira.hyperledger.org/browse/FAB-17140) Add go commercial paper contract (#102)
+* [4fe6a25](https://github.com/hyperledger/fabric-samples/commit/4fe6a25) [FABCI-482] Update Nexus URL's to Artifactory (#92)
+* [1488fbb](https://github.com/hyperledger/fabric-samples/commit/1488fbb) Add 1.x versions of fabric to blacklisted versions
+* [8ca279d](https://github.com/hyperledger/fabric-samples/commit/8ca279d) Add Support for Versioning NodeJS (#106)
+* [b3b5267](https://github.com/hyperledger/fabric-samples/commit/b3b5267) [FAB-17243](https://jira.hyperledger.org/browse/FAB-17243) Add support for Fabric CA for Org3 on the (#91)
+* [ce41ff7](https://github.com/hyperledger/fabric-samples/commit/ce41ff7) Remove references to vendoring chaincode from your gopath (#96)
+* [4235d30](https://github.com/hyperledger/fabric-samples/commit/4235d30) [FAB-17306](https://jira.hyperledger.org/browse/FAB-17306) Fix artifact names in test-network (#97)
+* [4c2a0a4](https://github.com/hyperledger/fabric-samples/commit/4c2a0a4) [FAB-16147](https://jira.hyperledger.org/browse/FAB-16147) Update Commercial Paper to work with v2 (#98)
+* [6d9fd6f](https://github.com/hyperledger/fabric-samples/commit/6d9fd6f) Remove Gerrit reference
+* [a026a4f](https://github.com/hyperledger/fabric-samples/commit/a026a4f) Fixed typo (#90)
+* [cdb0e8b](https://github.com/hyperledger/fabric-samples/commit/cdb0e8b) TYPO (#89)
+* [94ac8b6](https://github.com/hyperledger/fabric-samples/commit/94ac8b6) Update to use beta levels of modules (#88)
+* [d848633](https://github.com/hyperledger/fabric-samples/commit/d848633) [FAB-16844](https://jira.hyperledger.org/browse/FAB-16844) Correct BYFN CC name
+* [73267e1](https://github.com/hyperledger/fabric-samples/commit/73267e1) Fix test network bugs for adding org3
+* [5d58254](https://github.com/hyperledger/fabric-samples/commit/5d58254) [FAB-17145](https://jira.hyperledger.org/browse/FAB-17145) Add test network to Fabric Samples
+* [e9f2957](https://github.com/hyperledger/fabric-samples/commit/e9f2957) [FAB-17062](https://jira.hyperledger.org/browse/FAB-17062) Fix typos in Commercial Paper readme
+* [36694d0](https://github.com/hyperledger/fabric-samples/commit/36694d0) [FAB-17121](https://jira.hyperledger.org/browse/FAB-17121) Use new bootstrap config in orderer
+* [429f087](https://github.com/hyperledger/fabric-samples/commit/429f087) update fabcar go to new programming model
+* [1467086](https://github.com/hyperledger/fabric-samples/commit/1467086) Bump eslint-utils
+* [33f349a](https://github.com/hyperledger/fabric-samples/commit/33f349a) Remove Stalebot
+* [6af43bf](https://github.com/hyperledger/fabric-samples/commit/6af43bf) Change stalebot settings
+* [4880401](https://github.com/hyperledger/fabric-samples/commit/4880401) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [fe96f60](https://github.com/hyperledger/fabric-samples/commit/fe96f60) [FAB-16850](https://jira.hyperledger.org/browse/FAB-16850) Set up CI with Azure Pipelines
+* [81aabf4](https://github.com/hyperledger/fabric-samples/commit/81aabf4) [FAB-16849](https://jira.hyperledger.org/browse/FAB-16849) Various updates for Java version of FabCar
+* [a42b858](https://github.com/hyperledger/fabric-samples/commit/a42b858) Update FabCar to reflect wallet API changes
+* [890f9ea](https://github.com/hyperledger/fabric-samples/commit/890f9ea) [FAB-16713](https://jira.hyperledger.org/browse/FAB-16713) Fix npm audit warnings
+* [e48e804](https://github.com/hyperledger/fabric-samples/commit/e48e804) [FAB-16776](https://jira.hyperledger.org/browse/FAB-16776) Move BYFN up to V2_0 capabilities
+* [7b65a25](https://github.com/hyperledger/fabric-samples/commit/7b65a25) [IN-68] Add default GitHub SECURITY policy
+* [408e0e8](https://github.com/hyperledger/fabric-samples/commit/408e0e8) [FAB-16619](https://jira.hyperledger.org/browse/FAB-16619) Fix the policy warning
+* [670d446](https://github.com/hyperledger/fabric-samples/commit/670d446) [FAB-16668](https://jira.hyperledger.org/browse/FAB-16668) fabcar chaincode modify console output
+* [f2939e2](https://github.com/hyperledger/fabric-samples/commit/f2939e2) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for Commercial Paper sample
+* [3d19014](https://github.com/hyperledger/fabric-samples/commit/3d19014) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for FabCar sample
+* [e2b7cb7](https://github.com/hyperledger/fabric-samples/commit/e2b7cb7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Java 11 support for abstore sample
+* [db48612](https://github.com/hyperledger/fabric-samples/commit/db48612) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Increase chaincode execute timeout
+* [521a7ff](https://github.com/hyperledger/fabric-samples/commit/521a7ff) [FAB-16607](https://jira.hyperledger.org/browse/FAB-16607) Update FabCar to reflect CC updates
+* [c13a5ec](https://github.com/hyperledger/fabric-samples/commit/c13a5ec) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [3fad853](https://github.com/hyperledger/fabric-samples/commit/3fad853) [FAB-16528](https://jira.hyperledger.org/browse/FAB-16528) marbles private chaincode sync up
+* [8b9b82f](https://github.com/hyperledger/fabric-samples/commit/8b9b82f) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS
+* [a6ce915](https://github.com/hyperledger/fabric-samples/commit/a6ce915) [FAB-16487](https://jira.hyperledger.org/browse/FAB-16487) Update eslint
+* [48082cf](https://github.com/hyperledger/fabric-samples/commit/48082cf) [FAB-16362](https://jira.hyperledger.org/browse/FAB-16362) adding chaincode excution comments
+* [1d379f3](https://github.com/hyperledger/fabric-samples/commit/1d379f3) [FAB-16474](https://jira.hyperledger.org/browse/FAB-16474) marbles02 chaincode error
+* [18712ca](https://github.com/hyperledger/fabric-samples/commit/18712ca) [FAB-16133](https://jira.hyperledger.org/browse/FAB-16133) Remove Solo consensus from BYFN
+* [91c720a](https://github.com/hyperledger/fabric-samples/commit/91c720a) [FAB-16390](https://jira.hyperledger.org/browse/FAB-16390) Added filter for invalid transactions
+* [1d3e267](https://github.com/hyperledger/fabric-samples/commit/1d3e267) Redirect samples to fabric-{chaincode,protos}-go
+* [398a5b1](https://github.com/hyperledger/fabric-samples/commit/398a5b1) [FABCI-394] Remove AnsiColor Wrapper
+* [ce154e0](https://github.com/hyperledger/fabric-samples/commit/ce154e0) [FAB-16310](https://jira.hyperledger.org/browse/FAB-16310) Vendor Go dependencies in all samples
+* [6ea7c71](https://github.com/hyperledger/fabric-samples/commit/6ea7c71) [FAB-16285](https://jira.hyperledger.org/browse/FAB-16285) Update blacklisted versions in BYFN
+* [86cd831](https://github.com/hyperledger/fabric-samples/commit/86cd831) [FAB-16284](https://jira.hyperledger.org/browse/FAB-16284) Remove E2E file and -f option from BYFN
+* [0063abe](https://github.com/hyperledger/fabric-samples/commit/0063abe) Update stale script name in interest rate swaps
+* [3907507](https://github.com/hyperledger/fabric-samples/commit/3907507) [FAB-16277](https://jira.hyperledger.org/browse/FAB-16277) Update BYFN w/ Raft ports in Docker network
+* [33b0065](https://github.com/hyperledger/fabric-samples/commit/33b0065) [FAB-14813](https://jira.hyperledger.org/browse/FAB-14813) Channel event sample in fabric-samples
+* [b62d5bd](https://github.com/hyperledger/fabric-samples/commit/b62d5bd) [FAB-16132](https://jira.hyperledger.org/browse/FAB-16132) Remove Kafka consensus from BYFN
+* [9b14525](https://github.com/hyperledger/fabric-samples/commit/9b14525) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Update Commercial Paper for Java
+* [4158877](https://github.com/hyperledger/fabric-samples/commit/4158877) [FAB-16232](https://jira.hyperledger.org/browse/FAB-16232) Remove FabToken sample
+* [b6380cc](https://github.com/hyperledger/fabric-samples/commit/b6380cc) [FAB-16198](https://jira.hyperledger.org/browse/FAB-16198) Run "go mod vendor" for FabCar Go contract
+* [639848a](https://github.com/hyperledger/fabric-samples/commit/639848a) [FAB-16197](https://jira.hyperledger.org/browse/FAB-16197) Add connection profiles to .gitignore
+* [3996db5](https://github.com/hyperledger/fabric-samples/commit/3996db5) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) abstore node -> javascript
+* [14ac271](https://github.com/hyperledger/fabric-samples/commit/14ac271) [FAB-12219](https://jira.hyperledger.org/browse/FAB-12219) marbles02 node -> javascript
+* [13f16e5](https://github.com/hyperledger/fabric-samples/commit/13f16e5) [FGJ-4] CI tests for FabCar Java sample
+* [171a7d2](https://github.com/hyperledger/fabric-samples/commit/171a7d2) FGJ-4 Fabcar sample
+* [868f9d0](https://github.com/hyperledger/fabric-samples/commit/868f9d0) [FAB-15625](https://jira.hyperledger.org/browse/FAB-15625) Add UT for Simple Asset Chaincode
+* [597d150](https://github.com/hyperledger/fabric-samples/commit/597d150) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [583ff8f](https://github.com/hyperledger/fabric-samples/commit/583ff8f) Use renamed CheckCommitReadiness function
+* [750f937](https://github.com/hyperledger/fabric-samples/commit/750f937) [FAB-15213](https://jira.hyperledger.org/browse/FAB-15213) Add Java FabCar sample contract
+* [abbda95](https://github.com/hyperledger/fabric-samples/commit/abbda95) [FAB-15897](https://jira.hyperledger.org/browse/FAB-15897) Improve FabCar test logging
+* [dd8150a](https://github.com/hyperledger/fabric-samples/commit/dd8150a) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove versions from fabric-samples readme
+* [1387aa8](https://github.com/hyperledger/fabric-samples/commit/1387aa8) [FAB-15927](https://jira.hyperledger.org/browse/FAB-15927) Better expression for golang
+* [61c33d3](https://github.com/hyperledger/fabric-samples/commit/61c33d3) [FAB-15973](https://jira.hyperledger.org/browse/FAB-15973) use --output json on simulatecommit
+* [8bbdd0f](https://github.com/hyperledger/fabric-samples/commit/8bbdd0f) [FAB-15716](https://jira.hyperledger.org/browse/FAB-15716) Fix instructions for dev-mode
+* [0254d67](https://github.com/hyperledger/fabric-samples/commit/0254d67) QueryApprovalStatus -> SimulateCommitChaincodeDef
+* [c57d67c](https://github.com/hyperledger/fabric-samples/commit/c57d67c) [FAB-15782](https://jira.hyperledger.org/browse/FAB-15782) Sample Go CC should include deps
+* [6ba5a19](https://github.com/hyperledger/fabric-samples/commit/6ba5a19) Update to Go 1.12.5 in ci.properties
+* [1774a25](https://github.com/hyperledger/fabric-samples/commit/1774a25) [FAB-15723](https://jira.hyperledger.org/browse/FAB-15723) Fix script and instruction with ccenv
+* [6ae711c](https://github.com/hyperledger/fabric-samples/commit/6ae711c) [FAB-15717](https://jira.hyperledger.org/browse/FAB-15717) fix Error Unexpected end of JSON input
+* [5be56d3](https://github.com/hyperledger/fabric-samples/commit/5be56d3) [FAB-15104](https://jira.hyperledger.org/browse/FAB-15104) Remove scripts/bootstrap.sh
+* [779f8f3](https://github.com/hyperledger/fabric-samples/commit/779f8f3) [FAB-15649](https://jira.hyperledger.org/browse/FAB-15649)Fix Fabcar to install Chaincode on all peers
+* [7c5f5d3](https://github.com/hyperledger/fabric-samples/commit/7c5f5d3) [FAB-15199](https://jira.hyperledger.org/browse/FAB-15199) Update interest rate sample
+* [f0dca20](https://github.com/hyperledger/fabric-samples/commit/f0dca20) [FAB-14532](https://jira.hyperledger.org/browse/FAB-14532) Remove LL FabCar sample
+* [1ed1a10](https://github.com/hyperledger/fabric-samples/commit/1ed1a10) [FAB-15573](https://jira.hyperledger.org/browse/FAB-15573) Fix typo in fabric-samples-ci.md
+* [2e7fec9](https://github.com/hyperledger/fabric-samples/commit/2e7fec9) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [1e9e4c4](https://github.com/hyperledger/fabric-samples/commit/1e9e4c4) [FAB-9329](https://jira.hyperledger.org/browse/FAB-9329) Remove the unused variable in BYFN/EYFN
+* [964c09f](https://github.com/hyperledger/fabric-samples/commit/964c09f) [FAB-15601](https://jira.hyperledger.org/browse/FAB-15601) BYFN: Fix MAX_RETRY for couchdb
+* [41dca99](https://github.com/hyperledger/fabric-samples/commit/41dca99) [FAB-15127](https://jira.hyperledger.org/browse/FAB-15127) Update high throughput sample
+* [3fe014a](https://github.com/hyperledger/fabric-samples/commit/3fe014a) Use official CouchDB image
+* [f2d0fa0](https://github.com/hyperledger/fabric-samples/commit/f2d0fa0) [FAB-14487](https://jira.hyperledger.org/browse/FAB-14487) Make FabCar use BYFN, not basic-network
+* [e9c3649](https://github.com/hyperledger/fabric-samples/commit/e9c3649) [FAB-15276](https://jira.hyperledger.org/browse/FAB-15276) Fix license statements
+* [fbe4036](https://github.com/hyperledger/fabric-samples/commit/fbe4036) [FAB-14486](https://jira.hyperledger.org/browse/FAB-14486) Extend BYFN to opt skip chaincode deploy
+* [0c4141f](https://github.com/hyperledger/fabric-samples/commit/0c4141f) [FAB-14485](https://jira.hyperledger.org/browse/FAB-14485) Extend BYFN to opt inc cert authorities
+* [529b83b](https://github.com/hyperledger/fabric-samples/commit/529b83b) [FAB-14330](https://jira.hyperledger.org/browse/FAB-14330) Add connection profiles for BYFN and EYFN
+* [2c21c83](https://github.com/hyperledger/fabric-samples/commit/2c21c83) [FABN-1184] Update fabtoken/README.md
+* [5056a23](https://github.com/hyperledger/fabric-samples/commit/5056a23) [FABN-1184] Add CI script for fabtoken sample app
+* [5d6db95](https://github.com/hyperledger/fabric-samples/commit/5d6db95) Update maintainers for fabric-samples
+* [f527815](https://github.com/hyperledger/fabric-samples/commit/f527815) [FAB-15119](https://jira.hyperledger.org/browse/FAB-15119) Fix BYFN with Java chaincode
+* [8245252](https://github.com/hyperledger/fabric-samples/commit/8245252) [FABN-1184] Implement fabtoken sample app
+* [1bd1c2f](https://github.com/hyperledger/fabric-samples/commit/1bd1c2f) FABCI-284 Update CI Pipeline script
+* [c24abf9](https://github.com/hyperledger/fabric-samples/commit/c24abf9) [FAB-15022](https://jira.hyperledger.org/browse/FAB-15022) Basic-network support for new lifecycle
+* [b64fd45](https://github.com/hyperledger/fabric-samples/commit/b64fd45) [FAB-15051](https://jira.hyperledger.org/browse/FAB-15051) delStandard() function for high-throughput
+* [3e68a7e](https://github.com/hyperledger/fabric-samples/commit/3e68a7e) [FAB-14784](https://jira.hyperledger.org/browse/FAB-14784) Remove balance-transfer
+* [eb3fe08](https://github.com/hyperledger/fabric-samples/commit/eb3fe08) [FAB-14779](https://jira.hyperledger.org/browse/FAB-14779) QueryApprovalStatus step in byfn
+* [2777429](https://github.com/hyperledger/fabric-samples/commit/2777429) [FAB-14711](https://jira.hyperledger.org/browse/FAB-14711) update byfn with new lifecycle
+* [aec3389](https://github.com/hyperledger/fabric-samples/commit/aec3389) [FAB-12215](https://jira.hyperledger.org/browse/FAB-12215)WYFA:Remove chainId in tx proposal request
+* [b5d5026](https://github.com/hyperledger/fabric-samples/commit/b5d5026) [FAB-14633](https://jira.hyperledger.org/browse/FAB-14633) Remove apt-get from eyfn.sh
+* [efaadd3](https://github.com/hyperledger/fabric-samples/commit/efaadd3) [FAB-14531](https://jira.hyperledger.org/browse/FAB-14531) BYFN Raft with 5 nodes
+* [d63047c](https://github.com/hyperledger/fabric-samples/commit/d63047c) [FAB-14444](https://jira.hyperledger.org/browse/FAB-14444)
+* [7e3d428](https://github.com/hyperledger/fabric-samples/commit/7e3d428) [FAB-14369](https://jira.hyperledger.org/browse/FAB-14369)Fix dev mode failing to build Chaincode
+* [420ba23](https://github.com/hyperledger/fabric-samples/commit/420ba23) [FAB-12762](https://jira.hyperledger.org/browse/FAB-12762) Add etcd/raft consensus option to BYFN
+* [2b68c80](https://github.com/hyperledger/fabric-samples/commit/2b68c80) [FAB-14317](https://jira.hyperledger.org/browse/FAB-14317) Add default policies to org3
+* [f942010](https://github.com/hyperledger/fabric-samples/commit/f942010) [FAB-14268](https://jira.hyperledger.org/browse/FAB-14268) Make BYFN/EYFN ports match external ports
+* [4e2ce23](https://github.com/hyperledger/fabric-samples/commit/4e2ce23) [FAB-14271](https://jira.hyperledger.org/browse/FAB-14271) Add channel policies to channel config
+* [f26477c](https://github.com/hyperledger/fabric-samples/commit/f26477c) [FAB-11796](https://jira.hyperledger.org/browse/FAB-11796)high-throughput:Remove unnecessary prunesafe
+* [137327a](https://github.com/hyperledger/fabric-samples/commit/137327a) [FAB-14162](https://jira.hyperledger.org/browse/FAB-14162) Pin fabric-samples in master to "unstable"
+* [6007c09](https://github.com/hyperledger/fabric-samples/commit/6007c09) [FAB-13862](https://jira.hyperledger.org/browse/FAB-13862) Rename example02 ABstore
+* [94cb603](https://github.com/hyperledger/fabric-samples/commit/94cb603) [FAB-13933](https://jira.hyperledger.org/browse/FAB-13933) Fix misspellings
+* [a8a5539](https://github.com/hyperledger/fabric-samples/commit/a8a5539) Fix doc link Fix variable error
+* [b0cda61](https://github.com/hyperledger/fabric-samples/commit/b0cda61) [FAB-13769](https://jira.hyperledger.org/browse/FAB-13769) Add UT code to ABAC sample Chaincode
+* [c7438e1](https://github.com/hyperledger/fabric-samples/commit/c7438e1) [FAB-13668](https://jira.hyperledger.org/browse/FAB-13668) BYFN's container volume mapping is bad
+* [e48b2de](https://github.com/hyperledger/fabric-samples/commit/e48b2de) [FAB-13489](https://jira.hyperledger.org/browse/FAB-13489) fabric-samples add error msg
+* [8a458b5](https://github.com/hyperledger/fabric-samples/commit/8a458b5) [FAB-12056](https://jira.hyperledger.org/browse/FAB-12056) Private marbles cc use transient data
+* [6269941](https://github.com/hyperledger/fabric-samples/commit/6269941) Correct broken link
+* [461b6ab](https://github.com/hyperledger/fabric-samples/commit/461b6ab) FABC-781 Remove fabric-ca sample
+* [e9b9477](https://github.com/hyperledger/fabric-samples/commit/e9b9477) [FAB-13372](https://jira.hyperledger.org/browse/FAB-13372) Fabric-Samples return error msg
+* [e3da220](https://github.com/hyperledger/fabric-samples/commit/e3da220) [FAB-13433](https://jira.hyperledger.org/browse/FAB-13433) - Update Jenkinsfile configuration
+* [33db64e](https://github.com/hyperledger/fabric-samples/commit/33db64e) Configure Stale ProBot
+* [5cd277f](https://github.com/hyperledger/fabric-samples/commit/5cd277f) [FAB-11951](https://jira.hyperledger.org/browse/FAB-11951) Interest-rate swap example for SBE
+* [9567985](https://github.com/hyperledger/fabric-samples/commit/9567985) [FAB-13407](https://jira.hyperledger.org/browse/FAB-13407) Align fabric-samples with 1.4.0-rc2 release
+* [c7572aa](https://github.com/hyperledger/fabric-samples/commit/c7572aa) [FAB-13305](https://jira.hyperledger.org/browse/FAB-13305) Update scripts to pull latest artifacts
+* [ab46e35](https://github.com/hyperledger/fabric-samples/commit/ab46e35) [FAB-13283](https://jira.hyperledger.org/browse/FAB-13283) Update sample code for commercial paper
+* [f677821](https://github.com/hyperledger/fabric-samples/commit/f677821) [FAB-13232](https://jira.hyperledger.org/browse/FAB-13232) fix peer node start command
+* [6a7472e](https://github.com/hyperledger/fabric-samples/commit/6a7472e) [FAB-13126](https://jira.hyperledger.org/browse/FAB-13126) Align fabric-samples with 1.4.0-rc1 release
+* [445ccbc](https://github.com/hyperledger/fabric-samples/commit/445ccbc) [FAB-12880](https://jira.hyperledger.org/browse/FAB-12880) Move old prog model samples for FabCar
+* [5be62b5](https://github.com/hyperledger/fabric-samples/commit/5be62b5) [FAB-13207](https://jira.hyperledger.org/browse/FAB-13207) Remove incorrect discovery options
+* [fdbd92d](https://github.com/hyperledger/fabric-samples/commit/fdbd92d) [FAB-13206](https://jira.hyperledger.org/browse/FAB-13206) Remove dependencies on fabric-client
+* [eff0046](https://github.com/hyperledger/fabric-samples/commit/eff0046) [FAB-12877](https://jira.hyperledger.org/browse/FAB-12877) Add fabcar app using new prog model (JS)
+* [c184196](https://github.com/hyperledger/fabric-samples/commit/c184196) [FAB-12878](https://jira.hyperledger.org/browse/FAB-12878) Add fabcar app using new prog model (TS)
+* [e1a39e6](https://github.com/hyperledger/fabric-samples/commit/e1a39e6) [FAB-12724](https://jira.hyperledger.org/browse/FAB-12724) Upgrade from 1.3.x to 1.4.0
+* [c21bbba](https://github.com/hyperledger/fabric-samples/commit/c21bbba) Update samples to use new logging env variables
+* [7ad9f19](https://github.com/hyperledger/fabric-samples/commit/7ad9f19) [FAB-13011](https://jira.hyperledger.org/browse/FAB-13011) add kafka consensus type to byfn sample
+* [33f064f](https://github.com/hyperledger/fabric-samples/commit/33f064f) [FAB-13170](https://jira.hyperledger.org/browse/FAB-13170) Add memberOnlyRead to marbles sample
+* [928b72b](https://github.com/hyperledger/fabric-samples/commit/928b72b) [FAB-12875](https://jira.hyperledger.org/browse/FAB-12875) Add automated tests for fabcar sample
+* [5c087f1](https://github.com/hyperledger/fabric-samples/commit/5c087f1) [FAB-13046](https://jira.hyperledger.org/browse/FAB-13046) Update TypeScript contract dependencies
+* [3748983](https://github.com/hyperledger/fabric-samples/commit/3748983) [FAB-12879](https://jira.hyperledger.org/browse/FAB-12879) Update fabcar script for new contracts
+* [4fb3b57](https://github.com/hyperledger/fabric-samples/commit/4fb3b57) [FAB-12852](https://jira.hyperledger.org/browse/FAB-12852) Add fabcar contract w/ new prog model (TS)
+* [9facb42](https://github.com/hyperledger/fabric-samples/commit/9facb42) [FAB-12851](https://jira.hyperledger.org/browse/FAB-12851) Add fabcar contract w/ new prog model (JS)
+* [e67fcf1](https://github.com/hyperledger/fabric-samples/commit/e67fcf1) [FAB-12322](https://jira.hyperledger.org/browse/FAB-12322) Update commercial-paper sample
+* [fd6e2c4](https://github.com/hyperledger/fabric-samples/commit/fd6e2c4) [FAB-12703](https://jira.hyperledger.org/browse/FAB-12703) Fix misspelling "lauches"
+* [c05f172](https://github.com/hyperledger/fabric-samples/commit/c05f172) [FAB-12608](https://jira.hyperledger.org/browse/FAB-12608) Update pipeline script
+* [286861e](https://github.com/hyperledger/fabric-samples/commit/286861e) [FAB-12371](https://jira.hyperledger.org/browse/FAB-12371)Fix the abac sample to use new cid package
+* [24c5e47](https://github.com/hyperledger/fabric-samples/commit/24c5e47) [FAB-12026](https://jira.hyperledger.org/browse/FAB-12026) pagination samples for node marbles02
+* [6dc5ce5](https://github.com/hyperledger/fabric-samples/commit/6dc5ce5) [FAB-12587](https://jira.hyperledger.org/browse/FAB-12587) Fix for Query Block by block hash API
+* [df311ce](https://github.com/hyperledger/fabric-samples/commit/df311ce) [FAB-12173](https://jira.hyperledger.org/browse/FAB-12173) balance-transfer: Update anchor peers
+* [c925148](https://github.com/hyperledger/fabric-samples/commit/c925148) [FAB-12415](https://jira.hyperledger.org/browse/FAB-12415) samples for 1.3.0 (master cleanup)
+* [3a12c60](https://github.com/hyperledger/fabric-samples/commit/3a12c60) [FAB-12275](https://jira.hyperledger.org/browse/FAB-12275) Fix the warn in creating genesis block
+* [c6f6324](https://github.com/hyperledger/fabric-samples/commit/c6f6324) [FAB-12257](https://jira.hyperledger.org/browse/FAB-12257) allow balance-transfer for doscovery
+* [4445e8d](https://github.com/hyperledger/fabric-samples/commit/4445e8d) [FAB-12272](https://jira.hyperledger.org/browse/FAB-12272) Increase MAX_RETRY to 10
+* [33d333f](https://github.com/hyperledger/fabric-samples/commit/33d333f) [FAB-12190](https://jira.hyperledger.org/browse/FAB-12190) Update stable version in CI scripts
+* [edee638](https://github.com/hyperledger/fabric-samples/commit/edee638) [FAB-12184](https://jira.hyperledger.org/browse/FAB-12184) Prepare fabric-samples for 1.3.0-rc1
+* [514d456](https://github.com/hyperledger/fabric-samples/commit/514d456) [FAB-12170](https://jira.hyperledger.org/browse/FAB-12170) Fix dependency check in java chaincode
+* [9f80e47](https://github.com/hyperledger/fabric-samples/commit/9f80e47) [FAB-12119](https://jira.hyperledger.org/browse/FAB-12119) Fix groupId in java chaincodes
+* [3237229](https://github.com/hyperledger/fabric-samples/commit/3237229) [FAB-12073](https://jira.hyperledger.org/browse/FAB-12073) Fix Org3 peers CouchDB config.
+* [eece3d8](https://github.com/hyperledger/fabric-samples/commit/eece3d8) [FAB-12106](https://jira.hyperledger.org/browse/FAB-12106) Update fabric-ca build scripts
+* [f62952f](https://github.com/hyperledger/fabric-samples/commit/f62952f) [FABC-131] Change fabric-ca sample to build images
+* [4089786](https://github.com/hyperledger/fabric-samples/commit/4089786) [FAB-11867](https://jira.hyperledger.org/browse/FAB-11867) Develop Apps:Sample pt 2 -- application
+* [9ee57c6](https://github.com/hyperledger/fabric-samples/commit/9ee57c6) [FAB-11778](https://jira.hyperledger.org/browse/FAB-11778) Upgrade to v1.3.x from v1.2.x in byfn
+* [e7a1b76](https://github.com/hyperledger/fabric-samples/commit/e7a1b76) [FAB-9386](https://jira.hyperledger.org/browse/FAB-9386) Remove Marbles sample reference to Fauxton
+* [9c6acee](https://github.com/hyperledger/fabric-samples/commit/9c6acee) [FAB-12022](https://jira.hyperledger.org/browse/FAB-12022) Fix CI by increasing couchdb timeout
+* [4030ebd](https://github.com/hyperledger/fabric-samples/commit/4030ebd) [FAB-11397](https://jira.hyperledger.org/browse/FAB-11397) Adding java cc
+* [d776651](https://github.com/hyperledger/fabric-samples/commit/d776651) [FAB-11723](https://jira.hyperledger.org/browse/FAB-11723) Developing Apps: Sample pt 1 -- contract
+* [cbbbc78](https://github.com/hyperledger/fabric-samples/commit/cbbbc78) [FAB-11577](https://jira.hyperledger.org/browse/FAB-11577) Fix balance transfer to install Chaincode
+* [bfdc0b6](https://github.com/hyperledger/fabric-samples/commit/bfdc0b6) [FAB-11518](https://jira.hyperledger.org/browse/FAB-11518)
+* [5930dfc](https://github.com/hyperledger/fabric-samples/commit/5930dfc) [FAB-11488](https://jira.hyperledger.org/browse/FAB-11488) Update CI script
+* [ca6959c](https://github.com/hyperledger/fabric-samples/commit/ca6959c) [FAB-11311](https://jira.hyperledger.org/browse/FAB-11311) Update fabric image version
+* [0ca9e6e](https://github.com/hyperledger/fabric-samples/commit/0ca9e6e) FABN-833 Update Jenkinsfile to capture build artifacts
+* [a4a15cb](https://github.com/hyperledger/fabric-samples/commit/a4a15cb) [FAB-11220](https://jira.hyperledger.org/browse/FAB-11220) Samples - remove EventHub
+* [c4bdc68](https://github.com/hyperledger/fabric-samples/commit/c4bdc68) [FAB-8479](https://jira.hyperledger.org/browse/FAB-8479) Added Endorsement policy
+* [6edd320](https://github.com/hyperledger/fabric-samples/commit/6edd320) [FAB-9297](https://jira.hyperledger.org/browse/FAB-9297) fix README links and update bootstrap.sh
+* [75e2931](https://github.com/hyperledger/fabric-samples/commit/75e2931) [FAB-10811](https://jira.hyperledger.org/browse/FAB-10811) fabric-ca sample is broken on v1.2
+* [ad40e29](https://github.com/hyperledger/fabric-samples/commit/ad40e29) [FAB-10732](https://jira.hyperledger.org/browse/FAB-10732) BYFN upgrade to v1.2
+* [20ad472](https://github.com/hyperledger/fabric-samples/commit/20ad472) [FAB-10801](https://jira.hyperledger.org/browse/FAB-10801) format and styling for shell artifacts
+* [e95210e](https://github.com/hyperledger/fabric-samples/commit/e95210e) [FAB-10074](https://jira.hyperledger.org/browse/FAB-10074) CI Script for pipeline project type
+* [5956178](https://github.com/hyperledger/fabric-samples/commit/5956178) [FAB-6600](https://jira.hyperledger.org/browse/FAB-6600) Sample chaincode for private data
+* [21444ab](https://github.com/hyperledger/fabric-samples/commit/21444ab) [FAB-10297](https://jira.hyperledger.org/browse/FAB-10297) replace fabric-preload.sh
+* [3c32c52](https://github.com/hyperledger/fabric-samples/commit/3c32c52) [FAB-10346](https://jira.hyperledger.org/browse/FAB-10346) Ensure peers are in sync in eyfn
+* [2f30504](https://github.com/hyperledger/fabric-samples/commit/2f30504) [FAB-10340](https://jira.hyperledger.org/browse/FAB-10340) Fix broken link in chaincode-docker-devmode
+* [a603655](https://github.com/hyperledger/fabric-samples/commit/a603655) [FAB-10306](https://jira.hyperledger.org/browse/FAB-10306) Fix config in balance-transfer
+* [1cd059e](https://github.com/hyperledger/fabric-samples/commit/1cd059e) [FAB-8612](https://jira.hyperledger.org/browse/FAB-8612) Cleanup eyfn/byfn with --remove-orphans
+* [3031a8c](https://github.com/hyperledger/fabric-samples/commit/3031a8c) [FAB-10235](https://jira.hyperledger.org/browse/FAB-10235) Update BYFN to use V1_2 capability
+* [5cf1944](https://github.com/hyperledger/fabric-samples/commit/5cf1944) [FAB-10185](https://jira.hyperledger.org/browse/FAB-10185) Reorder for logical consistency
+* [e2f4f20](https://github.com/hyperledger/fabric-samples/commit/e2f4f20) [FAB-10176](https://jira.hyperledger.org/browse/FAB-10176) Fix invalid configtx.yaml documents
+* [cad2b98](https://github.com/hyperledger/fabric-samples/commit/cad2b98) [FAB-10073](https://jira.hyperledger.org/browse/FAB-10073) Fix broken link in samples/fabric-ca/README
+* [ace2e5a](https://github.com/hyperledger/fabric-samples/commit/ace2e5a) [FAB-10021](https://jira.hyperledger.org/browse/FAB-10021) Typo in balance-transfer/testAPIs.sh
+* [fa1d899](https://github.com/hyperledger/fabric-samples/commit/fa1d899) [FAB-9893](https://jira.hyperledger.org/browse/FAB-9893) Add .gitreview file for quicker Gerrit setup
+* [98561e0](https://github.com/hyperledger/fabric-samples/commit/98561e0) [FAB-9552](https://jira.hyperledger.org/browse/FAB-9552) Fix access of an un-declared variable
+* [b5dad8c](https://github.com/hyperledger/fabric-samples/commit/b5dad8c) [FAB-9317](https://jira.hyperledger.org/browse/FAB-9317) Update first-network with multi endorse
+* [146f7bc](https://github.com/hyperledger/fabric-samples/commit/146f7bc) [FAB-9572](https://jira.hyperledger.org/browse/FAB-9572) Fix README to match new version of BYFN
+* [010fcb5](https://github.com/hyperledger/fabric-samples/commit/010fcb5) [FAB-9326](https://jira.hyperledger.org/browse/FAB-9326) Clean up byfn.sh, drop "-m" option
+* [2d6386c](https://github.com/hyperledger/fabric-samples/commit/2d6386c) [FAB-8245](https://jira.hyperledger.org/browse/FAB-8245) change first network according to the fix
+* [3a5108f](https://github.com/hyperledger/fabric-samples/commit/3a5108f) [FAB-9475](https://jira.hyperledger.org/browse/FAB-9475) Fix a dead link in README
+* [77a6568](https://github.com/hyperledger/fabric-samples/commit/77a6568) [FAB-9294](https://jira.hyperledger.org/browse/FAB-9294) eliminate excess noise in BYFN
+* [41f5ab8](https://github.com/hyperledger/fabric-samples/commit/41f5ab8) [FAB-9406](https://jira.hyperledger.org/browse/FAB-9406) Typo in byfn.sh
+* [f5c2eb8](https://github.com/hyperledger/fabric-samples/commit/f5c2eb8) [FAB-9362](https://jira.hyperledger.org/browse/FAB-9362) add CONTRIBUTING.md and CODE_OF_CONDUCT.md
+* [9d518fb](https://github.com/hyperledger/fabric-samples/commit/9d518fb) [FAB-9330](https://jira.hyperledger.org/browse/FAB-9330) Refactor top-level .gitignore into subdirs
+* [a080da3](https://github.com/hyperledger/fabric-samples/commit/a080da3) [FAB-8958](https://jira.hyperledger.org/browse/FAB-8958) Add org3 removal to byfn.sh
+* [8fc0865](https://github.com/hyperledger/fabric-samples/commit/8fc0865) [FAB-9185](https://jira.hyperledger.org/browse/FAB-9185) add /config to .gitignore
+* [680ff01](https://github.com/hyperledger/fabric-samples/commit/680ff01) [FAB-8600](https://jira.hyperledger.org/browse/FAB-8600)Clear hyperledger-related containers only
+* [4f97717](https://github.com/hyperledger/fabric-samples/commit/4f97717) [FAB-8947](https://jira.hyperledger.org/browse/FAB-8947) Fabric-Samples remove package lock
+* [fcf62ad](https://github.com/hyperledger/fabric-samples/commit/fcf62ad) [FAB-8265](https://jira.hyperledger.org/browse/FAB-8265) Fixed spelling error in registerUser
+* [e4d7760](https://github.com/hyperledger/fabric-samples/commit/e4d7760) [ [FAB-8730](https://jira.hyperledger.org/browse/FAB-8730) ] hyphen breaks fabric-samples
+* [2bbb0a8](https://github.com/hyperledger/fabric-samples/commit/2bbb0a8) [FAB-8630](https://jira.hyperledger.org/browse/FAB-8630) byfn failing intermittently in CI
+* [823fb6b](https://github.com/hyperledger/fabric-samples/commit/823fb6b) [ [FAB-8679](https://jira.hyperledger.org/browse/FAB-8679) ] Permit samples to use RootCAs
+* [9f9fc7e](https://github.com/hyperledger/fabric-samples/commit/9f9fc7e) [FAB-8633](https://jira.hyperledger.org/browse/FAB-8633) Correct revoked error check
+* [f3b55c9](https://github.com/hyperledger/fabric-samples/commit/f3b55c9) [FAB-8621](https://jira.hyperledger.org/browse/FAB-8621) Remove Marbles index json data wrapper
+* [f110a6e](https://github.com/hyperledger/fabric-samples/commit/f110a6e) [FAB-8602](https://jira.hyperledger.org/browse/FAB-8602) Add volumes to first-network e2e yaml
+* [7362928](https://github.com/hyperledger/fabric-samples/commit/7362928) [FAB-8567](https://jira.hyperledger.org/browse/FAB-8567) Alt: Always use volumes for ledger (m)
+* [afb3d62](https://github.com/hyperledger/fabric-samples/commit/afb3d62) [FAB-8561](https://jira.hyperledger.org/browse/FAB-8561) Add note to readthedocs link in README
+* [10526d5](https://github.com/hyperledger/fabric-samples/commit/10526d5) [FAB-8564](https://jira.hyperledger.org/browse/FAB-8564) add debug commands to byfn
+* [e73a481](https://github.com/hyperledger/fabric-samples/commit/e73a481) [FAB-8568](https://jira.hyperledger.org/browse/FAB-8568) BYFN: Fix IMAGE_TAG for couchdb
+* [ffd7a25](https://github.com/hyperledger/fabric-samples/commit/ffd7a25) [FAB-6400](https://jira.hyperledger.org/browse/FAB-6400) Balance-transfer filtered events
+* [cba57da](https://github.com/hyperledger/fabric-samples/commit/cba57da) [FAB-8165](https://jira.hyperledger.org/browse/FAB-8165) Adding upgrade function to byfn
+* [c6166d6](https://github.com/hyperledger/fabric-samples/commit/c6166d6) [FAB-8540](https://jira.hyperledger.org/browse/FAB-8540) Add ledger persistance to first-network
+* [77e74b7](https://github.com/hyperledger/fabric-samples/commit/77e74b7) [FAB-8497](https://jira.hyperledger.org/browse/FAB-8497) Download images required for fabric-ca
+* [2bed1ef](https://github.com/hyperledger/fabric-samples/commit/2bed1ef) [FAB-8539](https://jira.hyperledger.org/browse/FAB-8539) Add version checking to first-network
+* [7b7fc09](https://github.com/hyperledger/fabric-samples/commit/7b7fc09) [FAB-8496](https://jira.hyperledger.org/browse/FAB-8496) allow modification of affiliations
+* [981efba](https://github.com/hyperledger/fabric-samples/commit/981efba) [FAB-8503](https://jira.hyperledger.org/browse/FAB-8503) Prevent CLI container from exiting
+* [c93268f](https://github.com/hyperledger/fabric-samples/commit/c93268f) [FAB-8518](https://jira.hyperledger.org/browse/FAB-8518) Not all compose files have IMAGE_TAG
+* [4f2cd8d](https://github.com/hyperledger/fabric-samples/commit/4f2cd8d) [FAB-8445](https://jira.hyperledger.org/browse/FAB-8445) Adding IMAGE_TAG option to byfn
+* [ca80163](https://github.com/hyperledger/fabric-samples/commit/ca80163) [FAB-8387](https://jira.hyperledger.org/browse/FAB-8387) Add gencrl to revoke command
+* [e379ac5](https://github.com/hyperledger/fabric-samples/commit/e379ac5) [FAB-8407](https://jira.hyperledger.org/browse/FAB-8407) Fix TLS bad cert error
+* [02ca1dc](https://github.com/hyperledger/fabric-samples/commit/02ca1dc) [FAB-7494](https://jira.hyperledger.org/browse/FAB-7494) Fix bootstrap peer config
+* [41e144f](https://github.com/hyperledger/fabric-samples/commit/41e144f) [FAB-8386](https://jira.hyperledger.org/browse/FAB-8386) eyfn fails to execute invoke on org3
+* [4ab098f](https://github.com/hyperledger/fabric-samples/commit/4ab098f) [FAB-8327](https://jira.hyperledger.org/browse/FAB-8327) Change eyfn.sh to use configtxlator cli
+* [24f35c1](https://github.com/hyperledger/fabric-samples/commit/24f35c1) [FAB-7750](https://jira.hyperledger.org/browse/FAB-7750) first network with support to [FAB-5664](https://jira.hyperledger.org/browse/FAB-5664)
+* [305d6f4](https://github.com/hyperledger/fabric-samples/commit/305d6f4) [FAB-8238](https://jira.hyperledger.org/browse/FAB-8238)wrong orderer/peer type in fabric-ca sam
+* [1d69e9e](https://github.com/hyperledger/fabric-samples/commit/1d69e9e) [FAB-7540](https://jira.hyperledger.org/browse/FAB-7540) Simplifies Reconfigure Your Network tutorial
+* [0daa8bc](https://github.com/hyperledger/fabric-samples/commit/0daa8bc) [FAB-8122](https://jira.hyperledger.org/browse/FAB-8122) Updated README.md file- balancetransfer
+* [652f074](https://github.com/hyperledger/fabric-samples/commit/652f074) [FAB-7342](https://jira.hyperledger.org/browse/FAB-7342) Enable client auth in fabric-ca sample
+* [90c2bfd](https://github.com/hyperledger/fabric-samples/commit/90c2bfd) [FAB-6934](https://jira.hyperledger.org/browse/FAB-6934) fix devmode sample for v1.1
+* [2c0c2c7](https://github.com/hyperledger/fabric-samples/commit/2c0c2c7) [FAB-7910](https://jira.hyperledger.org/browse/FAB-7910) Clean up chaincode containers in fabric-ca
+* [bbee1b2](https://github.com/hyperledger/fabric-samples/commit/bbee1b2) [FAB-7908](https://jira.hyperledger.org/browse/FAB-7908) Change hf.admin attr to admin
+* [dd12f88](https://github.com/hyperledger/fabric-samples/commit/dd12f88) [FAB-7834](https://jira.hyperledger.org/browse/FAB-7834) Add couchdb index to marbles02 sample
+* [25f6091](https://github.com/hyperledger/fabric-samples/commit/25f6091) [FAB-7836](https://jira.hyperledger.org/browse/FAB-7836) Fix "No identity type provided" Error
+* [5eb2fb2](https://github.com/hyperledger/fabric-samples/commit/5eb2fb2) [FAB-7653](https://jira.hyperledger.org/browse/FAB-7653) Fix incorrect chaincode location
+* [5a974a4](https://github.com/hyperledger/fabric-samples/commit/5a974a4) [FAB-7533](https://jira.hyperledger.org/browse/FAB-7533) Fix typo in balance-transfer sample
+* [038c496](https://github.com/hyperledger/fabric-samples/commit/038c496) [FAB-7592](https://jira.hyperledger.org/browse/FAB-7592) Give hf.Registrar attrs to admins
+* [cf79cd1](https://github.com/hyperledger/fabric-samples/commit/cf79cd1) [FAB-7584](https://jira.hyperledger.org/browse/FAB-7584) Removes copy of creds to keystore
+* [a0c1687](https://github.com/hyperledger/fabric-samples/commit/a0c1687) [FAB-7487](https://jira.hyperledger.org/browse/FAB-7487) Fix typo in node/fabcar.js
+* [1883ae9](https://github.com/hyperledger/fabric-samples/commit/1883ae9) [FAB-7527](https://jira.hyperledger.org/browse/FAB-7527) Improves BYFN
+* [e848216](https://github.com/hyperledger/fabric-samples/commit/e848216) [FAB-7511](https://jira.hyperledger.org/browse/FAB-7511) clear crypto material after clearing network
+* [54ffa5f](https://github.com/hyperledger/fabric-samples/commit/54ffa5f) [FAB-7241](https://jira.hyperledger.org/browse/FAB-7241) Fix chaincode-devmode
+* [c446510](https://github.com/hyperledger/fabric-samples/commit/c446510) [FAB-6550](https://jira.hyperledger.org/browse/FAB-6550) Sample app written in typescript
+* [69a127e](https://github.com/hyperledger/fabric-samples/commit/69a127e) [FAB-6254](https://jira.hyperledger.org/browse/FAB-6254) Fix the default CLI timeout
+* [7428f64](https://github.com/hyperledger/fabric-samples/commit/7428f64) [FAB-7160](https://jira.hyperledger.org/browse/FAB-7160) Samples - readme sample commands
+* [2474704](https://github.com/hyperledger/fabric-samples/commit/2474704) [FAB-6967](https://jira.hyperledger.org/browse/FAB-6967) Added steps to query by a revoked user
+* [948e237](https://github.com/hyperledger/fabric-samples/commit/948e237) [FAB-5913](https://jira.hyperledger.org/browse/FAB-5913)balance-transfer:Fix query response message
+* [7b76a7a](https://github.com/hyperledger/fabric-samples/commit/7b76a7a) [FAB-6632](https://jira.hyperledger.org/browse/FAB-6632) - Artifacts for BYFN reconfigure
+* [24ef9da](https://github.com/hyperledger/fabric-samples/commit/24ef9da) [FAB-6902](https://jira.hyperledger.org/browse/FAB-6902) [FAB-6904](https://jira.hyperledger.org/browse/FAB-6904) correct the sample for FAB-6904
+* [fd795d2](https://github.com/hyperledger/fabric-samples/commit/fd795d2) [FAB-6745](https://jira.hyperledger.org/browse/FAB-6745) Fix timing issue in sample
+* [a7be462](https://github.com/hyperledger/fabric-samples/commit/a7be462) [FAB-6895](https://jira.hyperledger.org/browse/FAB-6895) chaincode mounting issue
+* [7c23985](https://github.com/hyperledger/fabric-samples/commit/7c23985) [FAB-5221](https://jira.hyperledger.org/browse/FAB-5221) generateCerts should delete crypto
+* [1961835](https://github.com/hyperledger/fabric-samples/commit/1961835) [FAB-6870](https://jira.hyperledger.org/browse/FAB-6870) Update node modules versions
+* [bb3ac84](https://github.com/hyperledger/fabric-samples/commit/bb3ac84) [FAB-5363](https://jira.hyperledger.org/browse/FAB-5363) fabric-samples update balance
+* [6b2799e](https://github.com/hyperledger/fabric-samples/commit/6b2799e) [FAB-6568](https://jira.hyperledger.org/browse/FAB-6568) Fabric-Samples - update fabcar
+* [fafae55](https://github.com/hyperledger/fabric-samples/commit/fafae55) [FAB-6779](https://jira.hyperledger.org/browse/FAB-6779) Fix the error in fabric-ca
+* [caf5c33](https://github.com/hyperledger/fabric-samples/commit/caf5c33) [FAB-6050](https://jira.hyperledger.org/browse/FAB-6050) Adding fabric-ca sample
+* [44c204d](https://github.com/hyperledger/fabric-samples/commit/44c204d) [FAB-5898](https://jira.hyperledger.org/browse/FAB-5898) porting samples to node.js chaincode
+* [07a07d5](https://github.com/hyperledger/fabric-samples/commit/07a07d5) [FAB-6361](https://jira.hyperledger.org/browse/FAB-6361) Update license text in README
+* [c9b0c62](https://github.com/hyperledger/fabric-samples/commit/c9b0c62) [FAB-6292](https://jira.hyperledger.org/browse/FAB-6292) Fix spelling error
+* [9d70f31](https://github.com/hyperledger/fabric-samples/commit/9d70f31) Add high-throughput example to samples
+* [194b9b9](https://github.com/hyperledger/fabric-samples/commit/194b9b9) [FAB-5618](https://jira.hyperledger.org/browse/FAB-5618) Allow directory to contain spaces
+* [77b4090](https://github.com/hyperledger/fabric-samples/commit/77b4090) [FAB-5992](https://jira.hyperledger.org/browse/FAB-5992) Fix error in first-network dir
+
+## "v1.0.2"
+
+* [ba0a098](https://github.com/hyperledger/fabric-samples/commit/ba0a098) [FAB-5995](https://jira.hyperledger.org/browse/FAB-5995) Update samples to work with v1.0.2
+* [7cca09f](https://github.com/hyperledger/fabric-samples/commit/7cca09f) [FAB-5759](https://jira.hyperledger.org/browse/FAB-5759) fix byfn e2e test failures
+* [7f1c2f4](https://github.com/hyperledger/fabric-samples/commit/7f1c2f4) [FAB-5056](https://jira.hyperledger.org/browse/FAB-5056) enable couchdb test in byfn e2e script
+* [2571c96](https://github.com/hyperledger/fabric-samples/commit/2571c96) [FAB-56431](https://jira.hyperledger.org/browse/FAB-56431): Add dependency for couchdb into peer.
+* [be773d2](https://github.com/hyperledger/fabric-samples/commit/be773d2) [FAB-5603](https://jira.hyperledger.org/browse/FAB-5603) fixed missing f option on switch
+* [5921140](https://github.com/hyperledger/fabric-samples/commit/5921140) [FAB-5576](https://jira.hyperledger.org/browse/FAB-5576) -f flag to choose docker-compose on byfn.sh
+* [79cb041](https://github.com/hyperledger/fabric-samples/commit/79cb041) [FAB-5303](https://jira.hyperledger.org/browse/FAB-5303) Further balance-transfer code optimization
+* [92348cb](https://github.com/hyperledger/fabric-samples/commit/92348cb) [FAB-5453](https://jira.hyperledger.org/browse/FAB-5453) Enable CouchDB passwords in Fabric Samples
+* [2f4e9b8](https://github.com/hyperledger/fabric-samples/commit/2f4e9b8) [FAB-5130](https://jira.hyperledger.org/browse/FAB-5130) Invalid syntax in docker compose files
+* [7778837](https://github.com/hyperledger/fabric-samples/commit/7778837) [FAB-5394](https://jira.hyperledger.org/browse/FAB-5394) Introduce Delay as configurable variable
+* [134549a](https://github.com/hyperledger/fabric-samples/commit/134549a) [FAB-5412](https://jira.hyperledger.org/browse/FAB-5412) add fabric-preload.sh script
+* [a7e83fc](https://github.com/hyperledger/fabric-samples/commit/a7e83fc) [FAB-5392](https://jira.hyperledger.org/browse/FAB-5392) Fix path issue for Git Bash users
+* [6cce07c](https://github.com/hyperledger/fabric-samples/commit/6cce07c) [FAB-5155](https://jira.hyperledger.org/browse/FAB-5155)Fix README.md in "balance-transfer" example
+* [6422fc3](https://github.com/hyperledger/fabric-samples/commit/6422fc3) [FAB-5311](https://jira.hyperledger.org/browse/FAB-5311) Fix spelling error
+* [e0db341](https://github.com/hyperledger/fabric-samples/commit/e0db341) [FAB-5287](https://jira.hyperledger.org/browse/FAB-5287) NodeSDK - fix sample
+* [ca8fad3](https://github.com/hyperledger/fabric-samples/commit/ca8fad3) [FAB-5260](https://jira.hyperledger.org/browse/FAB-5260) Update samples to v1.0.0
+* [8f01c7f](https://github.com/hyperledger/fabric-samples/commit/8f01c7f) [FAB-5260](https://jira.hyperledger.org/browse/FAB-5260) Update balance-transfer sample to v1.0.0
+* [6899719](https://github.com/hyperledger/fabric-samples/commit/6899719) [FAB-5195](https://jira.hyperledger.org/browse/FAB-5195) byfn.sh help text is incorrect
+* [70bff28](https://github.com/hyperledger/fabric-samples/commit/70bff28) [FAB-5197](https://jira.hyperledger.org/browse/FAB-5197)Check all prop-responses in balance-transfer
+* [f9c2954](https://github.com/hyperledger/fabric-samples/commit/f9c2954) [FAB-5038](https://jira.hyperledger.org/browse/FAB-5038) Generate artifacts if they don't exist
+* [957a1ff](https://github.com/hyperledger/fabric-samples/commit/957a1ff) [FAB-5062](https://jira.hyperledger.org/browse/FAB-5062) Correct Gossip bootstrap setup in byfn
+* [d58af56](https://github.com/hyperledger/fabric-samples/commit/d58af56) [FAB-5031](https://jira.hyperledger.org/browse/FAB-5031) Fix e2e template typo
+* [04c9eff](https://github.com/hyperledger/fabric-samples/commit/04c9eff) [FAB-4996](https://jira.hyperledger.org/browse/FAB-4996) NodeSDK - move sample to fabric-samples
+* [6610584](https://github.com/hyperledger/fabric-samples/commit/6610584) [FAB-4994](https://jira.hyperledger.org/browse/FAB-4994) Fix formatting of js files
+* [e265cac](https://github.com/hyperledger/fabric-samples/commit/e265cac) [FAB-4988](https://jira.hyperledger.org/browse/FAB-4988) Update certs generated using rc1 binary
+* [ba777f3](https://github.com/hyperledger/fabric-samples/commit/ba777f3) [FAB-4371](https://jira.hyperledger.org/browse/FAB-4371) - Chaincode Dev Mode
+* [ca0b5d2](https://github.com/hyperledger/fabric-samples/commit/ca0b5d2) [FAB-4916](https://jira.hyperledger.org/browse/FAB-4916) add marbles02 chaincode
+* [b4beeff](https://github.com/hyperledger/fabric-samples/commit/b4beeff) [FAB-4927](https://jira.hyperledger.org/browse/FAB-4927) Modify TLS config
+* [803da71](https://github.com/hyperledger/fabric-samples/commit/803da71) [FAB-4430](https://jira.hyperledger.org/browse/FAB-4430) - WYFA scripts and node code
+* [241d08e](https://github.com/hyperledger/fabric-samples/commit/241d08e) [FAB-4910](https://jira.hyperledger.org/browse/FAB-4910) fix incorrect network spec
+* [fbc120f](https://github.com/hyperledger/fabric-samples/commit/fbc120f) [FAB-4851](https://jira.hyperledger.org/browse/FAB-4851) basic network sample
+* [3297865](https://github.com/hyperledger/fabric-samples/commit/3297865) [FAB-4073](https://jira.hyperledger.org/browse/FAB-4073) build your first network sample
+* [54cae68](https://github.com/hyperledger/fabric-samples/commit/54cae68) [FAB-4853](https://jira.hyperledger.org/browse/FAB-4853) initial content
+* [abb2b38](https://github.com/hyperledger/fabric-samples/commit/abb2b38) Initial empty repository
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000..8fcbd09
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: Apache-2.0
+
+# Fabric Samples Maintaners
+* @hyperledger/fabric-samples-maintainers
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..2f1f0e3
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,8 @@
+Code of Conduct Guidelines
+==========================
+
+Please review the Hyperledger [Code of
+Conduct](https://wiki.hyperledger.org/community/hyperledger-project-code-of-conduct)
+before participating. It is important that we keep things civil.
+
+ This work is licensed under a Creative Commons Attribution 4.0 International License .
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..6ac0fc4
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,18 @@
+## Contributing
+
+We welcome contributions to the Hyperledger Fabric Project in many forms, and
+there's always plenty to do!
+
+Please visit the
+[contributors guide](http://hyperledger-fabric.readthedocs.io/en/latest/CONTRIBUTING.html) in the
+docs to learn how to make contributions to this exciting project.
+
+## Code of Conduct Guidelines
+
+See our [Code of Conduct Guidelines](./CODE_OF_CONDUCT.md).
+
+## Maintainers
+
+Should you have any questions or concerns, please reach out to one of the project's [Maintainers](./MAINTAINERS.md).
+
+ This work is licensed under a Creative Commons Attribution 4.0 International License .
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8f71f43
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
new file mode 100644
index 0000000..a7dc8aa
--- /dev/null
+++ b/MAINTAINERS.md
@@ -0,0 +1,20 @@
+Maintainers
+===========
+
+fabric-samples uses a non-author code review policy, requiring a single approval from a non-author maintainer.
+
+| Name | GitHub | Chat | email |
+|---------------------------|------------------|----------------|-------------------------------------|
+| Arnaud Le Hors | lehors | lehors | lehors@us.ibm.com |
+| Bret Harrison | harrisob | bretharrison | harrisob@us.ibm.com |
+| Chris Ferris | christo4ferris | cbf | chris.ferris@gmail.com |
+| Dave Enyeart | denyeart | dave.enyeart | enyeart@us.ibm.com |
+| Gari Singh | mastersingh24 | mastersingh24 | gari.r.singh@gmail.com |
+| Jason Yellick | jyellick | jyellick | jyellick@us.ibm.com |
+| Matthew B White | mbwhite | mbwhite | whitemat@uk.ibm.com |
+| Nikhil Gupta | nikhil550 | negupta | nikhilg550@gmail.com |
+| Simon Stone | sstone1 | sstone1 | sstone1@uk.ibm.com |
+
+Also: Please see the [Release Manager section](https://github.com/hyperledger/fabric/blob/master/MAINTAINERS.md)
+
+ This work is licensed under a Creative Commons Attribution 4.0 International License .
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0edf4ae
--- /dev/null
+++ b/README.md
@@ -0,0 +1,59 @@
+[//]: # (SPDX-License-Identifier: CC-BY-4.0)
+
+# Hyperledger Fabric Samples
+
+You can use Fabric samples to get started working with Hyperledger Fabric, explore important Fabric features, and learn how to build applications that can interact with blockchain networks using the Fabric SDKs. To learn more about Hyperledger Fabric, visit the [Fabric documentation](https://hyperledger-fabric.readthedocs.io/en/latest).
+
+## Getting started with the Fabric samples
+
+To use the Fabric samples, you need to download the Fabric Docker images and the Fabric CLI tools. First, make sure that you have installed all of the [Fabric prerequisites](https://hyperledger-fabric.readthedocs.io/en/latest/prereqs.html). You can then follow the instructions to [Install the Fabric Samples, Binaries, and Docker Images](https://hyperledger-fabric.readthedocs.io/en/latest/install.html) in the Fabric documentation. In addition to downloading the Fabric images and tool binaries, the Fabric samples will also be cloned to your local machine.
+
+## Test network
+
+The [Fabric test network](test-network) in the samples repository provides a Docker Compose based test network with two
+Organization peers and an ordering service node. You can use it on your local machine to run the samples listed below.
+You can also use it to deploy and test your own Fabric chaincodes and applications. To get started, see
+the [test network tutorial](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html).
+
+## Asset transfer samples and tutorials
+
+The asset transfer series provides a series of sample smart contracts and applications to demonstrate how to store and transfer assets using Hyperledger Fabric.
+Each sample and associated tutorial in the series demonstrates a different core capability in Hyperledger Fabric. The **Basic** sample provides an introduction on how
+to write smart contracts and how to interact with a Fabric network using the Fabric SDKs. The **Ledger queries**, **Private data**, and **State-based endorsement**
+samples demonstrate these additional capabilities. Finally, the **Secured agreement** sample demonstrates how to bring all the capabilities together to securely
+transfer an asset in a more realistic transfer scenario.
+
+| **Smart Contract** | **Description** | **Tutorial** | **Smart contract languages** | **Application languages** |
+| -----------|------------------------------|----------|---------|---------|
+| [Basic](asset-transfer-basic) | The Basic sample smart contract that allows you to create and transfer an asset by putting data on the ledger and retrieving it. This sample is recommended for new Fabric users. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/latest/write_first_app.html) | Go, JavaScript, TypeScript, Java | Go, JavaScript, TypeScript, Java |
+| [Ledger queries](asset-transfer-ledger-queries) | The ledger queries sample demonstrates range queries and transaction updates using range queries (applicable for both LevelDB and CouchDB state databases), and how to deploy an index with your chaincode to support JSON queries (applicable for CouchDB state database only). | [Using CouchDB](https://hyperledger-fabric.readthedocs.io/en/latest/couchdb_tutorial.html) | Go, JavaScript | Java, JavaScript |
+| [Private data](asset-transfer-private-data) | This sample demonstrates the use of private data collections, how to manage private data collections with the chaincode lifecycle, and how the private data hash can be used to verify private data on the ledger. It also demonstrates how to control asset updates and transfers using client-based ownership and access control. | [Using Private Data](https://hyperledger-fabric.readthedocs.io/en/latest/private_data_tutorial.html) | Go, Java | JavaScript |
+| [State-Based Endorsement](asset-transfer-sbe) | This sample demonstrates how to override the chaincode-level endorsement policy to set endorsement policies at the key-level (data/asset level). | [Using State-based endorsement](https://github.com/hyperledger/fabric-samples/tree/master/asset-transfer-sbe) | Java, TypeScript | JavaScript |
+| [Secured agreement](asset-transfer-secured-agreement) | Smart contract that uses implicit private data collections, state-based endorsement, and organization-based ownership and access control to keep data private and securely transfer an asset with the consent of both the current owner and buyer. | [Secured asset transfer](https://hyperledger-fabric.readthedocs.io/en/latest/secured_asset_transfer/secured_private_asset_transfer_tutorial.html) | Go | JavaScript |
+| [Events](asset-transfer-events) | The events sample demonstrates how smart contracts can emit events that are read by the applications interacting with the network. | [README](asset-transfer-events/README.md) | JavaScript, Java | JavaScript |
+| [Attribute-based access control](asset-transfer-abac) | Demonstrates the use of attribute and identity based access control using a simple asset transfer scenario | [README](asset-transfer-abac/README.md) | Go | None |
+
+
+
+## Additional samples
+
+Additional samples demonstrate various Fabric use cases and application patterns.
+
+| **Sample** | **Description** | **Documentation** |
+| -------------|------------------------------|------------------|
+| [Commercial paper](commercial-paper) | Explore a use case and detailed application development tutorial in which two organizations use a blockchain network to trade commercial paper. | [Commercial paper tutorial](https://hyperledger-fabric.readthedocs.io/en/latest/tutorial/commercial_paper.html) |
+| [Off chain data](off_chain_data) | Learn how to use the Peer channel-based event services to build an off-chain database for reporting and analytics. | [Peer channel-based event services](https://hyperledger-fabric.readthedocs.io/en/latest/peer_event_services.html) |
+| [Token ERC-20](token-erc-20) | Smart contract demonstrating how to create and transfer fungible tokens using an account-based model. | [README](token-erc-20/README.md) |
+| [Token UTXO](token-utxo) | Smart contract demonstrating how to create and transfer fungible tokens using a UTXO (unspent transaction output) model. | [README](token-utxo/README.md) |
+| [High throughput](high-throughput) | Learn how you can design your smart contract to avoid transaction collisions in high volume environments. | [README](high-throughput/README.md) |
+| [Auction](auction) | Run an auction where bids are kept private until the auction is closed, after which users can reveal their bid | [README](auction/README.md) |
+| [Chaincode](chaincode) | A set of other sample smart contracts, many of which were used in tutorials prior to the asset transfer sample series. | |
+| [Interest rate swaps](interest_rate_swaps) | **Deprecated in favor of state based endorsement asset transfer sample** | |
+| [Fabcar](fabcar) | **Deprecated in favor of basic asset transfer sample** | |
+
+## License
+
+Hyperledger Project source code files are made available under the Apache
+License, Version 2.0 (Apache-2.0), located in the [LICENSE](LICENSE) file.
+Hyperledger Project documentation files are made available under the Creative
+Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..91509aa
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# Hyperledger Security Policy
+
+## Reporting a Security Bug
+
+If you think you have discovered a security issue in any of the Hyperledger projects, we'd love to hear from you. We will take all security bugs seriously and if confirmed upon investigation we will patch it within a reasonable amount of time and release a public security bulletin discussing the impact and credit the discoverer.
+
+There are two ways to report a security bug. The easiest is to email a description of the flaw and any related information (e.g. reproduction steps, version) to [security at hyperledger dot org](mailto:security@hyperledger.org).
+
+The other way is to file a confidential security bug in our [JIRA bug tracking system](https://jira.hyperledger.org). Be sure to set the “Security Level†to “Security issueâ€.
+
+The process by which the Hyperledger Security Team handles security bugs is documented further in our [Defect Response page](https://wiki.hyperledger.org/display/HYP/Defect+Response) on our [wiki](https://wiki.hyperledger.org).
+
diff --git a/asset-transfer-abac/README.md b/asset-transfer-abac/README.md
new file mode 100644
index 0000000..46350d8
--- /dev/null
+++ b/asset-transfer-abac/README.md
@@ -0,0 +1,173 @@
+# Attribute based access control
+
+The `asset-transfer-abac` sample demonstrates the use of Attribute-based access control within the context of a simple asset transfer scenario. The sample also uses authorization based on individual client identities to allow the users that interact with the network to own assets on the blockchain ledger.
+
+Attribute-Based Access Control (ABAC) refers to the ability to restrict access to certain functionality within a smart contract based on the attributes within a users certificate. For example, you may want certain functions within a smart contract to be used only by application administrators. ABAC allows organizations to provide elevated access to certain users without requiring those users be administrators of the network. For more information, see [Attribute-based access control](https://hyperledger-fabric-ca.readthedocs.io/en/latest/users-guide.html#attribute-based-access-control) in the Fabric CA users guide.
+
+The `asset-transfer-abac` smart contract allows you to create assets that can be updated or transferred by the asset owner. However, the ability to create or remove assets from the ledger is restricted to identities with the `abac.creator=true` attribute. The identity that creates the asset is assigned as the asset owner. Only the owner can transfer the asset to a new owner or update the asset properties. In the course of the tutorial, we will use the Fabric CA client to create identities with the attribute required to create a new asset. We will then transfer the asset to another identity and demonstrate how the `GetID()` function can be used to enforce asset ownership. We will then use the owner identity to delete the asset. Both Attribute based access control and the `GetID()` function are provided by the [Client Identity Chaincode Library](https://github.com/hyperledger/fabric-chaincode-go/blob/master/pkg/cid/README.md).
+
+## Start the network and deploy the smart contract
+
+We can use the Fabric test network to deploy and interact with the `asset-transfer-abac` smart contract. Run the following command to change into the test network directory and bring down any running nodes:
+```
+cd fabric-samples/test-network
+./network.sh down
+```
+
+Run the following command to deploy the test network using Certificate Authorities:
+```
+./network.sh up createChannel -ca
+```
+
+You can then use the test network script to deploy the `asset-transfer-abac` smart contract to a channel on the network:
+```
+./network.sh deployCC -ccn abac -ccp ../asset-transfer-abac/chaincode-go/ -ccl go
+```
+
+## Register identities with attributes
+
+We can use the one of the test network Certificate Authorities to register and enroll identities with the attribute of `abac.creator=true`. First, we need to set the following environment variables in order to use the Fabric CA client.
+```
+export PATH=${PWD}/../bin:${PWD}:$PATH
+export FABRIC_CFG_PATH=$PWD/../config/
+```
+
+We will create the identities using the Org1 CA. Set the Fabric CA client home to the MSP of the Org1 CA admin:
+```
+export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org1.example.com/
+```
+
+There are two ways to generate certificates with attributes added. We will use both methods and create two identities in the process. The first method is to specify that the attribute be added to the certificate by default when the identity is registered. The following command will register an identity named creator1 with the attribute of `abac.creator=true`.
+
+```
+fabric-ca-client register --id.name creator1 --id.secret creator1pw --id.type client --id.affiliation org1 --id.attrs 'abac.creator=true:ecert' --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
+```
+
+The `ecert` suffix adds the attribute to the certificate automatically when the identity is enrolled. As a result, the following enroll command will contain the attribute that was provided in the registration command.
+
+```
+fabric-ca-client enroll -u https://creator1:creator1pw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/users/creator1@org1.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
+```
+
+Now that we have enrolled the identity, run the command below to copy the Node OU configuration file into the creator1 MSP folder.
+```
+cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/creator1@org1.example.com/msp/config.yaml
+```
+
+The second method is to request that the attribute be added upon enrollment. The following command will register an identity named creator2 with the same `abac.creator` attribute.
+```
+fabric-ca-client register --id.name creator2 --id.secret creator2pw --id.type client --id.affiliation org1 --id.attrs 'abac.creator=true:' --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
+```
+
+The following enroll command will add the attribute to the certificate:
+
+```
+fabric-ca-client enroll -u https://creator2:creator2pw@localhost:7054 --caname ca-org1 --enrollment.attrs "abac.creator" -M ${PWD}/organizations/peerOrganizations/org1.example.com/users/creator2@org1.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
+```
+
+Run the command below to copy the Node OU configuration file into the creator2 MSP folder.
+```
+cp ${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml ${PWD}/organizations/peerOrganizations/org1.example.com/users/creator2@org1.example.com/msp/config.yaml
+```
+
+## Create an asset
+
+You can use either identity with the `abac.creator=true` attribute to create an asset using the `asset-transfer-abac` smart contract. We will set the following environment variables to use the first identity that was generated, creator1:
+
+```
+export CORE_PEER_TLS_ENABLED=true
+export CORE_PEER_LOCALMSPID="Org1MSP"
+export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/creator1@org1.example.com/msp
+export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
+export CORE_PEER_ADDRESS=localhost:7051
+export TARGET_TLS_OPTIONS="-o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt"
+```
+
+Run the following command to create Asset1:
+```
+peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n abac -c '{"function":"CreateAsset","Args":["Asset1","blue","20","100"]}'
+```
+
+You can use the command below to query the asset on the ledger:
+```
+peer chaincode query -C mychannel -n abac -c '{"function":"ReadAsset","Args":["Asset1"]}'
+```
+The result will list the creator1 identity as the asset owner. The `GetID()` API reads the name and issuer from the certificate of the identity that submitted the transaction and assigns that identity as the asset owner:
+```
+{"ID":"Asset1","color":"blue","size":20,"owner":"x509::CN=creator1,OU=client+OU=org1,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.example.com,O=org1.example.com,L=Durham,ST=North Carolina,C=US","appraisedValue":100}
+```
+
+## Transfer the asset
+
+As the owner of Asset1, the creator1 identity has the ability to transfer the asset to another owner. In order to transfer the asset, the owner needs to provide the name and issuer of the new owner to the `TransferAsset` function. The `asset-transfer-abac` smart contract has a `GetSubmittingClientIdentity` function that allows users to retrieve their certificate information and provide it to the asset owner out of band (we omit this step). Issue the command below to transfer Asset1 to the user1 identity from Org1 that was created when the test network was deployed:
+```
+export RECIPIENT="x509::CN=user1,OU=client,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.example.com,O=org1.example.com,L=Durham,ST=North Carolina,C=US"
+peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n abac -c '{"function":"TransferAsset","Args":["Asset1","'"$RECIPIENT"'"]}'
+```
+Query the ledger to verify that the asset has a new owner:
+```
+peer chaincode query -C mychannel -n abac -c '{"function":"ReadAsset","Args":["Asset1"]}'
+```
+We can see that Asset1 with is now owned by User1:
+```
+{"ID":"Asset1","color":"blue","size":20,"owner":"x509::CN=user1,OU=client,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.example.com,O=org1.example.com,L=Durham,ST=North Carolina,C=US","appraisedValue":100}
+```
+
+## Update the asset
+
+Now that the asset has been transferred, the new owner can update the asset properties. The smart contract uses the `GetID()` API to ensure that the update is being submitted by the asset owner. To demonstrate the difference between identity and attribute based access control, lets try to update the asset using the creator1 identity first:
+```
+peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n abac -c '{"function":"UpdateAsset","Args":["Asset1","green","20","100"]}'
+```
+
+Even though creator1 can create new assets, the smart contract detects that the transaction was not submitted by the identity that owns the asset, user1. The command returns the following error:
+```
+Error: endorsement failure during invoke. response: status:500 message:"submitting client not authorized to update asset, does not own asset"
+```
+
+Run the following command to operate as the asset owner by setting the MSP path to User1:
+```
+export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp
+```
+
+We can now update the asset. Run the following command to change the asset color from blue to green. All other aspects of the asset will remain unchanged.
+```
+peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n abac -c '{"function":"UpdateAsset","Args":["Asset1","green","20","100"]}'
+```
+Run the query command again to verify that the asset has changed color:
+```
+peer chaincode query -C mychannel -n abac -c '{"function":"ReadAsset","Args":["Asset1"]}'
+```
+The result will display that Asset1 is now green:
+```
+{"ID":"Asset1","color":"green","size":20,"owner":"x509::CN=user1,OU=client,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.example.com,O=org1.example.com,L=Durham,ST=North Carolina,C=US","appraisedValue":100}
+```
+
+## Delete the asset
+
+The owner also has the ability to delete the asset. Run the following command to remove Asset1 from the ledger:
+```
+peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n abac -c '{"function":"DeleteAsset","Args":["Asset1"]}'
+```
+
+If you query the ledger once more, you will see that Asset1 no longer exists:
+```
+peer chaincode query -C mychannel -n abac -c '{"function":"ReadAsset","Args":["Asset1"]}'
+```
+
+While we are operating as User1, we can demonstrate attribute based access control by trying to create an asset using an identity without the `abac.creator=true` attribute. Run the following command to try to create Asset1 as User1:
+```
+peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n abac -c '{"function":"CreateAsset","Args":["Asset2","red","20","100"]}'
+```
+
+The smart contract will return the following error:
+```
+Error: endorsement failure during invoke. response: status:500 message:"submitting client not authorized to create asset, does not have abac.creator role"
+```
+
+## Clean up
+
+When you are finished, you can run the following command to bring down the test network:
+```
+./network.sh down
+```
diff --git a/asset-transfer-abac/chaincode-go/go.mod b/asset-transfer-abac/chaincode-go/go.mod
new file mode 100644
index 0000000..4144e4c
--- /dev/null
+++ b/asset-transfer-abac/chaincode-go/go.mod
@@ -0,0 +1,5 @@
+module github.com/hyperledger/fabric-samples/asset-transfer-abac/chaincode-go
+
+go 1.15
+
+require github.com/hyperledger/fabric-contract-api-go v1.1.1
diff --git a/asset-transfer-abac/chaincode-go/go.sum b/asset-transfer-abac/chaincode-go/go.sum
new file mode 100644
index 0000000..116a448
--- /dev/null
+++ b/asset-transfer-abac/chaincode-go/go.sum
@@ -0,0 +1,138 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.1 h1:gDhOC18gjgElNZ85kFWsbCQq95hyUP/21n++m0Sv6B0=
+github.com/hyperledger/fabric-contract-api-go v1.1.1/go.mod h1:+39cWxbh5py3NtXpRA63rAH7NzXyED+QJx1EZr0tJPo=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/asset-transfer-abac/chaincode-go/smart-contract/abac.go b/asset-transfer-abac/chaincode-go/smart-contract/abac.go
new file mode 100644
index 0000000..746b09c
--- /dev/null
+++ b/asset-transfer-abac/chaincode-go/smart-contract/abac.go
@@ -0,0 +1,214 @@
+package abac
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// SmartContract provides functions for managing an Asset
+type SmartContract struct {
+ contractapi.Contract
+}
+
+// Asset describes basic details of what makes up a simple asset
+type Asset struct {
+ ID string `json:"ID"`
+ Color string `json:"color"`
+ Size int `json:"size"`
+ Owner string `json:"owner"`
+ AppraisedValue int `json:"appraisedValue"`
+}
+
+// CreateAsset issues a new asset to the world state with given details.
+func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, appraisedValue int) error {
+
+ // Demonstrate the use of Attribute-Based Access Control (ABAC) by checking
+ // to see if the caller has the "abac.creator" attribute with a value of true;
+ // if not, return an error.
+ //
+ err := ctx.GetClientIdentity().AssertAttributeValue("abac.creator", "true")
+ if err != nil {
+ return fmt.Errorf("submitting client not authorized to create asset, does not have abac.creator role")
+ }
+
+ exists, err := s.AssetExists(ctx, id)
+ if err != nil {
+ return err
+ }
+ if exists {
+ return fmt.Errorf("the asset %s already exists", id)
+ }
+
+ // Get ID of submitting client identity
+ clientID, err := s.GetSubmittingClientIdentity(ctx)
+ if err != nil {
+ return err
+ }
+
+ asset := Asset{
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: clientID,
+ AppraisedValue: appraisedValue,
+ }
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// UpdateAsset updates an existing asset in the world state with provided parameters.
+func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, newColor string, newSize int, newValue int) error {
+
+ asset, err := s.ReadAsset(ctx, id)
+ if err != nil {
+ return err
+ }
+
+ clientID, err := s.GetSubmittingClientIdentity(ctx)
+ if err != nil {
+ return err
+ }
+
+ if clientID != asset.Owner {
+ return fmt.Errorf("submitting client not authorized to update asset, does not own asset")
+ }
+
+ asset.Color = newColor
+ asset.Size = newSize
+ asset.AppraisedValue = newValue
+
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// DeleteAsset deletes a given asset from the world state.
+func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
+
+ asset, err := s.ReadAsset(ctx, id)
+ if err != nil {
+ return err
+ }
+
+ clientID, err := s.GetSubmittingClientIdentity(ctx)
+ if err != nil {
+ return err
+ }
+
+ if clientID != asset.Owner {
+ return fmt.Errorf("submitting client not authorized to update asset, does not own asset")
+ }
+
+ return ctx.GetStub().DelState(id)
+}
+
+// TransferAsset updates the owner field of asset with given id in world state.
+func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
+
+ asset, err := s.ReadAsset(ctx, id)
+ if err != nil {
+ return err
+ }
+
+ clientID, err := s.GetSubmittingClientIdentity(ctx)
+ if err != nil {
+ return err
+ }
+
+ if clientID != asset.Owner {
+ return fmt.Errorf("submitting client not authorized to update asset, does not own asset")
+ }
+
+ asset.Owner = newOwner
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// ReadAsset returns the asset stored in the world state with given id.
+func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
+
+ assetJSON, err := ctx.GetStub().GetState(id)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read from world state: %v", err)
+ }
+ if assetJSON == nil {
+ return nil, fmt.Errorf("the asset %s does not exist", id)
+ }
+
+ var asset Asset
+ err = json.Unmarshal(assetJSON, &asset)
+ if err != nil {
+ return nil, err
+ }
+
+ return &asset, nil
+}
+
+// GetAllAssets returns all assets found in world state
+func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
+ // range query with empty string for startKey and endKey does an
+ // open-ended query of all assets in the chaincode namespace.
+ resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ var assets []*Asset
+ for resultsIterator.HasNext() {
+ queryResponse, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ var asset Asset
+ err = json.Unmarshal(queryResponse.Value, &asset)
+ if err != nil {
+ return nil, err
+ }
+ assets = append(assets, &asset)
+ }
+
+ return assets, nil
+}
+
+// AssetExists returns true when asset with given ID exists in world state
+func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
+
+ assetJSON, err := ctx.GetStub().GetState(id)
+ if err != nil {
+ return false, fmt.Errorf("failed to read from world state: %v", err)
+ }
+
+ return assetJSON != nil, nil
+}
+
+// GetSubmittingClientIdentity returns the name and issuer of the identity that
+// invokes the smart contract. This function base64 decodes the identity string
+// before returning the value to the client or smart contract.
+func (s *SmartContract) GetSubmittingClientIdentity(ctx contractapi.TransactionContextInterface) (string, error) {
+
+ b64ID, err := ctx.GetClientIdentity().GetID()
+ if err != nil {
+ return "", fmt.Errorf("Failed to read clientID: %v", err)
+ }
+ decodeID, err := base64.StdEncoding.DecodeString(b64ID)
+ if err != nil {
+ return "", fmt.Errorf("failed to base64 decode clientID: %v", err)
+ }
+ return string(decodeID), nil
+}
diff --git a/asset-transfer-abac/chaincode-go/smartContract.go b/asset-transfer-abac/chaincode-go/smartContract.go
new file mode 100644
index 0000000..c54c78b
--- /dev/null
+++ b/asset-transfer-abac/chaincode-go/smartContract.go
@@ -0,0 +1,23 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "log"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ abac "github.com/hyperledger/fabric-samples/asset-transfer-abac/chaincode-go/smart-contract"
+)
+
+func main() {
+ abacSmartContract, err := contractapi.NewChaincode(&abac.SmartContract{})
+ if err != nil {
+ log.Panicf("Error creating abac chaincode: %v", err)
+ }
+
+ if err := abacSmartContract.Start(); err != nil {
+ log.Panicf("Error starting abac chaincode: %v", err)
+ }
+}
diff --git a/asset-transfer-basic/.gitignore b/asset-transfer-basic/.gitignore
new file mode 100755
index 0000000..1ddbcb2
--- /dev/null
+++ b/asset-transfer-basic/.gitignore
@@ -0,0 +1,2 @@
+wallet
+!wallet/.gitkeep
diff --git a/asset-transfer-basic/application-go/.gitignore b/asset-transfer-basic/application-go/.gitignore
new file mode 100755
index 0000000..e9fec12
--- /dev/null
+++ b/asset-transfer-basic/application-go/.gitignore
@@ -0,0 +1,4 @@
+wallet
+!wallet/.gitkeep
+
+keystore
diff --git a/asset-transfer-basic/application-go/assetTransfer.go b/asset-transfer-basic/application-go/assetTransfer.go
new file mode 100644
index 0000000..b9a100b
--- /dev/null
+++ b/asset-transfer-basic/application-go/assetTransfer.go
@@ -0,0 +1,155 @@
+/*
+Copyright 2020 IBM All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+
+ "github.com/hyperledger/fabric-sdk-go/pkg/core/config"
+ "github.com/hyperledger/fabric-sdk-go/pkg/gateway"
+)
+
+func main() {
+ log.Println("============ application-golang starts ============")
+
+ err := os.Setenv("DISCOVERY_AS_LOCALHOST", "true")
+ if err != nil {
+ log.Fatalf("Error setting DISCOVERY_AS_LOCALHOST environemnt variable: %v", err)
+ }
+
+ wallet, err := gateway.NewFileSystemWallet("wallet")
+ if err != nil {
+ log.Fatalf("Failed to create wallet: %v", err)
+ }
+
+ if !wallet.Exists("appUser") {
+ err = populateWallet(wallet)
+ if err != nil {
+ log.Fatalf("Failed to populate wallet contents: %v", err)
+ }
+ }
+
+ ccpPath := filepath.Join(
+ "..",
+ "..",
+ "test-network",
+ "organizations",
+ "peerOrganizations",
+ "org1.example.com",
+ "connection-org1.yaml",
+ )
+
+ gw, err := gateway.Connect(
+ gateway.WithConfig(config.FromFile(filepath.Clean(ccpPath))),
+ gateway.WithIdentity(wallet, "appUser"),
+ )
+ if err != nil {
+ log.Fatalf("Failed to connect to gateway: %v", err)
+ }
+ defer gw.Close()
+
+ network, err := gw.GetNetwork("mychannel")
+ if err != nil {
+ log.Fatalf("Failed to get network: %v", err)
+ }
+
+ contract := network.GetContract("basic")
+
+ log.Println("--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger")
+ result, err := contract.SubmitTransaction("InitLedger")
+ if err != nil {
+ log.Fatalf("Failed to Submit transaction: %v", err)
+ }
+ log.Println(string(result))
+
+ log.Println("--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger")
+ result, err = contract.EvaluateTransaction("GetAllAssets")
+ if err != nil {
+ log.Fatalf("Failed to evaluate transaction: %v", err)
+ }
+ log.Println(string(result))
+
+ log.Println("--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments")
+ result, err = contract.SubmitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300")
+ if err != nil {
+ log.Fatalf("Failed to Submit transaction: %v", err)
+ }
+ log.Println(string(result))
+
+ log.Println("--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID")
+ result, err = contract.EvaluateTransaction("ReadAsset", "asset13")
+ if err != nil {
+ log.Fatalf("Failed to evaluate transaction: %v\n", err)
+ }
+ log.Println(string(result))
+
+ log.Println("--> Evaluate Transaction: AssetExists, function returns 'true' if an asset with given assetID exist")
+ result, err = contract.EvaluateTransaction("AssetExists", "asset1")
+ if err != nil {
+ log.Fatalf("Failed to evaluate transaction: %v\n", err)
+ }
+ log.Println(string(result))
+
+ log.Println("--> Submit Transaction: TransferAsset asset1, transfer to new owner of Tom")
+ _, err = contract.SubmitTransaction("TransferAsset", "asset1", "Tom")
+ if err != nil {
+ log.Fatalf("Failed to Submit transaction: %v", err)
+ }
+
+ log.Println("--> Evaluate Transaction: ReadAsset, function returns 'asset1' attributes")
+ result, err = contract.EvaluateTransaction("ReadAsset", "asset1")
+ if err != nil {
+ log.Fatalf("Failed to evaluate transaction: %v", err)
+ }
+ log.Println(string(result))
+ log.Println("============ application-golang ends ============")
+}
+
+func populateWallet(wallet *gateway.Wallet) error {
+ log.Println("============ Populating wallet ============")
+ credPath := filepath.Join(
+ "..",
+ "..",
+ "test-network",
+ "organizations",
+ "peerOrganizations",
+ "org1.example.com",
+ "users",
+ "User1@org1.example.com",
+ "msp",
+ )
+
+ certPath := filepath.Join(credPath, "signcerts", "cert.pem")
+ // read the certificate pem
+ cert, err := ioutil.ReadFile(filepath.Clean(certPath))
+ if err != nil {
+ return err
+ }
+
+ keyDir := filepath.Join(credPath, "keystore")
+ // there's a single file in this dir containing the private key
+ files, err := ioutil.ReadDir(keyDir)
+ if err != nil {
+ return err
+ }
+ if len(files) != 1 {
+ return fmt.Errorf("keystore folder should have contain one file")
+ }
+ keyPath := filepath.Join(keyDir, files[0].Name())
+ key, err := ioutil.ReadFile(filepath.Clean(keyPath))
+ if err != nil {
+ return err
+ }
+
+ identity := gateway.NewX509Identity("Org1MSP", string(cert), string(key))
+
+ return wallet.Put("appUser", identity)
+}
diff --git a/asset-transfer-basic/application-go/go.mod b/asset-transfer-basic/application-go/go.mod
new file mode 100644
index 0000000..dad8228
--- /dev/null
+++ b/asset-transfer-basic/application-go/go.mod
@@ -0,0 +1,5 @@
+module asset-transfer-basic
+
+go 1.14
+
+require github.com/hyperledger/fabric-sdk-go v1.0.0-rc1
diff --git a/asset-transfer-basic/application-go/go.sum b/asset-transfer-basic/application-go/go.sum
new file mode 100644
index 0000000..19c5b31
--- /dev/null
+++ b/asset-transfer-basic/application-go/go.sum
@@ -0,0 +1,277 @@
+bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4=
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
+github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
+github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
+github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY=
+github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
+github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
+github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiKw=
+github.com/cloudflare/cfssl v1.4.1/go.mod h1:KManx/OJPb5QY+y0+o/898AMcM128sF0bURvoVUSjTo=
+github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4=
+github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
+github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/google/certificate-transparency-go v0.0.0-20180222191210-5ab67e519c93 h1:qdfmdGwtm13OVx+AxguOWUTbgmXGn2TbdUHipo3chMg=
+github.com/google/certificate-transparency-go v0.0.0-20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
+github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
+github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
+github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hyperledger/fabric-config v0.0.5 h1:khRkm8U9Ghdg8VmZfptgzCFlCzrka8bPfUkM+/j6Zlg=
+github.com/hyperledger/fabric-config v0.0.5/go.mod h1:YpITBI/+ZayA3XWY5lF302K7PAsFYjEEPM/zr3hegA8=
+github.com/hyperledger/fabric-lib-go v1.0.0 h1:UL1w7c9LvHZUSkIvHTDGklxFv2kTeva1QI2emOVc324=
+github.com/hyperledger/fabric-lib-go v1.0.0/go.mod h1:H362nMlunurmHwkYqR5uHL2UDWbQdbfz74n8kbCFsqc=
+github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85 h1:bNgEcCg5NVRWs/T+VUEfhgh5Olx/N4VB+0+ybW+oSuA=
+github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23 h1:SEbB3yH4ISTGRifDamYXAst36gO2kM855ndMJlsv+pc=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta1.0.20200526155846-219a09aadc0f h1:eAkJx0+8PBbfP6xZxVRD2agk9W7oDbqllxO+ERgnKJk=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta1.0.20200526155846-219a09aadc0f/go.mod h1:/s224b8NLvOJOCIqBvWd9O6u7GE33iuIOT6OfcTE1OE=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta2 h1:FBYygns0Qga+mQ4PXycyTU5m4N9KAZM+Ttf7agiV7M8=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta2/go.mod h1:/s224b8NLvOJOCIqBvWd9O6u7GE33iuIOT6OfcTE1OE=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta3.0.20201006151309-9c426dcc5096 h1:veml7LmfavSHqF8w8z/PGGlfdXvmx5SstQIH6Nyy87c=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta3.0.20201006151309-9c426dcc5096/go.mod h1:qWE9Syfg1KbwNjtILk70bJLilnmCvllIYFCSY/pa1RU=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo=
+github.com/jmoiron/sqlx v0.0.0-20180124204410-05cef0741ade/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
+github.com/kisom/goutils v1.1.0/go.mod h1:+UBTfd78habUYWFbNWTJNG+jNG/i/lGURakr4A/yNRw=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kylelemons/go-gypsy v0.0.0-20160905020020-08cad365cd28/go.mod h1:T/T7jsxVqf9k/zYOqbgNAsANsjxTd1Yq3htjDhQ1H0c=
+github.com/lib/pq v0.0.0-20180201184707-88edab080323/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/magiconair/properties v1.7.6 h1:U+1DqNen04MdEPgFiIwdOUiqZ8qPa37xgogX/sd3+54=
+github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/pkcs11 v0.0.0-20190329070431-55f3fac3af27/go.mod h1:WCBAbTOdfhHhz7YXujeZMF7owC4tPb1naKFsgfUISjo=
+github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238 h1:+MZW2uvHgN8kYvksEN3f7eFL2wpzk0GxmlFsMybWc7E=
+github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
+github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/pelletier/go-toml v1.1.0 h1:cmiOvKzEunMsAxyhXSzpL5Q1CRKpVv0KQsnAIcSEVYM=
+github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
+github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1 h1:osmNoEW2SCW3L7EX0km2LYM8HKpNWRiouxjE3XHkyGc=
+github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/procfs v0.0.0-20180705121852-ae68e2d4c00f h1:c9M4CCa6g8WURSsbrl3lb/w/G1Z5xZpYvhhjdcVDOkE=
+github.com/prometheus/procfs v0.0.0-20180705121852-ae68e2d4c00f/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/spf13/afero v1.1.0 h1:bopulORc2JeYaxfHLvJa5NzxviA9PoWhpiiJkru7Ji4=
+github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.3.1 h1:GPTpEAuNr98px18yNQ66JllNil98wfRZ/5Ukny8FeQA=
+github.com/spf13/afero v1.3.1/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
+github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
+github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
+github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig=
+github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
+github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso=
+github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
+github.com/spf13/viper v1.1.1 h1:/8JBRFO4eoHu1TmpsLgNBq1CQgRUg4GolYlEFieqJgo=
+github.com/spf13/viper v1.1.1/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
+github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
+github.com/weppos/publicsuffix-go v0.5.0 h1:rutRtjBJViU/YjcI5d80t4JAVvDltS6bciJg2K1HrLU=
+github.com/weppos/publicsuffix-go v0.5.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
+github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
+github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
+github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e h1:mvOa4+/DXStR4ZXOks/UsjeFdn5O5JpLUtzqk9U8xXw=
+github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e/go.mod h1:w7kd3qXHh8FNaczNjslXqvFQiv5mMWRXlL9klTUAHc8=
+github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb h1:vxqkjztXSaPVDc8FQCdHTaejm2x747f6yPbnu1h2xkg=
+github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb/go.mod h1:29UiAJNsiVdvTBFCJW8e3q6dcDbOoPkhMgttOSCIMMY=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
+golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d h1:XB2jc5XQ9uhizGTS2vWcN01bc4dI6z3C4KY5MQm8SS8=
+google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/asset-transfer-basic/application-java/.gitattributes b/asset-transfer-basic/application-java/.gitattributes
new file mode 100644
index 0000000..00a51af
--- /dev/null
+++ b/asset-transfer-basic/application-java/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat text eol=crlf
+
diff --git a/asset-transfer-basic/application-java/build.gradle b/asset-transfer-basic/application-java/build.gradle
new file mode 100644
index 0000000..e2eed62
--- /dev/null
+++ b/asset-transfer-basic/application-java/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * This generated file contains a sample Java project to get you started.
+ * For more details take a look at the Java Quickstart chapter in the Gradle
+ * User Manual available at https://docs.gradle.org/6.5/userguide/tutorial_java_projects.html
+ */
+
+plugins {
+ // Apply the java plugin to add support for Java
+ id 'java'
+
+ // Apply the application plugin to add support for building a CLI application.
+ id 'application'
+}
+ext {
+ javaMainClass = "application.java.App"
+}
+
+repositories {
+ // Use jcenter for resolving dependencies.
+ // You can declare any Maven/Ivy/file repository here.
+ jcenter()
+}
+
+dependencies {
+ // This dependency is used by the application.
+ implementation 'com.google.guava:guava:29.0-jre'
+ implementation 'org.hyperledger.fabric:fabric-gateway-java:2.1.1'
+}
+
+application {
+ // Define the main class for the application.
+ mainClassName = 'application.java.App'
+}
+
+// task for running the app after building dependencies
+task runApp(type: Exec) {
+ dependsOn build
+ group = "Execution"
+ description = "Run the main class with ExecTask"
+ commandLine "java", "-classpath", sourceSets.main.runtimeClasspath.getAsPath(), javaMainClass
+}
\ No newline at end of file
diff --git a/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..62d4c05
Binary files /dev/null and b/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..622ab64
--- /dev/null
+++ b/asset-transfer-basic/application-java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/asset-transfer-basic/application-java/gradlew b/asset-transfer-basic/application-java/gradlew
new file mode 100755
index 0000000..fbd7c51
--- /dev/null
+++ b/asset-transfer-basic/application-java/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/asset-transfer-basic/application-java/gradlew.bat b/asset-transfer-basic/application-java/gradlew.bat
new file mode 100644
index 0000000..5093609
--- /dev/null
+++ b/asset-transfer-basic/application-java/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/asset-transfer-basic/application-java/settings.gradle b/asset-transfer-basic/application-java/settings.gradle
new file mode 100644
index 0000000..5423bc7
--- /dev/null
+++ b/asset-transfer-basic/application-java/settings.gradle
@@ -0,0 +1,10 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * The settings file is used to specify which projects to include in your build.
+ *
+ * Detailed information about configuring a multi-project build in Gradle can be found
+ * in the user manual at https://docs.gradle.org/6.5/userguide/multi_project_builds.html
+ */
+
+rootProject.name = 'application-java'
diff --git a/asset-transfer-basic/application-java/src/main/java/application/java/App.java b/asset-transfer-basic/application-java/src/main/java/application/java/App.java
new file mode 100644
index 0000000..a0d1d5c
--- /dev/null
+++ b/asset-transfer-basic/application-java/src/main/java/application/java/App.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// Running TestApp:
+// gradle runApp
+
+package application.java;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.hyperledger.fabric.gateway.Contract;
+import org.hyperledger.fabric.gateway.Gateway;
+import org.hyperledger.fabric.gateway.Network;
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+
+
+public class App {
+
+ static {
+ System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
+ }
+
+ // helper function for getting connected to the gateway
+ public static Gateway connect() throws Exception{
+ // Load a file system based wallet for managing identities.
+ Path walletPath = Paths.get("wallet");
+ Wallet wallet = Wallets.newFileSystemWallet(walletPath);
+ // load a CCP
+ Path networkConfigPath = Paths.get("..", "..", "test-network", "organizations", "peerOrganizations", "org1.example.com", "connection-org1.yaml");
+
+ Gateway.Builder builder = Gateway.createBuilder();
+ builder.identity(wallet, "appUser").networkConfig(networkConfigPath).discovery(true);
+ return builder.connect();
+ }
+
+ public static void main(String[] args) throws Exception {
+ // enrolls the admin and registers the user
+ try {
+ EnrollAdmin.main(null);
+ RegisterUser.main(null);
+ } catch (Exception e) {
+ System.err.println(e);
+ }
+
+ // connect to the network and invoke the smart contract
+ try (Gateway gateway = connect()) {
+
+ // get the network and contract
+ Network network = gateway.getNetwork("mychannel");
+ Contract contract = network.getContract("basic");
+
+ byte[] result;
+
+ System.out.println("Submit Transaction: InitLedger creates the initial set of assets on the ledger.");
+ contract.submitTransaction("InitLedger");
+
+ System.out.println("\n");
+ result = contract.evaluateTransaction("GetAllAssets");
+ System.out.println("Evaluate Transaction: GetAllAssets, result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Submit Transaction: CreateAsset asset13");
+ //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraisedValue of 1300
+ contract.submitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300");
+
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction: ReadAsset asset13");
+ // ReadAsset returns an asset with given assetID
+ result = contract.evaluateTransaction("ReadAsset", "asset13");
+ System.out.println("result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction: AssetExists asset1");
+ // AssetExists returns "true" if an asset with given assetID exist
+ result = contract.evaluateTransaction("AssetExists", "asset1");
+ System.out.println("result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Submit Transaction: UpdateAsset asset1, new AppraisedValue : 350");
+ // UpdateAsset updates an existing asset with new properties. Same args as CreateAsset
+ contract.submitTransaction("UpdateAsset", "asset1", "blue", "5", "Tomoko", "350");
+
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction: ReadAsset asset1");
+ result = contract.evaluateTransaction("ReadAsset", "asset1");
+ System.out.println("result: " + new String(result));
+
+ try {
+ System.out.println("\n");
+ System.out.println("Submit Transaction: UpdateAsset asset70");
+ //Non existing asset asset70 should throw Error
+ contract.submitTransaction("UpdateAsset", "asset70", "blue", "5", "Tomoko", "300");
+ } catch (Exception e) {
+ System.err.println("Expected an error on UpdateAsset of non-existing Asset: " + e);
+ }
+
+ System.out.println("\n");
+ System.out.println("Submit Transaction: TransferAsset asset1 from owner Tomoko > owner Tom");
+ // TransferAsset transfers an asset with given ID to new owner Tom
+ contract.submitTransaction("TransferAsset", "asset1", "Tom");
+
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction: ReadAsset asset1");
+ result = contract.evaluateTransaction("ReadAsset", "asset1");
+ System.out.println("result: " + new String(result));
+ }
+ catch(Exception e){
+ System.err.println(e);
+ }
+
+ }
+}
diff --git a/asset-transfer-basic/application-java/src/main/java/application/java/EnrollAdmin.java b/asset-transfer-basic/application-java/src/main/java/application/java/EnrollAdmin.java
new file mode 100644
index 0000000..563a35f
--- /dev/null
+++ b/asset-transfer-basic/application-java/src/main/java/application/java/EnrollAdmin.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package application.java;
+
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.hyperledger.fabric.gateway.Identities;
+import org.hyperledger.fabric.gateway.Identity;
+import org.hyperledger.fabric.sdk.Enrollment;
+import org.hyperledger.fabric.sdk.security.CryptoSuite;
+import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
+import org.hyperledger.fabric_ca.sdk.EnrollmentRequest;
+import org.hyperledger.fabric_ca.sdk.HFCAClient;
+
+public class EnrollAdmin {
+
+ public static void main(String[] args) throws Exception {
+
+ // Create a CA client for interacting with the CA.
+ Properties props = new Properties();
+ props.put("pemFile",
+ "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
+ props.put("allowAllHostNames", "true");
+ HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
+ CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
+ caClient.setCryptoSuite(cryptoSuite);
+
+ // Create a wallet for managing identities
+ Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));
+
+ // Check to see if we've already enrolled the admin user.
+ if (wallet.get("admin") != null) {
+ System.out.println("An identity for the admin user \"admin\" already exists in the wallet");
+ return;
+ }
+
+ // Enroll the admin user, and import the new identity into the wallet.
+ final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
+ enrollmentRequestTLS.addHost("localhost");
+ enrollmentRequestTLS.setProfile("tls");
+ Enrollment enrollment = caClient.enroll("admin", "adminpw", enrollmentRequestTLS);
+ Identity user = Identities.newX509Identity("Org1MSP", enrollment);
+ wallet.put("admin", user);
+ System.out.println("Successfully enrolled user \"admin\" and imported it into the wallet");
+ }
+}
diff --git a/asset-transfer-basic/application-java/src/main/java/application/java/RegisterUser.java b/asset-transfer-basic/application-java/src/main/java/application/java/RegisterUser.java
new file mode 100644
index 0000000..367b4a3
--- /dev/null
+++ b/asset-transfer-basic/application-java/src/main/java/application/java/RegisterUser.java
@@ -0,0 +1,107 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package application.java;
+
+import java.nio.file.Paths;
+import java.security.PrivateKey;
+import java.util.Properties;
+import java.util.Set;
+
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.hyperledger.fabric.gateway.Identities;
+import org.hyperledger.fabric.gateway.Identity;
+import org.hyperledger.fabric.gateway.X509Identity;
+import org.hyperledger.fabric.sdk.Enrollment;
+import org.hyperledger.fabric.sdk.User;
+import org.hyperledger.fabric.sdk.security.CryptoSuite;
+import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
+import org.hyperledger.fabric_ca.sdk.HFCAClient;
+import org.hyperledger.fabric_ca.sdk.RegistrationRequest;
+
+public class RegisterUser {
+
+ public static void main(String[] args) throws Exception {
+
+ // Create a CA client for interacting with the CA.
+ Properties props = new Properties();
+ props.put("pemFile",
+ "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
+ props.put("allowAllHostNames", "true");
+ HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
+ CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
+ caClient.setCryptoSuite(cryptoSuite);
+
+ // Create a wallet for managing identities
+ Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));
+
+ // Check to see if we've already enrolled the user.
+ if (wallet.get("appUser") != null) {
+ System.out.println("An identity for the user \"appUser\" already exists in the wallet");
+ return;
+ }
+
+ X509Identity adminIdentity = (X509Identity)wallet.get("admin");
+ if (adminIdentity == null) {
+ System.out.println("\"admin\" needs to be enrolled and added to the wallet first");
+ return;
+ }
+ User admin = new User() {
+
+ @Override
+ public String getName() {
+ return "admin";
+ }
+
+ @Override
+ public Set getRoles() {
+ return null;
+ }
+
+ @Override
+ public String getAccount() {
+ return null;
+ }
+
+ @Override
+ public String getAffiliation() {
+ return "org1.department1";
+ }
+
+ @Override
+ public Enrollment getEnrollment() {
+ return new Enrollment() {
+
+ @Override
+ public PrivateKey getKey() {
+ return adminIdentity.getPrivateKey();
+ }
+
+ @Override
+ public String getCert() {
+ return Identities.toPemString(adminIdentity.getCertificate());
+ }
+ };
+ }
+
+ @Override
+ public String getMspId() {
+ return "Org1MSP";
+ }
+
+ };
+
+ // Register the user, enroll the user, and import the new identity into the wallet.
+ RegistrationRequest registrationRequest = new RegistrationRequest("appUser");
+ registrationRequest.setAffiliation("org1.department1");
+ registrationRequest.setEnrollmentID("appUser");
+ String enrollmentSecret = caClient.register(registrationRequest, admin);
+ Enrollment enrollment = caClient.enroll("appUser", enrollmentSecret);
+ Identity user = Identities.newX509Identity("Org1MSP", enrollment);
+ wallet.put("appUser", user);
+ System.out.println("Successfully enrolled user \"appUser\" and imported it into the wallet");
+ }
+
+}
diff --git a/asset-transfer-basic/application-java/src/main/resources/log4j.properties b/asset-transfer-basic/application-java/src/main/resources/log4j.properties
new file mode 100644
index 0000000..f1f841f
--- /dev/null
+++ b/asset-transfer-basic/application-java/src/main/resources/log4j.properties
@@ -0,0 +1,19 @@
+# initialize root logger with level ERROR for stdout and fout
+log4j.rootLogger=ERROR,stdout,fout
+# set the log level for these components
+log4j.logger.com.endeca=INFO
+log4j.logger.com.endeca.itl.web.metrics=INFO
+
+# add a ConsoleAppender to the logger stdout to write to the console
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+# use a simple message format
+log4j.appender.stdout.layout.ConversionPattern=%m%n
+
+# add a FileAppender to the logger fout
+log4j.appender.fout=org.apache.log4j.FileAppender
+# create a log file
+log4j.appender.fout.File=crawl.log
+log4j.appender.fout.layout=org.apache.log4j.PatternLayout
+# use a more detailed message pattern
+log4j.appender.fout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n
diff --git a/asset-transfer-basic/application-javascript/.eslintignore b/asset-transfer-basic/application-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-basic/application-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-basic/application-javascript/.eslintrc.js b/asset-transfer-basic/application-javascript/.eslintrc.js
new file mode 100644
index 0000000..6fa636b
--- /dev/null
+++ b/asset-transfer-basic/application-javascript/.eslintrc.js
@@ -0,0 +1,36 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 'tab'],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/asset-transfer-basic/application-javascript/.gitignore b/asset-transfer-basic/application-javascript/.gitignore
new file mode 100644
index 0000000..21b287f
--- /dev/null
+++ b/asset-transfer-basic/application-javascript/.gitignore
@@ -0,0 +1,14 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+package-lock.json
+
+wallet
+!wallet/.gitkeep
diff --git a/asset-transfer-basic/application-javascript/app.js b/asset-transfer-basic/application-javascript/app.js
new file mode 100644
index 0000000..fc8eeca
--- /dev/null
+++ b/asset-transfer-basic/application-javascript/app.js
@@ -0,0 +1,180 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const FabricCAServices = require('fabric-ca-client');
+const path = require('path');
+const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
+const { buildCCPOrg1, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const channelName = 'mychannel';
+const chaincodeName = 'basic';
+const mspOrg1 = 'Org1MSP';
+const walletPath = path.join(__dirname, 'wallet');
+const org1UserId = 'appUser';
+
+function prettyJSONString(inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+}
+
+// pre-requisites:
+// - fabric-sample two organization test-network setup with two peers, ordering service,
+// and 2 certificate authorities
+// ===> from directory /fabric-samples/test-network
+// ./network.sh up createChannel -ca
+// - Use any of the asset-transfer-basic chaincodes deployed on the channel "mychannel"
+// with the chaincode name of "basic". The following deploy command will package,
+// install, approve, and commit the javascript chaincode, all the actions it takes
+// to deploy a chaincode to a channel.
+// ===> from directory /fabric-samples/test-network
+// ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-javascript/ -ccl javascript
+// - Be sure that node.js is installed
+// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript
+// node -v
+// - npm installed code dependencies
+// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript
+// npm install
+// - to run this test application
+// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript
+// node app.js
+
+// NOTE: If you see kind an error like these:
+/*
+ 2020-08-07T20:23:17.590Z - error: [DiscoveryService]: send[mychannel] - Channel:mychannel received discovery error:access denied
+ ******** FAILED to run the application: Error: DiscoveryService: mychannel error: access denied
+
+ OR
+
+ Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
+ ******** FAILED to run the application: Error: Identity not found in wallet: appUser
+*/
+// Delete the /fabric-samples/asset-transfer-basic/application-javascript/wallet directory
+// and retry this application.
+//
+// The certificate authority must have been restarted and the saved certificates for the
+// admin and application user are not valid. Deleting the wallet store will force these to be reset
+// with the new certificate authority.
+//
+
+/**
+ * A test application to show basic queries operations with any of the asset-transfer-basic chaincodes
+ * -- How to submit a transaction
+ * -- How to query and check the results
+ *
+ * To see the SDK workings, try setting the logging to show on the console before running
+ * export HFC_LOGGING='{"debug":"console"}'
+ */
+async function main() {
+ try {
+ // build an in memory object with the network configuration (also known as a connection profile)
+ const ccp = buildCCPOrg1();
+
+ // build an instance of the fabric ca services client based on
+ // the information in the network configuration
+ const caClient = buildCAClient(FabricCAServices, ccp, 'ca.org1.example.com');
+
+ // setup the wallet to hold the credentials of the application user
+ const wallet = await buildWallet(Wallets, walletPath);
+
+ // in a real application this would be done on an administrative flow, and only once
+ await enrollAdmin(caClient, wallet, mspOrg1);
+
+ // in a real application this would be done only when a new user was required to be added
+ // and would be part of an administrative flow
+ await registerAndEnrollUser(caClient, wallet, mspOrg1, org1UserId, 'org1.department1');
+
+ // Create a new gateway instance for interacting with the fabric network.
+ // In a real application this would be done as the backend server session is setup for
+ // a user that has been verified.
+ const gateway = new Gateway();
+
+ try {
+ // setup the gateway instance
+ // The user will now be able to create connections to the fabric network and be able to
+ // submit transactions and query. All transactions submitted by this gateway will be
+ // signed by this user using the credentials stored in the wallet.
+ await gateway.connect(ccp, {
+ wallet,
+ identity: org1UserId,
+ discovery: { enabled: true, asLocalhost: true } // using asLocalhost as this gateway is using a fabric network deployed locally
+ });
+
+ // Build a network instance based on the channel where the smart contract is deployed
+ const network = await gateway.getNetwork(channelName);
+
+ // Get the contract from the network.
+ const contract = network.getContract(chaincodeName);
+
+ // Initialize a set of asset data on the channel using the chaincode 'InitLedger' function.
+ // This type of transaction would only be run once by an application the first time it was started after it
+ // deployed the first time. Any updates to the chaincode deployed later would likely not need to run
+ // an "init" type function.
+ console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger');
+ await contract.submitTransaction('InitLedger');
+ console.log('*** Result: committed');
+
+ // Let's try a query type operation (function).
+ // This will be sent to just one peer and the results will be shown.
+ console.log('\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger');
+ let result = await contract.evaluateTransaction('GetAllAssets');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Now let's try to submit a transaction.
+ // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent
+ // to the orderer to be committed by each of the peer's to the channel ledger.
+ console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments');
+ result = await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300');
+ console.log('*** Result: committed');
+ if (`${result}` !== '') {
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+ }
+
+ console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID');
+ result = await contract.evaluateTransaction('ReadAsset', 'asset13');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist');
+ result = await contract.evaluateTransaction('AssetExists', 'asset1');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Submit Transaction: UpdateAsset asset1, change the appraisedValue to 350');
+ await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', '5', 'Tomoko', '350');
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes');
+ result = await contract.evaluateTransaction('ReadAsset', 'asset1');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ try {
+ // How about we try a transactions where the executing chaincode throws an error
+ // Notice how the submitTransaction will throw an error containing the error thrown by the chaincode
+ console.log('\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error');
+ await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', '5', 'Tomoko', '300');
+ console.log('******** FAILED to return an error');
+ } catch (error) {
+ console.log(`*** Successfully caught the error: \n ${error}`);
+ }
+
+ console.log('\n--> Submit Transaction: TransferAsset asset1, transfer to new owner of Tom');
+ await contract.submitTransaction('TransferAsset', 'asset1', 'Tom');
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes');
+ result = await contract.evaluateTransaction('ReadAsset', 'asset1');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+ } finally {
+ // Disconnect from the gateway when the application is closing
+ // This will close all connections to the network
+ gateway.disconnect();
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ }
+}
+
+main();
diff --git a/asset-transfer-basic/application-javascript/package.json b/asset-transfer-basic/application-javascript/package.json
new file mode 100644
index 0000000..bab813d
--- /dev/null
+++ b/asset-transfer-basic/application-javascript/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "asset-transfer-basic",
+ "version": "1.0.0",
+ "description": "Asset-transfer-basic application implemented in JavaScript",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.4",
+ "fabric-network": "^2.2.4"
+ }
+}
diff --git a/asset-transfer-basic/application-typescript/.gitignore b/asset-transfer-basic/application-typescript/.gitignore
new file mode 100644
index 0000000..48285d1
--- /dev/null
+++ b/asset-transfer-basic/application-typescript/.gitignore
@@ -0,0 +1,15 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Compiled TypeScript files
+dist
+
diff --git a/asset-transfer-basic/application-typescript/package.json b/asset-transfer-basic/application-typescript/package.json
new file mode 100644
index 0000000..91a9c6b
--- /dev/null
+++ b/asset-transfer-basic/application-typescript/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "asset-transfer-basic",
+ "version": "1.0.0",
+ "description": "Asset Transfer Basic contract implemented in TypeScript",
+ "main": "dist/index.js",
+ "typings": "dist/index.d.ts",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "tslint -c tslint.json 'src/**/*.ts'",
+ "pretest": "npm run lint",
+ "start": "npm run build && node dist/app.js",
+ "build": "tsc",
+ "build:watch": "tsc -w",
+ "prepublishOnly": "npm run build"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.4",
+ "fabric-network": "^2.2.4"
+ },
+ "devDependencies": {
+ "tslint": "^5.11.0",
+ "typescript": "^3.1.6"
+ },
+ "nyc": {
+ "extension": [
+ ".ts",
+ ".tsx"
+ ],
+ "exclude": [
+ "coverage/**",
+ "dist/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/asset-transfer-basic/application-typescript/src/app.ts b/asset-transfer-basic/application-typescript/src/app.ts
new file mode 100644
index 0000000..075e0ed
--- /dev/null
+++ b/asset-transfer-basic/application-typescript/src/app.ts
@@ -0,0 +1,171 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import { Gateway, GatewayOptions } from 'fabric-network';
+import * as path from 'path';
+import { buildCCPOrg1, buildWallet, prettyJSONString } from './utils//AppUtil';
+import { buildCAClient, enrollAdmin, registerAndEnrollUser } from './utils/CAUtil';
+
+const channelName = 'mychannel';
+const chaincodeName = 'basic';
+const mspOrg1 = 'Org1MSP';
+const walletPath = path.join(__dirname, 'wallet');
+const org1UserId = 'appUser';
+
+// pre-requisites:
+// - fabric-sample two organization test-network setup with two peers, ordering service,
+// and 2 certificate authorities
+// ===> from directory /fabric-samples/test-network
+// ./network.sh up createChannel -ca
+// - Use any of the asset-transfer-basic chaincodes deployed on the channel "mychannel"
+// with the chaincode name of "basic". The following deploy command will package,
+// install, approve, and commit the javascript chaincode, all the actions it takes
+// to deploy a chaincode to a channel.
+// ===> from directory /fabric-samples/test-network
+// ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-typescript/ -ccl javascript
+// - Be sure that node.js is installed
+// ===> from directory /fabric-samples/asset-transfer-basic/application-typescript
+// node -v
+// - npm installed code dependencies
+// ===> from directory /fabric-samples/asset-transfer-basic/application-typescript
+// npm install
+// - to run this test application
+// ===> from directory /fabric-samples/asset-transfer-basic/application-typescript
+// npm start
+
+// NOTE: If you see kind an error like these:
+/*
+ 2020-08-07T20:23:17.590Z - error: [DiscoveryService]: send[mychannel] - Channel:mychannel received discovery error:access denied
+ ******** FAILED to run the application: Error: DiscoveryService: mychannel error: access denied
+
+ OR
+
+ Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
+ ******** FAILED to run the application: Error: Identity not found in wallet: appUser
+*/
+// Delete the /fabric-samples/asset-transfer-basic/application-typescript/wallet directory
+// and retry this application.
+//
+// The certificate authority must have been restarted and the saved certificates for the
+// admin and application user are not valid. Deleting the wallet store will force these to be reset
+// with the new certificate authority.
+//
+
+/**
+ * A test application to show basic queries operations with any of the asset-transfer-basic chaincodes
+ * -- How to submit a transaction
+ * -- How to query and check the results
+ *
+ * To see the SDK workings, try setting the logging to show on the console before running
+ * export HFC_LOGGING='{"debug":"console"}'
+ */
+async function main() {
+ try {
+ // build an in memory object with the network configuration (also known as a connection profile)
+ const ccp = buildCCPOrg1();
+
+ // build an instance of the fabric ca services client based on
+ // the information in the network configuration
+ const caClient = buildCAClient(ccp, 'ca.org1.example.com');
+
+ // setup the wallet to hold the credentials of the application user
+ const wallet = await buildWallet(walletPath);
+
+ // in a real application this would be done on an administrative flow, and only once
+ await enrollAdmin(caClient, wallet, mspOrg1);
+
+ // in a real application this would be done only when a new user was required to be added
+ // and would be part of an administrative flow
+ await registerAndEnrollUser(caClient, wallet, mspOrg1, org1UserId, 'org1.department1');
+
+ // Create a new gateway instance for interacting with the fabric network.
+ // In a real application this would be done as the backend server session is setup for
+ // a user that has been verified.
+ const gateway = new Gateway();
+
+ const gatewayOpts: GatewayOptions = {
+ wallet,
+ identity: org1UserId,
+ discovery: { enabled: true, asLocalhost: true }, // using asLocalhost as this gateway is using a fabric network deployed locally
+ };
+
+ try {
+ // setup the gateway instance
+ // The user will now be able to create connections to the fabric network and be able to
+ // submit transactions and query. All transactions submitted by this gateway will be
+ // signed by this user using the credentials stored in the wallet.
+ await gateway.connect(ccp, gatewayOpts);
+
+ // Build a network instance based on the channel where the smart contract is deployed
+ const network = await gateway.getNetwork(channelName);
+
+ // Get the contract from the network.
+ const contract = network.getContract(chaincodeName);
+
+ // Initialize a set of asset data on the channel using the chaincode 'InitLedger' function.
+ // This type of transaction would only be run once by an application the first time it was started after it
+ // deployed the first time. Any updates to the chaincode deployed later would likely not need to run
+ // an "init" type function.
+ console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger');
+ await contract.submitTransaction('InitLedger');
+ console.log('*** Result: committed');
+
+ // Let's try a query type operation (function).
+ // This will be sent to just one peer and the results will be shown.
+ console.log('\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger');
+ let result = await contract.evaluateTransaction('GetAllAssets');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Now let's try to submit a transaction.
+ // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent
+ // to the orderer to be committed by each of the peer's to the channel ledger.
+ console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments');
+ await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300');
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID');
+ result = await contract.evaluateTransaction('ReadAsset', 'asset13');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist');
+ result = await contract.evaluateTransaction('AssetExists', 'asset1');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Submit Transaction: UpdateAsset asset1, change the appraisedValue to 350');
+ await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', '5', 'Tomoko', '350');
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes');
+ result = await contract.evaluateTransaction('ReadAsset', 'asset1');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ try {
+ // How about we try a transactions where the executing chaincode throws an error
+ // Notice how the submitTransaction will throw an error containing the error thrown by the chaincode
+ console.log('\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error');
+ await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', '5', 'Tomoko', '300');
+ console.log('******** FAILED to return an error');
+ } catch (error) {
+ console.log(`*** Successfully caught the error: \n ${error}`);
+ }
+
+ console.log('\n--> Submit Transaction: TransferAsset asset1, transfer to new owner of Tom');
+ await contract.submitTransaction('TransferAsset', 'asset1', 'Tom');
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes');
+ result = await contract.evaluateTransaction('ReadAsset', 'asset1');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+ } finally {
+ // Disconnect from the gateway when the application is closing
+ // This will close all connections to the network
+ gateway.disconnect();
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ }
+}
+
+main();
diff --git a/asset-transfer-basic/application-typescript/src/utils/AppUtil.ts b/asset-transfer-basic/application-typescript/src/utils/AppUtil.ts
new file mode 100644
index 0000000..f284e30
--- /dev/null
+++ b/asset-transfer-basic/application-typescript/src/utils/AppUtil.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Wallet, Wallets } from 'fabric-network';
+import * as fs from 'fs';
+import * as path from 'path';
+
+const buildCCPOrg1 = (): Record => {
+ // load the common connection configuration file
+ const ccpPath = path.resolve(__dirname, '..', '..', '..', '..', 'test-network',
+ 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
+ const fileExists = fs.existsSync(ccpPath);
+ if (!fileExists) {
+ throw new Error(`no such file or directory: ${ccpPath}`);
+ }
+ const contents = fs.readFileSync(ccpPath, 'utf8');
+
+ // build a JSON object from the file contents
+ const ccp = JSON.parse(contents);
+
+ console.log(`Loaded the network configuration located at ${ccpPath}`);
+ return ccp;
+};
+
+const buildCCPOrg2 = (): Record => {
+ // load the common connection configuration file
+ const ccpPath = path.resolve(__dirname, '..', '..', '..', '..', 'test-network',
+ 'organizations', 'peerOrganizations', 'org2.example.com', 'connection-org2.json');
+ const fileExists = fs.existsSync(ccpPath);
+ if (!fileExists) {
+ throw new Error(`no such file or directory: ${ccpPath}`);
+ }
+ const contents = fs.readFileSync(ccpPath, 'utf8');
+
+ // build a JSON object from the file contents
+ const ccp = JSON.parse(contents);
+
+ console.log(`Loaded the network configuration located at ${ccpPath}`);
+ return ccp;
+};
+
+const buildWallet = async (walletPath: string): Promise => {
+ // Create a new wallet : Note that wallet is for managing identities.
+ let wallet: Wallet;
+ if (walletPath) {
+ wallet = await Wallets.newFileSystemWallet(walletPath);
+ console.log(`Built a file system wallet at ${walletPath}`);
+ } else {
+ wallet = await Wallets.newInMemoryWallet();
+ console.log('Built an in memory wallet');
+ }
+
+ return wallet;
+};
+
+const prettyJSONString = (inputString: string): string => {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ } else {
+ return inputString;
+ }
+};
+
+export {
+ buildCCPOrg1,
+ buildCCPOrg2,
+ buildWallet,
+ prettyJSONString,
+};
diff --git a/asset-transfer-basic/application-typescript/src/utils/CAUtil.ts b/asset-transfer-basic/application-typescript/src/utils/CAUtil.ts
new file mode 100644
index 0000000..665dd0e
--- /dev/null
+++ b/asset-transfer-basic/application-typescript/src/utils/CAUtil.ts
@@ -0,0 +1,104 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as FabricCAServices from 'fabric-ca-client';
+import { Wallet } from 'fabric-network';
+
+const adminUserId = 'admin';
+const adminUserPasswd = 'adminpw';
+
+/**
+ *
+ * @param {*} ccp
+ */
+const buildCAClient = (ccp: Record, caHostName: string): FabricCAServices => {
+ // Create a new CA client for interacting with the CA.
+ const caInfo = ccp.certificateAuthorities[caHostName]; // lookup CA details from config
+ const caTLSCACerts = caInfo.tlsCACerts.pem;
+ const caClient = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName);
+
+ console.log(`Built a CA Client named ${caInfo.caName}`);
+ return caClient;
+};
+
+const enrollAdmin = async (caClient: FabricCAServices, wallet: Wallet, orgMspId: string): Promise => {
+ try {
+ // Check to see if we've already enrolled the admin user.
+ const identity = await wallet.get(adminUserId);
+ if (identity) {
+ console.log('An identity for the admin user already exists in the wallet');
+ return;
+ }
+
+ // Enroll the admin user, and import the new identity into the wallet.
+ const enrollment = await caClient.enroll({ enrollmentID: adminUserId, enrollmentSecret: adminUserPasswd });
+ const x509Identity = {
+ credentials: {
+ certificate: enrollment.certificate,
+ privateKey: enrollment.key.toBytes(),
+ },
+ mspId: orgMspId,
+ type: 'X.509',
+ };
+ await wallet.put(adminUserId, x509Identity);
+ console.log('Successfully enrolled admin user and imported it into the wallet');
+ } catch (error) {
+ console.error(`Failed to enroll admin user : ${error}`);
+ }
+};
+
+const registerAndEnrollUser = async (caClient: FabricCAServices, wallet: Wallet, orgMspId: string, userId: string, affiliation: string): Promise => {
+ try {
+ // Check to see if we've already enrolled the user
+ const userIdentity = await wallet.get(userId);
+ if (userIdentity) {
+ console.log(`An identity for the user ${userId} already exists in the wallet`);
+ return;
+ }
+
+ // Must use an admin to register a new user
+ const adminIdentity = await wallet.get(adminUserId);
+ if (!adminIdentity) {
+ console.log('An identity for the admin user does not exist in the wallet');
+ console.log('Enroll the admin user before retrying');
+ return;
+ }
+
+ // build a user object for authenticating with the CA
+ const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type);
+ const adminUser = await provider.getUserContext(adminIdentity, adminUserId);
+
+ // Register the user, enroll the user, and import the new identity into the wallet.
+ // if affiliation is specified by client, the affiliation value must be configured in CA
+ const secret = await caClient.register({
+ affiliation,
+ enrollmentID: userId,
+ role: 'client',
+ }, adminUser);
+ const enrollment = await caClient.enroll({
+ enrollmentID: userId,
+ enrollmentSecret: secret,
+ });
+ const x509Identity = {
+ credentials: {
+ certificate: enrollment.certificate,
+ privateKey: enrollment.key.toBytes(),
+ },
+ mspId: orgMspId,
+ type: 'X.509',
+ };
+ await wallet.put(userId, x509Identity);
+ console.log(`Successfully registered and enrolled user ${userId} and imported it into the wallet`);
+ } catch (error) {
+ console.error(`Failed to register user : ${error}`);
+ }
+};
+
+export {
+ buildCAClient,
+ enrollAdmin,
+ registerAndEnrollUser,
+};
diff --git a/asset-transfer-basic/application-typescript/tsconfig.json b/asset-transfer-basic/application-typescript/tsconfig.json
new file mode 100644
index 0000000..8d35748
--- /dev/null
+++ b/asset-transfer-basic/application-typescript/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "outDir": "dist",
+ "target": "es2017",
+ "moduleResolution": "node",
+ "module": "commonjs",
+ "declaration": true,
+ "sourceMap": true,
+ "noImplicitAny": true
+ },
+ "include": [
+ "./src/**/*"
+ ],
+ "exclude": [
+ "./src/**/*.spec.ts"
+ ]
+}
diff --git a/asset-transfer-basic/application-typescript/tslint.json b/asset-transfer-basic/application-typescript/tslint.json
new file mode 100644
index 0000000..a52c3ee
--- /dev/null
+++ b/asset-transfer-basic/application-typescript/tslint.json
@@ -0,0 +1,23 @@
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint:recommended"
+ ],
+ "jsRules": {},
+ "rules": {
+ "indent": [true, "spaces", 4],
+ "linebreak-style": [true, "LF"],
+ "quotemark": [true, "single"],
+ "semicolon": [true, "always"],
+ "no-console": false,
+ "curly": true,
+ "triple-equals": true,
+ "no-string-throw": true,
+ "no-var-keyword": true,
+ "no-trailing-whitespace": true,
+ "object-literal-key-quotes": [true, "as-needed"],
+ "object-literal-sort-keys": false,
+ "max-line-length": false
+ },
+ "rulesDirectory": []
+}
diff --git a/asset-transfer-basic/chaincode-external/.dockerignore b/asset-transfer-basic/chaincode-external/.dockerignore
new file mode 100644
index 0000000..61260e5
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/.dockerignore
@@ -0,0 +1,5 @@
+chaincode.env*
+*.json
+*.md
+*.tar.gz
+*.tgz
diff --git a/asset-transfer-basic/chaincode-external/.gitignore b/asset-transfer-basic/chaincode-external/.gitignore
new file mode 100644
index 0000000..9db4ee6
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/.gitignore
@@ -0,0 +1,2 @@
+*.tar.gz
+*.tgz
diff --git a/asset-transfer-basic/chaincode-external/Dockerfile b/asset-transfer-basic/chaincode-external/Dockerfile
new file mode 100644
index 0000000..833b14e
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/Dockerfile
@@ -0,0 +1,17 @@
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+ARG GO_VER=1.14.4
+ARG ALPINE_VER=3.12
+
+FROM golang:${GO_VER}-alpine${ALPINE_VER}
+
+WORKDIR /go/src/github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-external
+COPY . .
+
+RUN go get -d -v ./...
+RUN go install -v ./...
+
+EXPOSE 9999
+CMD ["chaincode-external"]
diff --git a/asset-transfer-basic/chaincode-external/README.md b/asset-transfer-basic/chaincode-external/README.md
new file mode 100755
index 0000000..1b4a294
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/README.md
@@ -0,0 +1,161 @@
+# Asset-Transfer-Basic as an external service
+
+This sample provides an introduction to how to use external builder and launcher scripts to run chaincode as an external service to your peer. For more information, see the [Chaincode as an external service](https://hyperledger-fabric.readthedocs.io/en/latest/cc_launcher.html) topic in the Fabric documentation.
+
+**Note:** each organization in a real network would need to setup and host their own instance of the external service. For simplification purpose, in this sample we use the same instance for both organizations.
+
+## Setting up the external builder and launcher
+
+External Builders and Launchers is an advanced feature that typically requires custom packaging of the peer image so that it contains all the tools your builder and launcher require. For this sample we use very simple (and crude) shell scripts that can be run directly within the default Fabric peer images.
+
+Open the `config/core.yaml` file at the top of the `fabric-samples` hierarchy. Note that this file comes along with the Fabric binaries, so if you don't have it, follow the [Install the Samples, Binaries and Docker Images](https://hyperledger-fabric.readthedocs.io/en/latest/install.html) instructions in the Hyperledger Fabric documentation to download the binaries and config files.
+
+Modify the field `externalBuilders` as the following:
+```
+externalBuilders:
+ - path: /opt/gopath/src/github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-external/sampleBuilder
+ name: external-sample-builder
+```
+This configuration sets the name of the external builder as `external-sample-builder`, and the path of the builder to the scripts provided in this sample. Note that this is the path within the peer container, not your local machine.
+
+To set the path within the peer container, you will need to modify the container compose file to mount a couple of additional volumes.
+Open the file `test-network/docker/docker-compose-test-net.yaml`, and add to the `volumes` section of both `peer0.org1.example.com` and `peer0.org2.example.com` the following two lines:
+
+```
+ - ../..:/opt/gopath/src/github.com/hyperledger/fabric-samples
+ - ../../config/core.yaml:/etc/hyperledger/fabric/core.yaml
+```
+
+This will mount the fabric-sample builder into the peer container so that it can be found at the location specified in the config file,
+and override the peer's core.yaml config file within the fabric-peer image so that the config file modified above is used.
+
+## Packaging and installing Chaincode
+
+The Asset-Transfer-Basic external chaincode requires two environment variables to run, `CHAINCODE_SERVER_ADDRESS` and `CHAINCODE_ID`, which are described and set in the `chaincode.env` file.
+
+The peer needs a corresponding `connection.json` configuration file so that it can connect to the external Asset-Transfer-Basic service.
+
+The address specified in the `connection.json` must correspond to the `CHAINCODE_SERVER_ADDRESS` value in `chaincode.env`, which is `asset-transfer-basic.org1.example.com:9999` in our example.
+
+Because we will run our chaincode as an external service, the chaincode itself does not need to be included in the chaincode
+package that gets installed to each peer. Only the configuration and metadata information needs to be included
+in the package. Since the packaging is trivial, we can manually create the chaincode package.
+
+First, create a `code.tar.gz` archive containing the `connection.json` file:
+
+```
+tar cfz code.tar.gz connection.json
+```
+
+Then, create the chaincode package, including the `code.tar.gz` file and the supplied `metadata.json` file:
+
+```
+tar cfz asset-transfer-basic-external.tgz metadata.json code.tar.gz
+```
+
+You are now ready to use the external chaincode. We will use the `test-network` sample to get a network setup and make use of it.
+
+## Starting the test network
+
+In a different terminal, from the `test-network` sample directory starts the network using the following command:
+
+```
+./network.sh up createChannel -c mychannel -ca
+```
+
+This starts the test network and creates the channel. We will now proceed to installing our external chaincode package.
+
+## Installing the external chaincode
+
+We can't use the `test-network/network.sh` script to install our external chaincode so we will have to do a bit more work by hand but we can still leverage part of the test-network scripts to make this easier.
+
+First, get the functions to setup your environment as needed by running the following command (this assumes you are still in the `test-network` directory):
+
+```
+. ./scripts/envVar.sh
+```
+
+Install the `asset-transfer-basic-external.tar.gz` chaincode on org1:
+
+```
+setGlobals 1
+../bin/peer lifecycle chaincode install ../asset-transfer-basic/chaincode-external/asset-transfer-basic-external.tgz
+```
+
+setGlobals simply defines a bunch of environment variables suitable to act as one organization or another, org1 or org2.
+
+Install it on org2:
+
+```
+setGlobals 2
+../bin/peer lifecycle chaincode install ../asset-transfer-basic/chaincode-external/asset-transfer-basic-external.tgz
+```
+
+This will output the chaincode pakage identifier such as `basic_1.0:0262396ccaffaa2174bc09f750f742319c4f14d60b16334d2c8921b6842c090c` that you will need to use in the following commands.
+
+For convenience it is best to store your package id value in an environment variable so that it can be referenced in later commands:
+
+```
+export PKGID=basic_1.0:0262396ccaffaa2174bc09f750f742319c4f14d60b16334d2c8921b6842c090
+```
+
+If needed, you can query the installed chaincode to get its package-id:
+
+```
+setGlobals 1
+../bin/peer lifecycle chaincode queryinstalled --peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
+```
+
+Edit the `chaincode.env` file in the `fabric-samples/asset-transfer-basic/chaincode-external` directory as necessary to set the `CHAINCODE_ID` variable to the chaincode package-id obtained above.
+
+
+## Running the Asset-Transfer-Basic external service
+
+To run the service in a container, from a different terminal, build an Asset-Transfer-Basic docker image, using the supplied `Dockerfile`, using the following command in the `fabric-samples/asset-transfer-basic/chaincode-external` directory:
+
+```
+docker build -t hyperledger/asset-transfer-basic .
+```
+
+Then, start the Asset-Transfer-Basic service:
+
+```
+docker run -it --rm --name asset-transfer-basic.org1.example.com --hostname asset-transfer-basic.org1.example.com --env-file chaincode.env --network=net_test hyperledger/asset-transfer-basic
+```
+
+This will start the container and start the external chaincode service within it.
+
+## Finish deploying the Asset-Transfer-Basic external chaincode
+
+Finishing the deployment of the chaincode on the test network can be done from the terminal you started the network from with the following commands (make sure the package-id is set to the value you received above):
+
+```
+setGlobals 2
+../bin/peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $PWD/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name basic --version 1.0 --package-id $PKGID --sequence 1
+
+setGlobals 1
+../bin/peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $PWD/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name basic --version 1.0 --package-id $PKGID --sequence 1
+
+../bin/peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile $PWD/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name basic --peerAddresses localhost:7051 --tlsRootCertFiles $PWD/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1.0 --sequence 1
+```
+
+This approves the chaincode definition for both orgs and commits it using org1. This should result in an output similar to:
+
+```
+2020-08-05 15:41:44.982 PDT [chaincodeCmd] ClientWait -> INFO 001 txid [6bdbe040b99a45cc90a23ec21f02ea5da7be8b70590eb04ff3323ef77fdedfc7] committed with status (VALID) at localhost:7051
+2020-08-05 15:41:44.983 PDT [chaincodeCmd] ClientWait -> INFO 002 txid [6bdbe040b99a45cc90a23ec21f02ea5da7be8b70590eb04ff3323ef77fdedfc7] committed with status (VALID) at localhost:9051
+```
+
+Now that the chaincode is deployed to the channel, and started as an external service, it can be used as normal.
+
+## Using the Asset-Transfer-Basic external chaincode
+
+From yet another terminal, go to `fabric-samples/asset-transfer-basic/application-javascript` and use the node application to test the chaincode you just installed with the following commands:
+
+```
+rm -rf wallet # in case you ran this before
+npm install
+node app.js
+```
+
+If all goes well, it should run exactly the same as described in the "Writing Your First Application" tutorial.
diff --git a/asset-transfer-basic/chaincode-external/assetTransfer.go b/asset-transfer-basic/chaincode-external/assetTransfer.go
new file mode 100644
index 0000000..d66e073
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/assetTransfer.go
@@ -0,0 +1,235 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+type serverConfig struct {
+ CCID string
+ Address string
+}
+
+// SmartContract provides functions for managing an asset
+type SmartContract struct {
+ contractapi.Contract
+}
+
+// Asset describes basic details of what makes up a simple asset
+type Asset struct {
+ ID string `json:"ID"`
+ Color string `json:"color"`
+ Size int `json:"size"`
+ Owner string `json:"owner"`
+ AppraisedValue int `json:"appraisedValue"`
+}
+
+// QueryResult structure used for handling result of query
+type QueryResult struct {
+ Key string `json:"Key"`
+ Record *Asset
+}
+
+// InitLedger adds a base set of cars to the ledger
+func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
+ assets := []Asset{
+ {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
+ {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
+ {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
+ {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
+ {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
+ {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
+ }
+
+ for _, asset := range assets {
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ err = ctx.GetStub().PutState(asset.ID, assetJSON)
+ if err != nil {
+ return fmt.Errorf("failed to put to world state: %v", err)
+ }
+ }
+
+ return nil
+}
+
+// CreateAsset issues a new asset to the world state with given details.
+func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id, color string, size int, owner string, appraisedValue int) error {
+ exists, err := s.AssetExists(ctx, id)
+ if err != nil {
+ return err
+ }
+ if exists {
+ return fmt.Errorf("the asset %s already exists", id)
+ }
+ asset := Asset{
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ }
+
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// ReadAsset returns the asset stored in the world state with given id.
+func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
+ assetJSON, err := ctx.GetStub().GetState(id)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read from world state. %s", err.Error())
+ }
+ if assetJSON == nil {
+ return nil, fmt.Errorf("the asset %s does not exist", id)
+ }
+
+ var asset Asset
+ err = json.Unmarshal(assetJSON, &asset)
+ if err != nil {
+ return nil, err
+ }
+
+ return &asset, nil
+}
+
+// UpdateAsset updates an existing asset in the world state with provided parameters.
+func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id, color string, size int, owner string, appraisedValue int) error {
+ exists, err := s.AssetExists(ctx, id)
+ if err != nil {
+ return err
+ }
+ if !exists {
+ return fmt.Errorf("the asset %s does not exist", id)
+ }
+
+ // overwritting original asset with new asset
+ asset := Asset{
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ }
+
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// DeleteAsset deletes an given asset from the world state.
+func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
+ exists, err := s.AssetExists(ctx, id)
+ if err != nil {
+ return err
+ }
+ if !exists {
+ return fmt.Errorf("the asset %s does not exist", id)
+ }
+
+ return ctx.GetStub().DelState(id)
+}
+
+// AssetExists returns true when asset with given ID exists in world state
+func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
+ assetJSON, err := ctx.GetStub().GetState(id)
+ if err != nil {
+ return false, fmt.Errorf("failed to read from world state. %s", err.Error())
+ }
+
+ return assetJSON != nil, nil
+}
+
+// TransferAsset updates the owner field of asset with given id in world state.
+func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
+ asset, err := s.ReadAsset(ctx, id)
+ if err != nil {
+ return err
+ }
+
+ asset.Owner = newOwner
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// GetAllAssets returns all assets found in world state
+func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
+ // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace.
+ resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
+
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ var results []QueryResult
+
+ for resultsIterator.HasNext() {
+ queryResponse, err := resultsIterator.Next()
+
+ if err != nil {
+ return nil, err
+ }
+
+ var asset Asset
+ err = json.Unmarshal(queryResponse.Value, &asset)
+ if err != nil {
+ return nil, err
+ }
+
+ queryResult := QueryResult{Key: queryResponse.Key, Record: &asset}
+ results = append(results, queryResult)
+ }
+
+ return results, nil
+}
+
+func main() {
+ // See chaincode.env.example
+ config := serverConfig{
+ CCID: os.Getenv("CHAINCODE_ID"),
+ Address: os.Getenv("CHAINCODE_SERVER_ADDRESS"),
+ }
+
+ chaincode, err := contractapi.NewChaincode(&SmartContract{})
+
+ if err != nil {
+ log.Panicf("error create asset-transfer-basic chaincode: %s", err)
+ }
+
+ server := &shim.ChaincodeServer{
+ CCID: config.CCID,
+ Address: config.Address,
+ CC: chaincode,
+ TLSProps: shim.TLSProperties{
+ Disabled: true,
+ },
+ }
+
+ if err := server.Start(); err != nil {
+ log.Panicf("error starting asset-transfer-basic chaincode: %s", err)
+ }
+}
diff --git a/asset-transfer-basic/chaincode-external/chaincode.env b/asset-transfer-basic/chaincode-external/chaincode.env
new file mode 100644
index 0000000..d029f58
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/chaincode.env
@@ -0,0 +1,8 @@
+# CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can
+# connect to the chaincode server
+CHAINCODE_SERVER_ADDRESS=asset-transfer-basic.org1.example.com:9999
+
+# CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode
+# on install. The `peer lifecycle chaincode queryinstalled` command can be
+# used to get the ID after install if required
+CHAINCODE_ID=basic_1.0:0262396ccaffaa2174bc09f750f742319c4f14d60b16334d2c8921b6842c090c
diff --git a/asset-transfer-basic/chaincode-external/connection.json b/asset-transfer-basic/chaincode-external/connection.json
new file mode 100644
index 0000000..d97f8f1
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/connection.json
@@ -0,0 +1,5 @@
+{
+ "address": "asset-transfer-basic.org1.example.com:9999",
+ "dial_timeout": "10s",
+ "tls_required": false
+}
diff --git a/asset-transfer-basic/chaincode-external/go.mod b/asset-transfer-basic/chaincode-external/go.mod
new file mode 100644
index 0000000..d8e3eec
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/go.mod
@@ -0,0 +1,8 @@
+module github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-external
+
+go 1.14
+
+require (
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212
+ github.com/hyperledger/fabric-contract-api-go v1.1.0
+)
diff --git a/asset-transfer-basic/chaincode-external/go.sum b/asset-transfer-basic/chaincode-external/go.sum
new file mode 100644
index 0000000..a159a45
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/go.sum
@@ -0,0 +1,146 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/asset-transfer-basic/chaincode-external/metadata.json b/asset-transfer-basic/chaincode-external/metadata.json
new file mode 100644
index 0000000..ae23860
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/metadata.json
@@ -0,0 +1,4 @@
+{
+ "type": "external",
+ "label": "basic_1.0"
+}
diff --git a/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect
new file mode 100755
index 0000000..d4b7007
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/detect
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+set -euo pipefail
+
+METADIR=$2
+# check if the "type" field is set to "external"
+# crude way without jq which is not in the default fabric peer image
+TYPE=$(tr -d '\n' < "$METADIR/metadata.json" | awk -F':' '{ for (i = 1; i < NF; i++){ if ($i~/type/) { print $(i+1); break }}}'| cut -d\" -f2)
+
+if [ "$TYPE" = "external" ]; then
+ exit 0
+fi
+
+exit 1
diff --git a/asset-transfer-basic/chaincode-external/sampleBuilder/bin/release b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/release
new file mode 100755
index 0000000..7f03a05
--- /dev/null
+++ b/asset-transfer-basic/chaincode-external/sampleBuilder/bin/release
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+set -euo pipefail
+
+BLD="$1"
+RELEASE="$2"
+
+if [ -d "$BLD/metadata" ]; then
+ cp -a "$BLD/metadata/"* "$RELEASE/"
+fi
+
+#external chaincodes expect artifacts to be placed under "$RELEASE"/chaincode/server
+if [ -f $BLD/connection.json ]; then
+ mkdir -p "$RELEASE"/chaincode/server
+ cp $BLD/connection.json "$RELEASE"/chaincode/server
+
+ #if tls_required is true, copy TLS files (using above example, the fully qualified path for these fils would be "$RELEASE"/chaincode/server/tls)
+
+ exit 0
+fi
+
+exit 1
diff --git a/asset-transfer-basic/chaincode-go/assetTransfer.go b/asset-transfer-basic/chaincode-go/assetTransfer.go
new file mode 100644
index 0000000..9c619d5
--- /dev/null
+++ b/asset-transfer-basic/chaincode-go/assetTransfer.go
@@ -0,0 +1,23 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "log"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ "github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode"
+)
+
+func main() {
+ assetChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})
+ if err != nil {
+ log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
+ }
+
+ if err := assetChaincode.Start(); err != nil {
+ log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
+ }
+}
diff --git a/asset-transfer-basic/chaincode-go/chaincode/mocks/chaincodestub.go b/asset-transfer-basic/chaincode-go/chaincode/mocks/chaincodestub.go
new file mode 100644
index 0000000..91348fe
--- /dev/null
+++ b/asset-transfer-basic/chaincode-go/chaincode/mocks/chaincodestub.go
@@ -0,0 +1,2878 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package mocks
+
+import (
+ "sync"
+
+ "github.com/golang/protobuf/ptypes/timestamp"
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-protos-go/peer"
+)
+
+type ChaincodeStub struct {
+ CreateCompositeKeyStub func(string, []string) (string, error)
+ createCompositeKeyMutex sync.RWMutex
+ createCompositeKeyArgsForCall []struct {
+ arg1 string
+ arg2 []string
+ }
+ createCompositeKeyReturns struct {
+ result1 string
+ result2 error
+ }
+ createCompositeKeyReturnsOnCall map[int]struct {
+ result1 string
+ result2 error
+ }
+ DelPrivateDataStub func(string, string) error
+ delPrivateDataMutex sync.RWMutex
+ delPrivateDataArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ delPrivateDataReturns struct {
+ result1 error
+ }
+ delPrivateDataReturnsOnCall map[int]struct {
+ result1 error
+ }
+ DelStateStub func(string) error
+ delStateMutex sync.RWMutex
+ delStateArgsForCall []struct {
+ arg1 string
+ }
+ delStateReturns struct {
+ result1 error
+ }
+ delStateReturnsOnCall map[int]struct {
+ result1 error
+ }
+ GetArgsStub func() [][]byte
+ getArgsMutex sync.RWMutex
+ getArgsArgsForCall []struct {
+ }
+ getArgsReturns struct {
+ result1 [][]byte
+ }
+ getArgsReturnsOnCall map[int]struct {
+ result1 [][]byte
+ }
+ GetArgsSliceStub func() ([]byte, error)
+ getArgsSliceMutex sync.RWMutex
+ getArgsSliceArgsForCall []struct {
+ }
+ getArgsSliceReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getArgsSliceReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetBindingStub func() ([]byte, error)
+ getBindingMutex sync.RWMutex
+ getBindingArgsForCall []struct {
+ }
+ getBindingReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getBindingReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetChannelIDStub func() string
+ getChannelIDMutex sync.RWMutex
+ getChannelIDArgsForCall []struct {
+ }
+ getChannelIDReturns struct {
+ result1 string
+ }
+ getChannelIDReturnsOnCall map[int]struct {
+ result1 string
+ }
+ GetCreatorStub func() ([]byte, error)
+ getCreatorMutex sync.RWMutex
+ getCreatorArgsForCall []struct {
+ }
+ getCreatorReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getCreatorReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetDecorationsStub func() map[string][]byte
+ getDecorationsMutex sync.RWMutex
+ getDecorationsArgsForCall []struct {
+ }
+ getDecorationsReturns struct {
+ result1 map[string][]byte
+ }
+ getDecorationsReturnsOnCall map[int]struct {
+ result1 map[string][]byte
+ }
+ GetFunctionAndParametersStub func() (string, []string)
+ getFunctionAndParametersMutex sync.RWMutex
+ getFunctionAndParametersArgsForCall []struct {
+ }
+ getFunctionAndParametersReturns struct {
+ result1 string
+ result2 []string
+ }
+ getFunctionAndParametersReturnsOnCall map[int]struct {
+ result1 string
+ result2 []string
+ }
+ GetHistoryForKeyStub func(string) (shim.HistoryQueryIteratorInterface, error)
+ getHistoryForKeyMutex sync.RWMutex
+ getHistoryForKeyArgsForCall []struct {
+ arg1 string
+ }
+ getHistoryForKeyReturns struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ }
+ getHistoryForKeyReturnsOnCall map[int]struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ }
+ GetPrivateDataStub func(string, string) ([]byte, error)
+ getPrivateDataMutex sync.RWMutex
+ getPrivateDataArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getPrivateDataReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getPrivateDataReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetPrivateDataByPartialCompositeKeyStub func(string, string, []string) (shim.StateQueryIteratorInterface, error)
+ getPrivateDataByPartialCompositeKeyMutex sync.RWMutex
+ getPrivateDataByPartialCompositeKeyArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 []string
+ }
+ getPrivateDataByPartialCompositeKeyReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getPrivateDataByPartialCompositeKeyReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetPrivateDataByRangeStub func(string, string, string) (shim.StateQueryIteratorInterface, error)
+ getPrivateDataByRangeMutex sync.RWMutex
+ getPrivateDataByRangeArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 string
+ }
+ getPrivateDataByRangeReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getPrivateDataByRangeReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetPrivateDataHashStub func(string, string) ([]byte, error)
+ getPrivateDataHashMutex sync.RWMutex
+ getPrivateDataHashArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getPrivateDataHashReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getPrivateDataHashReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetPrivateDataQueryResultStub func(string, string) (shim.StateQueryIteratorInterface, error)
+ getPrivateDataQueryResultMutex sync.RWMutex
+ getPrivateDataQueryResultArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getPrivateDataQueryResultReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getPrivateDataQueryResultReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetPrivateDataValidationParameterStub func(string, string) ([]byte, error)
+ getPrivateDataValidationParameterMutex sync.RWMutex
+ getPrivateDataValidationParameterArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getPrivateDataValidationParameterReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getPrivateDataValidationParameterReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetQueryResultStub func(string) (shim.StateQueryIteratorInterface, error)
+ getQueryResultMutex sync.RWMutex
+ getQueryResultArgsForCall []struct {
+ arg1 string
+ }
+ getQueryResultReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getQueryResultReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetQueryResultWithPaginationStub func(string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)
+ getQueryResultWithPaginationMutex sync.RWMutex
+ getQueryResultWithPaginationArgsForCall []struct {
+ arg1 string
+ arg2 int32
+ arg3 string
+ }
+ getQueryResultWithPaginationReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ getQueryResultWithPaginationReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ GetSignedProposalStub func() (*peer.SignedProposal, error)
+ getSignedProposalMutex sync.RWMutex
+ getSignedProposalArgsForCall []struct {
+ }
+ getSignedProposalReturns struct {
+ result1 *peer.SignedProposal
+ result2 error
+ }
+ getSignedProposalReturnsOnCall map[int]struct {
+ result1 *peer.SignedProposal
+ result2 error
+ }
+ GetStateStub func(string) ([]byte, error)
+ getStateMutex sync.RWMutex
+ getStateArgsForCall []struct {
+ arg1 string
+ }
+ getStateReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getStateReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetStateByPartialCompositeKeyStub func(string, []string) (shim.StateQueryIteratorInterface, error)
+ getStateByPartialCompositeKeyMutex sync.RWMutex
+ getStateByPartialCompositeKeyArgsForCall []struct {
+ arg1 string
+ arg2 []string
+ }
+ getStateByPartialCompositeKeyReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getStateByPartialCompositeKeyReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetStateByPartialCompositeKeyWithPaginationStub func(string, []string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)
+ getStateByPartialCompositeKeyWithPaginationMutex sync.RWMutex
+ getStateByPartialCompositeKeyWithPaginationArgsForCall []struct {
+ arg1 string
+ arg2 []string
+ arg3 int32
+ arg4 string
+ }
+ getStateByPartialCompositeKeyWithPaginationReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ getStateByPartialCompositeKeyWithPaginationReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ GetStateByRangeStub func(string, string) (shim.StateQueryIteratorInterface, error)
+ getStateByRangeMutex sync.RWMutex
+ getStateByRangeArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getStateByRangeReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getStateByRangeReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetStateByRangeWithPaginationStub func(string, string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)
+ getStateByRangeWithPaginationMutex sync.RWMutex
+ getStateByRangeWithPaginationArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 int32
+ arg4 string
+ }
+ getStateByRangeWithPaginationReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ getStateByRangeWithPaginationReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ GetStateValidationParameterStub func(string) ([]byte, error)
+ getStateValidationParameterMutex sync.RWMutex
+ getStateValidationParameterArgsForCall []struct {
+ arg1 string
+ }
+ getStateValidationParameterReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getStateValidationParameterReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetStringArgsStub func() []string
+ getStringArgsMutex sync.RWMutex
+ getStringArgsArgsForCall []struct {
+ }
+ getStringArgsReturns struct {
+ result1 []string
+ }
+ getStringArgsReturnsOnCall map[int]struct {
+ result1 []string
+ }
+ GetTransientStub func() (map[string][]byte, error)
+ getTransientMutex sync.RWMutex
+ getTransientArgsForCall []struct {
+ }
+ getTransientReturns struct {
+ result1 map[string][]byte
+ result2 error
+ }
+ getTransientReturnsOnCall map[int]struct {
+ result1 map[string][]byte
+ result2 error
+ }
+ GetTxIDStub func() string
+ getTxIDMutex sync.RWMutex
+ getTxIDArgsForCall []struct {
+ }
+ getTxIDReturns struct {
+ result1 string
+ }
+ getTxIDReturnsOnCall map[int]struct {
+ result1 string
+ }
+ GetTxTimestampStub func() (*timestamp.Timestamp, error)
+ getTxTimestampMutex sync.RWMutex
+ getTxTimestampArgsForCall []struct {
+ }
+ getTxTimestampReturns struct {
+ result1 *timestamp.Timestamp
+ result2 error
+ }
+ getTxTimestampReturnsOnCall map[int]struct {
+ result1 *timestamp.Timestamp
+ result2 error
+ }
+ InvokeChaincodeStub func(string, [][]byte, string) peer.Response
+ invokeChaincodeMutex sync.RWMutex
+ invokeChaincodeArgsForCall []struct {
+ arg1 string
+ arg2 [][]byte
+ arg3 string
+ }
+ invokeChaincodeReturns struct {
+ result1 peer.Response
+ }
+ invokeChaincodeReturnsOnCall map[int]struct {
+ result1 peer.Response
+ }
+ PutPrivateDataStub func(string, string, []byte) error
+ putPrivateDataMutex sync.RWMutex
+ putPrivateDataArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 []byte
+ }
+ putPrivateDataReturns struct {
+ result1 error
+ }
+ putPrivateDataReturnsOnCall map[int]struct {
+ result1 error
+ }
+ PutStateStub func(string, []byte) error
+ putStateMutex sync.RWMutex
+ putStateArgsForCall []struct {
+ arg1 string
+ arg2 []byte
+ }
+ putStateReturns struct {
+ result1 error
+ }
+ putStateReturnsOnCall map[int]struct {
+ result1 error
+ }
+ SetEventStub func(string, []byte) error
+ setEventMutex sync.RWMutex
+ setEventArgsForCall []struct {
+ arg1 string
+ arg2 []byte
+ }
+ setEventReturns struct {
+ result1 error
+ }
+ setEventReturnsOnCall map[int]struct {
+ result1 error
+ }
+ SetPrivateDataValidationParameterStub func(string, string, []byte) error
+ setPrivateDataValidationParameterMutex sync.RWMutex
+ setPrivateDataValidationParameterArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 []byte
+ }
+ setPrivateDataValidationParameterReturns struct {
+ result1 error
+ }
+ setPrivateDataValidationParameterReturnsOnCall map[int]struct {
+ result1 error
+ }
+ SetStateValidationParameterStub func(string, []byte) error
+ setStateValidationParameterMutex sync.RWMutex
+ setStateValidationParameterArgsForCall []struct {
+ arg1 string
+ arg2 []byte
+ }
+ setStateValidationParameterReturns struct {
+ result1 error
+ }
+ setStateValidationParameterReturnsOnCall map[int]struct {
+ result1 error
+ }
+ SplitCompositeKeyStub func(string) (string, []string, error)
+ splitCompositeKeyMutex sync.RWMutex
+ splitCompositeKeyArgsForCall []struct {
+ arg1 string
+ }
+ splitCompositeKeyReturns struct {
+ result1 string
+ result2 []string
+ result3 error
+ }
+ splitCompositeKeyReturnsOnCall map[int]struct {
+ result1 string
+ result2 []string
+ result3 error
+ }
+ invocations map[string][][]interface{}
+ invocationsMutex sync.RWMutex
+}
+
+func (fake *ChaincodeStub) CreateCompositeKey(arg1 string, arg2 []string) (string, error) {
+ var arg2Copy []string
+ if arg2 != nil {
+ arg2Copy = make([]string, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.createCompositeKeyMutex.Lock()
+ ret, specificReturn := fake.createCompositeKeyReturnsOnCall[len(fake.createCompositeKeyArgsForCall)]
+ fake.createCompositeKeyArgsForCall = append(fake.createCompositeKeyArgsForCall, struct {
+ arg1 string
+ arg2 []string
+ }{arg1, arg2Copy})
+ fake.recordInvocation("CreateCompositeKey", []interface{}{arg1, arg2Copy})
+ fake.createCompositeKeyMutex.Unlock()
+ if fake.CreateCompositeKeyStub != nil {
+ return fake.CreateCompositeKeyStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.createCompositeKeyReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyCallCount() int {
+ fake.createCompositeKeyMutex.RLock()
+ defer fake.createCompositeKeyMutex.RUnlock()
+ return len(fake.createCompositeKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyCalls(stub func(string, []string) (string, error)) {
+ fake.createCompositeKeyMutex.Lock()
+ defer fake.createCompositeKeyMutex.Unlock()
+ fake.CreateCompositeKeyStub = stub
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyArgsForCall(i int) (string, []string) {
+ fake.createCompositeKeyMutex.RLock()
+ defer fake.createCompositeKeyMutex.RUnlock()
+ argsForCall := fake.createCompositeKeyArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyReturns(result1 string, result2 error) {
+ fake.createCompositeKeyMutex.Lock()
+ defer fake.createCompositeKeyMutex.Unlock()
+ fake.CreateCompositeKeyStub = nil
+ fake.createCompositeKeyReturns = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyReturnsOnCall(i int, result1 string, result2 error) {
+ fake.createCompositeKeyMutex.Lock()
+ defer fake.createCompositeKeyMutex.Unlock()
+ fake.CreateCompositeKeyStub = nil
+ if fake.createCompositeKeyReturnsOnCall == nil {
+ fake.createCompositeKeyReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 error
+ })
+ }
+ fake.createCompositeKeyReturnsOnCall[i] = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) DelPrivateData(arg1 string, arg2 string) error {
+ fake.delPrivateDataMutex.Lock()
+ ret, specificReturn := fake.delPrivateDataReturnsOnCall[len(fake.delPrivateDataArgsForCall)]
+ fake.delPrivateDataArgsForCall = append(fake.delPrivateDataArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("DelPrivateData", []interface{}{arg1, arg2})
+ fake.delPrivateDataMutex.Unlock()
+ if fake.DelPrivateDataStub != nil {
+ return fake.DelPrivateDataStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.delPrivateDataReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) DelPrivateDataCallCount() int {
+ fake.delPrivateDataMutex.RLock()
+ defer fake.delPrivateDataMutex.RUnlock()
+ return len(fake.delPrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) DelPrivateDataCalls(stub func(string, string) error) {
+ fake.delPrivateDataMutex.Lock()
+ defer fake.delPrivateDataMutex.Unlock()
+ fake.DelPrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) DelPrivateDataArgsForCall(i int) (string, string) {
+ fake.delPrivateDataMutex.RLock()
+ defer fake.delPrivateDataMutex.RUnlock()
+ argsForCall := fake.delPrivateDataArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) DelPrivateDataReturns(result1 error) {
+ fake.delPrivateDataMutex.Lock()
+ defer fake.delPrivateDataMutex.Unlock()
+ fake.DelPrivateDataStub = nil
+ fake.delPrivateDataReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) DelPrivateDataReturnsOnCall(i int, result1 error) {
+ fake.delPrivateDataMutex.Lock()
+ defer fake.delPrivateDataMutex.Unlock()
+ fake.DelPrivateDataStub = nil
+ if fake.delPrivateDataReturnsOnCall == nil {
+ fake.delPrivateDataReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.delPrivateDataReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) DelState(arg1 string) error {
+ fake.delStateMutex.Lock()
+ ret, specificReturn := fake.delStateReturnsOnCall[len(fake.delStateArgsForCall)]
+ fake.delStateArgsForCall = append(fake.delStateArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("DelState", []interface{}{arg1})
+ fake.delStateMutex.Unlock()
+ if fake.DelStateStub != nil {
+ return fake.DelStateStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.delStateReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) DelStateCallCount() int {
+ fake.delStateMutex.RLock()
+ defer fake.delStateMutex.RUnlock()
+ return len(fake.delStateArgsForCall)
+}
+
+func (fake *ChaincodeStub) DelStateCalls(stub func(string) error) {
+ fake.delStateMutex.Lock()
+ defer fake.delStateMutex.Unlock()
+ fake.DelStateStub = stub
+}
+
+func (fake *ChaincodeStub) DelStateArgsForCall(i int) string {
+ fake.delStateMutex.RLock()
+ defer fake.delStateMutex.RUnlock()
+ argsForCall := fake.delStateArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) DelStateReturns(result1 error) {
+ fake.delStateMutex.Lock()
+ defer fake.delStateMutex.Unlock()
+ fake.DelStateStub = nil
+ fake.delStateReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) DelStateReturnsOnCall(i int, result1 error) {
+ fake.delStateMutex.Lock()
+ defer fake.delStateMutex.Unlock()
+ fake.DelStateStub = nil
+ if fake.delStateReturnsOnCall == nil {
+ fake.delStateReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.delStateReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetArgs() [][]byte {
+ fake.getArgsMutex.Lock()
+ ret, specificReturn := fake.getArgsReturnsOnCall[len(fake.getArgsArgsForCall)]
+ fake.getArgsArgsForCall = append(fake.getArgsArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetArgs", []interface{}{})
+ fake.getArgsMutex.Unlock()
+ if fake.GetArgsStub != nil {
+ return fake.GetArgsStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getArgsReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetArgsCallCount() int {
+ fake.getArgsMutex.RLock()
+ defer fake.getArgsMutex.RUnlock()
+ return len(fake.getArgsArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetArgsCalls(stub func() [][]byte) {
+ fake.getArgsMutex.Lock()
+ defer fake.getArgsMutex.Unlock()
+ fake.GetArgsStub = stub
+}
+
+func (fake *ChaincodeStub) GetArgsReturns(result1 [][]byte) {
+ fake.getArgsMutex.Lock()
+ defer fake.getArgsMutex.Unlock()
+ fake.GetArgsStub = nil
+ fake.getArgsReturns = struct {
+ result1 [][]byte
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetArgsReturnsOnCall(i int, result1 [][]byte) {
+ fake.getArgsMutex.Lock()
+ defer fake.getArgsMutex.Unlock()
+ fake.GetArgsStub = nil
+ if fake.getArgsReturnsOnCall == nil {
+ fake.getArgsReturnsOnCall = make(map[int]struct {
+ result1 [][]byte
+ })
+ }
+ fake.getArgsReturnsOnCall[i] = struct {
+ result1 [][]byte
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetArgsSlice() ([]byte, error) {
+ fake.getArgsSliceMutex.Lock()
+ ret, specificReturn := fake.getArgsSliceReturnsOnCall[len(fake.getArgsSliceArgsForCall)]
+ fake.getArgsSliceArgsForCall = append(fake.getArgsSliceArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetArgsSlice", []interface{}{})
+ fake.getArgsSliceMutex.Unlock()
+ if fake.GetArgsSliceStub != nil {
+ return fake.GetArgsSliceStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getArgsSliceReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetArgsSliceCallCount() int {
+ fake.getArgsSliceMutex.RLock()
+ defer fake.getArgsSliceMutex.RUnlock()
+ return len(fake.getArgsSliceArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetArgsSliceCalls(stub func() ([]byte, error)) {
+ fake.getArgsSliceMutex.Lock()
+ defer fake.getArgsSliceMutex.Unlock()
+ fake.GetArgsSliceStub = stub
+}
+
+func (fake *ChaincodeStub) GetArgsSliceReturns(result1 []byte, result2 error) {
+ fake.getArgsSliceMutex.Lock()
+ defer fake.getArgsSliceMutex.Unlock()
+ fake.GetArgsSliceStub = nil
+ fake.getArgsSliceReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetArgsSliceReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getArgsSliceMutex.Lock()
+ defer fake.getArgsSliceMutex.Unlock()
+ fake.GetArgsSliceStub = nil
+ if fake.getArgsSliceReturnsOnCall == nil {
+ fake.getArgsSliceReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getArgsSliceReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetBinding() ([]byte, error) {
+ fake.getBindingMutex.Lock()
+ ret, specificReturn := fake.getBindingReturnsOnCall[len(fake.getBindingArgsForCall)]
+ fake.getBindingArgsForCall = append(fake.getBindingArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetBinding", []interface{}{})
+ fake.getBindingMutex.Unlock()
+ if fake.GetBindingStub != nil {
+ return fake.GetBindingStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getBindingReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetBindingCallCount() int {
+ fake.getBindingMutex.RLock()
+ defer fake.getBindingMutex.RUnlock()
+ return len(fake.getBindingArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetBindingCalls(stub func() ([]byte, error)) {
+ fake.getBindingMutex.Lock()
+ defer fake.getBindingMutex.Unlock()
+ fake.GetBindingStub = stub
+}
+
+func (fake *ChaincodeStub) GetBindingReturns(result1 []byte, result2 error) {
+ fake.getBindingMutex.Lock()
+ defer fake.getBindingMutex.Unlock()
+ fake.GetBindingStub = nil
+ fake.getBindingReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetBindingReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getBindingMutex.Lock()
+ defer fake.getBindingMutex.Unlock()
+ fake.GetBindingStub = nil
+ if fake.getBindingReturnsOnCall == nil {
+ fake.getBindingReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getBindingReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetChannelID() string {
+ fake.getChannelIDMutex.Lock()
+ ret, specificReturn := fake.getChannelIDReturnsOnCall[len(fake.getChannelIDArgsForCall)]
+ fake.getChannelIDArgsForCall = append(fake.getChannelIDArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetChannelID", []interface{}{})
+ fake.getChannelIDMutex.Unlock()
+ if fake.GetChannelIDStub != nil {
+ return fake.GetChannelIDStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getChannelIDReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetChannelIDCallCount() int {
+ fake.getChannelIDMutex.RLock()
+ defer fake.getChannelIDMutex.RUnlock()
+ return len(fake.getChannelIDArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetChannelIDCalls(stub func() string) {
+ fake.getChannelIDMutex.Lock()
+ defer fake.getChannelIDMutex.Unlock()
+ fake.GetChannelIDStub = stub
+}
+
+func (fake *ChaincodeStub) GetChannelIDReturns(result1 string) {
+ fake.getChannelIDMutex.Lock()
+ defer fake.getChannelIDMutex.Unlock()
+ fake.GetChannelIDStub = nil
+ fake.getChannelIDReturns = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetChannelIDReturnsOnCall(i int, result1 string) {
+ fake.getChannelIDMutex.Lock()
+ defer fake.getChannelIDMutex.Unlock()
+ fake.GetChannelIDStub = nil
+ if fake.getChannelIDReturnsOnCall == nil {
+ fake.getChannelIDReturnsOnCall = make(map[int]struct {
+ result1 string
+ })
+ }
+ fake.getChannelIDReturnsOnCall[i] = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetCreator() ([]byte, error) {
+ fake.getCreatorMutex.Lock()
+ ret, specificReturn := fake.getCreatorReturnsOnCall[len(fake.getCreatorArgsForCall)]
+ fake.getCreatorArgsForCall = append(fake.getCreatorArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetCreator", []interface{}{})
+ fake.getCreatorMutex.Unlock()
+ if fake.GetCreatorStub != nil {
+ return fake.GetCreatorStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getCreatorReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetCreatorCallCount() int {
+ fake.getCreatorMutex.RLock()
+ defer fake.getCreatorMutex.RUnlock()
+ return len(fake.getCreatorArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetCreatorCalls(stub func() ([]byte, error)) {
+ fake.getCreatorMutex.Lock()
+ defer fake.getCreatorMutex.Unlock()
+ fake.GetCreatorStub = stub
+}
+
+func (fake *ChaincodeStub) GetCreatorReturns(result1 []byte, result2 error) {
+ fake.getCreatorMutex.Lock()
+ defer fake.getCreatorMutex.Unlock()
+ fake.GetCreatorStub = nil
+ fake.getCreatorReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetCreatorReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getCreatorMutex.Lock()
+ defer fake.getCreatorMutex.Unlock()
+ fake.GetCreatorStub = nil
+ if fake.getCreatorReturnsOnCall == nil {
+ fake.getCreatorReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getCreatorReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetDecorations() map[string][]byte {
+ fake.getDecorationsMutex.Lock()
+ ret, specificReturn := fake.getDecorationsReturnsOnCall[len(fake.getDecorationsArgsForCall)]
+ fake.getDecorationsArgsForCall = append(fake.getDecorationsArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetDecorations", []interface{}{})
+ fake.getDecorationsMutex.Unlock()
+ if fake.GetDecorationsStub != nil {
+ return fake.GetDecorationsStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getDecorationsReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetDecorationsCallCount() int {
+ fake.getDecorationsMutex.RLock()
+ defer fake.getDecorationsMutex.RUnlock()
+ return len(fake.getDecorationsArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetDecorationsCalls(stub func() map[string][]byte) {
+ fake.getDecorationsMutex.Lock()
+ defer fake.getDecorationsMutex.Unlock()
+ fake.GetDecorationsStub = stub
+}
+
+func (fake *ChaincodeStub) GetDecorationsReturns(result1 map[string][]byte) {
+ fake.getDecorationsMutex.Lock()
+ defer fake.getDecorationsMutex.Unlock()
+ fake.GetDecorationsStub = nil
+ fake.getDecorationsReturns = struct {
+ result1 map[string][]byte
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetDecorationsReturnsOnCall(i int, result1 map[string][]byte) {
+ fake.getDecorationsMutex.Lock()
+ defer fake.getDecorationsMutex.Unlock()
+ fake.GetDecorationsStub = nil
+ if fake.getDecorationsReturnsOnCall == nil {
+ fake.getDecorationsReturnsOnCall = make(map[int]struct {
+ result1 map[string][]byte
+ })
+ }
+ fake.getDecorationsReturnsOnCall[i] = struct {
+ result1 map[string][]byte
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParameters() (string, []string) {
+ fake.getFunctionAndParametersMutex.Lock()
+ ret, specificReturn := fake.getFunctionAndParametersReturnsOnCall[len(fake.getFunctionAndParametersArgsForCall)]
+ fake.getFunctionAndParametersArgsForCall = append(fake.getFunctionAndParametersArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetFunctionAndParameters", []interface{}{})
+ fake.getFunctionAndParametersMutex.Unlock()
+ if fake.GetFunctionAndParametersStub != nil {
+ return fake.GetFunctionAndParametersStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getFunctionAndParametersReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParametersCallCount() int {
+ fake.getFunctionAndParametersMutex.RLock()
+ defer fake.getFunctionAndParametersMutex.RUnlock()
+ return len(fake.getFunctionAndParametersArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParametersCalls(stub func() (string, []string)) {
+ fake.getFunctionAndParametersMutex.Lock()
+ defer fake.getFunctionAndParametersMutex.Unlock()
+ fake.GetFunctionAndParametersStub = stub
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParametersReturns(result1 string, result2 []string) {
+ fake.getFunctionAndParametersMutex.Lock()
+ defer fake.getFunctionAndParametersMutex.Unlock()
+ fake.GetFunctionAndParametersStub = nil
+ fake.getFunctionAndParametersReturns = struct {
+ result1 string
+ result2 []string
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParametersReturnsOnCall(i int, result1 string, result2 []string) {
+ fake.getFunctionAndParametersMutex.Lock()
+ defer fake.getFunctionAndParametersMutex.Unlock()
+ fake.GetFunctionAndParametersStub = nil
+ if fake.getFunctionAndParametersReturnsOnCall == nil {
+ fake.getFunctionAndParametersReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 []string
+ })
+ }
+ fake.getFunctionAndParametersReturnsOnCall[i] = struct {
+ result1 string
+ result2 []string
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetHistoryForKey(arg1 string) (shim.HistoryQueryIteratorInterface, error) {
+ fake.getHistoryForKeyMutex.Lock()
+ ret, specificReturn := fake.getHistoryForKeyReturnsOnCall[len(fake.getHistoryForKeyArgsForCall)]
+ fake.getHistoryForKeyArgsForCall = append(fake.getHistoryForKeyArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetHistoryForKey", []interface{}{arg1})
+ fake.getHistoryForKeyMutex.Unlock()
+ if fake.GetHistoryForKeyStub != nil {
+ return fake.GetHistoryForKeyStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getHistoryForKeyReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyCallCount() int {
+ fake.getHistoryForKeyMutex.RLock()
+ defer fake.getHistoryForKeyMutex.RUnlock()
+ return len(fake.getHistoryForKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyCalls(stub func(string) (shim.HistoryQueryIteratorInterface, error)) {
+ fake.getHistoryForKeyMutex.Lock()
+ defer fake.getHistoryForKeyMutex.Unlock()
+ fake.GetHistoryForKeyStub = stub
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyArgsForCall(i int) string {
+ fake.getHistoryForKeyMutex.RLock()
+ defer fake.getHistoryForKeyMutex.RUnlock()
+ argsForCall := fake.getHistoryForKeyArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyReturns(result1 shim.HistoryQueryIteratorInterface, result2 error) {
+ fake.getHistoryForKeyMutex.Lock()
+ defer fake.getHistoryForKeyMutex.Unlock()
+ fake.GetHistoryForKeyStub = nil
+ fake.getHistoryForKeyReturns = struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyReturnsOnCall(i int, result1 shim.HistoryQueryIteratorInterface, result2 error) {
+ fake.getHistoryForKeyMutex.Lock()
+ defer fake.getHistoryForKeyMutex.Unlock()
+ fake.GetHistoryForKeyStub = nil
+ if fake.getHistoryForKeyReturnsOnCall == nil {
+ fake.getHistoryForKeyReturnsOnCall = make(map[int]struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getHistoryForKeyReturnsOnCall[i] = struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateData(arg1 string, arg2 string) ([]byte, error) {
+ fake.getPrivateDataMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataReturnsOnCall[len(fake.getPrivateDataArgsForCall)]
+ fake.getPrivateDataArgsForCall = append(fake.getPrivateDataArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetPrivateData", []interface{}{arg1, arg2})
+ fake.getPrivateDataMutex.Unlock()
+ if fake.GetPrivateDataStub != nil {
+ return fake.GetPrivateDataStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataCallCount() int {
+ fake.getPrivateDataMutex.RLock()
+ defer fake.getPrivateDataMutex.RUnlock()
+ return len(fake.getPrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataCalls(stub func(string, string) ([]byte, error)) {
+ fake.getPrivateDataMutex.Lock()
+ defer fake.getPrivateDataMutex.Unlock()
+ fake.GetPrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataArgsForCall(i int) (string, string) {
+ fake.getPrivateDataMutex.RLock()
+ defer fake.getPrivateDataMutex.RUnlock()
+ argsForCall := fake.getPrivateDataArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataReturns(result1 []byte, result2 error) {
+ fake.getPrivateDataMutex.Lock()
+ defer fake.getPrivateDataMutex.Unlock()
+ fake.GetPrivateDataStub = nil
+ fake.getPrivateDataReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getPrivateDataMutex.Lock()
+ defer fake.getPrivateDataMutex.Unlock()
+ fake.GetPrivateDataStub = nil
+ if fake.getPrivateDataReturnsOnCall == nil {
+ fake.getPrivateDataReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getPrivateDataReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKey(arg1 string, arg2 string, arg3 []string) (shim.StateQueryIteratorInterface, error) {
+ var arg3Copy []string
+ if arg3 != nil {
+ arg3Copy = make([]string, len(arg3))
+ copy(arg3Copy, arg3)
+ }
+ fake.getPrivateDataByPartialCompositeKeyMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataByPartialCompositeKeyReturnsOnCall[len(fake.getPrivateDataByPartialCompositeKeyArgsForCall)]
+ fake.getPrivateDataByPartialCompositeKeyArgsForCall = append(fake.getPrivateDataByPartialCompositeKeyArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 []string
+ }{arg1, arg2, arg3Copy})
+ fake.recordInvocation("GetPrivateDataByPartialCompositeKey", []interface{}{arg1, arg2, arg3Copy})
+ fake.getPrivateDataByPartialCompositeKeyMutex.Unlock()
+ if fake.GetPrivateDataByPartialCompositeKeyStub != nil {
+ return fake.GetPrivateDataByPartialCompositeKeyStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataByPartialCompositeKeyReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyCallCount() int {
+ fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.RUnlock()
+ return len(fake.getPrivateDataByPartialCompositeKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyCalls(stub func(string, string, []string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getPrivateDataByPartialCompositeKeyMutex.Lock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.Unlock()
+ fake.GetPrivateDataByPartialCompositeKeyStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyArgsForCall(i int) (string, string, []string) {
+ fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.RUnlock()
+ argsForCall := fake.getPrivateDataByPartialCompositeKeyArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataByPartialCompositeKeyMutex.Lock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.Unlock()
+ fake.GetPrivateDataByPartialCompositeKeyStub = nil
+ fake.getPrivateDataByPartialCompositeKeyReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataByPartialCompositeKeyMutex.Lock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.Unlock()
+ fake.GetPrivateDataByPartialCompositeKeyStub = nil
+ if fake.getPrivateDataByPartialCompositeKeyReturnsOnCall == nil {
+ fake.getPrivateDataByPartialCompositeKeyReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getPrivateDataByPartialCompositeKeyReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRange(arg1 string, arg2 string, arg3 string) (shim.StateQueryIteratorInterface, error) {
+ fake.getPrivateDataByRangeMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataByRangeReturnsOnCall[len(fake.getPrivateDataByRangeArgsForCall)]
+ fake.getPrivateDataByRangeArgsForCall = append(fake.getPrivateDataByRangeArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 string
+ }{arg1, arg2, arg3})
+ fake.recordInvocation("GetPrivateDataByRange", []interface{}{arg1, arg2, arg3})
+ fake.getPrivateDataByRangeMutex.Unlock()
+ if fake.GetPrivateDataByRangeStub != nil {
+ return fake.GetPrivateDataByRangeStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataByRangeReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeCallCount() int {
+ fake.getPrivateDataByRangeMutex.RLock()
+ defer fake.getPrivateDataByRangeMutex.RUnlock()
+ return len(fake.getPrivateDataByRangeArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeCalls(stub func(string, string, string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getPrivateDataByRangeMutex.Lock()
+ defer fake.getPrivateDataByRangeMutex.Unlock()
+ fake.GetPrivateDataByRangeStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeArgsForCall(i int) (string, string, string) {
+ fake.getPrivateDataByRangeMutex.RLock()
+ defer fake.getPrivateDataByRangeMutex.RUnlock()
+ argsForCall := fake.getPrivateDataByRangeArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataByRangeMutex.Lock()
+ defer fake.getPrivateDataByRangeMutex.Unlock()
+ fake.GetPrivateDataByRangeStub = nil
+ fake.getPrivateDataByRangeReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataByRangeMutex.Lock()
+ defer fake.getPrivateDataByRangeMutex.Unlock()
+ fake.GetPrivateDataByRangeStub = nil
+ if fake.getPrivateDataByRangeReturnsOnCall == nil {
+ fake.getPrivateDataByRangeReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getPrivateDataByRangeReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHash(arg1 string, arg2 string) ([]byte, error) {
+ fake.getPrivateDataHashMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataHashReturnsOnCall[len(fake.getPrivateDataHashArgsForCall)]
+ fake.getPrivateDataHashArgsForCall = append(fake.getPrivateDataHashArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetPrivateDataHash", []interface{}{arg1, arg2})
+ fake.getPrivateDataHashMutex.Unlock()
+ if fake.GetPrivateDataHashStub != nil {
+ return fake.GetPrivateDataHashStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataHashReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashCallCount() int {
+ fake.getPrivateDataHashMutex.RLock()
+ defer fake.getPrivateDataHashMutex.RUnlock()
+ return len(fake.getPrivateDataHashArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashCalls(stub func(string, string) ([]byte, error)) {
+ fake.getPrivateDataHashMutex.Lock()
+ defer fake.getPrivateDataHashMutex.Unlock()
+ fake.GetPrivateDataHashStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashArgsForCall(i int) (string, string) {
+ fake.getPrivateDataHashMutex.RLock()
+ defer fake.getPrivateDataHashMutex.RUnlock()
+ argsForCall := fake.getPrivateDataHashArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashReturns(result1 []byte, result2 error) {
+ fake.getPrivateDataHashMutex.Lock()
+ defer fake.getPrivateDataHashMutex.Unlock()
+ fake.GetPrivateDataHashStub = nil
+ fake.getPrivateDataHashReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getPrivateDataHashMutex.Lock()
+ defer fake.getPrivateDataHashMutex.Unlock()
+ fake.GetPrivateDataHashStub = nil
+ if fake.getPrivateDataHashReturnsOnCall == nil {
+ fake.getPrivateDataHashReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getPrivateDataHashReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResult(arg1 string, arg2 string) (shim.StateQueryIteratorInterface, error) {
+ fake.getPrivateDataQueryResultMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataQueryResultReturnsOnCall[len(fake.getPrivateDataQueryResultArgsForCall)]
+ fake.getPrivateDataQueryResultArgsForCall = append(fake.getPrivateDataQueryResultArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetPrivateDataQueryResult", []interface{}{arg1, arg2})
+ fake.getPrivateDataQueryResultMutex.Unlock()
+ if fake.GetPrivateDataQueryResultStub != nil {
+ return fake.GetPrivateDataQueryResultStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataQueryResultReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultCallCount() int {
+ fake.getPrivateDataQueryResultMutex.RLock()
+ defer fake.getPrivateDataQueryResultMutex.RUnlock()
+ return len(fake.getPrivateDataQueryResultArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultCalls(stub func(string, string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getPrivateDataQueryResultMutex.Lock()
+ defer fake.getPrivateDataQueryResultMutex.Unlock()
+ fake.GetPrivateDataQueryResultStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultArgsForCall(i int) (string, string) {
+ fake.getPrivateDataQueryResultMutex.RLock()
+ defer fake.getPrivateDataQueryResultMutex.RUnlock()
+ argsForCall := fake.getPrivateDataQueryResultArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataQueryResultMutex.Lock()
+ defer fake.getPrivateDataQueryResultMutex.Unlock()
+ fake.GetPrivateDataQueryResultStub = nil
+ fake.getPrivateDataQueryResultReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataQueryResultMutex.Lock()
+ defer fake.getPrivateDataQueryResultMutex.Unlock()
+ fake.GetPrivateDataQueryResultStub = nil
+ if fake.getPrivateDataQueryResultReturnsOnCall == nil {
+ fake.getPrivateDataQueryResultReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getPrivateDataQueryResultReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameter(arg1 string, arg2 string) ([]byte, error) {
+ fake.getPrivateDataValidationParameterMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataValidationParameterReturnsOnCall[len(fake.getPrivateDataValidationParameterArgsForCall)]
+ fake.getPrivateDataValidationParameterArgsForCall = append(fake.getPrivateDataValidationParameterArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetPrivateDataValidationParameter", []interface{}{arg1, arg2})
+ fake.getPrivateDataValidationParameterMutex.Unlock()
+ if fake.GetPrivateDataValidationParameterStub != nil {
+ return fake.GetPrivateDataValidationParameterStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataValidationParameterReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterCallCount() int {
+ fake.getPrivateDataValidationParameterMutex.RLock()
+ defer fake.getPrivateDataValidationParameterMutex.RUnlock()
+ return len(fake.getPrivateDataValidationParameterArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterCalls(stub func(string, string) ([]byte, error)) {
+ fake.getPrivateDataValidationParameterMutex.Lock()
+ defer fake.getPrivateDataValidationParameterMutex.Unlock()
+ fake.GetPrivateDataValidationParameterStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterArgsForCall(i int) (string, string) {
+ fake.getPrivateDataValidationParameterMutex.RLock()
+ defer fake.getPrivateDataValidationParameterMutex.RUnlock()
+ argsForCall := fake.getPrivateDataValidationParameterArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterReturns(result1 []byte, result2 error) {
+ fake.getPrivateDataValidationParameterMutex.Lock()
+ defer fake.getPrivateDataValidationParameterMutex.Unlock()
+ fake.GetPrivateDataValidationParameterStub = nil
+ fake.getPrivateDataValidationParameterReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getPrivateDataValidationParameterMutex.Lock()
+ defer fake.getPrivateDataValidationParameterMutex.Unlock()
+ fake.GetPrivateDataValidationParameterStub = nil
+ if fake.getPrivateDataValidationParameterReturnsOnCall == nil {
+ fake.getPrivateDataValidationParameterReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getPrivateDataValidationParameterReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetQueryResult(arg1 string) (shim.StateQueryIteratorInterface, error) {
+ fake.getQueryResultMutex.Lock()
+ ret, specificReturn := fake.getQueryResultReturnsOnCall[len(fake.getQueryResultArgsForCall)]
+ fake.getQueryResultArgsForCall = append(fake.getQueryResultArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetQueryResult", []interface{}{arg1})
+ fake.getQueryResultMutex.Unlock()
+ if fake.GetQueryResultStub != nil {
+ return fake.GetQueryResultStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getQueryResultReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetQueryResultCallCount() int {
+ fake.getQueryResultMutex.RLock()
+ defer fake.getQueryResultMutex.RUnlock()
+ return len(fake.getQueryResultArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetQueryResultCalls(stub func(string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getQueryResultMutex.Lock()
+ defer fake.getQueryResultMutex.Unlock()
+ fake.GetQueryResultStub = stub
+}
+
+func (fake *ChaincodeStub) GetQueryResultArgsForCall(i int) string {
+ fake.getQueryResultMutex.RLock()
+ defer fake.getQueryResultMutex.RUnlock()
+ argsForCall := fake.getQueryResultArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetQueryResultReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getQueryResultMutex.Lock()
+ defer fake.getQueryResultMutex.Unlock()
+ fake.GetQueryResultStub = nil
+ fake.getQueryResultReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetQueryResultReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getQueryResultMutex.Lock()
+ defer fake.getQueryResultMutex.Unlock()
+ fake.GetQueryResultStub = nil
+ if fake.getQueryResultReturnsOnCall == nil {
+ fake.getQueryResultReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getQueryResultReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPagination(arg1 string, arg2 int32, arg3 string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) {
+ fake.getQueryResultWithPaginationMutex.Lock()
+ ret, specificReturn := fake.getQueryResultWithPaginationReturnsOnCall[len(fake.getQueryResultWithPaginationArgsForCall)]
+ fake.getQueryResultWithPaginationArgsForCall = append(fake.getQueryResultWithPaginationArgsForCall, struct {
+ arg1 string
+ arg2 int32
+ arg3 string
+ }{arg1, arg2, arg3})
+ fake.recordInvocation("GetQueryResultWithPagination", []interface{}{arg1, arg2, arg3})
+ fake.getQueryResultWithPaginationMutex.Unlock()
+ if fake.GetQueryResultWithPaginationStub != nil {
+ return fake.GetQueryResultWithPaginationStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.getQueryResultWithPaginationReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationCallCount() int {
+ fake.getQueryResultWithPaginationMutex.RLock()
+ defer fake.getQueryResultWithPaginationMutex.RUnlock()
+ return len(fake.getQueryResultWithPaginationArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationCalls(stub func(string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) {
+ fake.getQueryResultWithPaginationMutex.Lock()
+ defer fake.getQueryResultWithPaginationMutex.Unlock()
+ fake.GetQueryResultWithPaginationStub = stub
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationArgsForCall(i int) (string, int32, string) {
+ fake.getQueryResultWithPaginationMutex.RLock()
+ defer fake.getQueryResultWithPaginationMutex.RUnlock()
+ argsForCall := fake.getQueryResultWithPaginationArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationReturns(result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getQueryResultWithPaginationMutex.Lock()
+ defer fake.getQueryResultWithPaginationMutex.Unlock()
+ fake.GetQueryResultWithPaginationStub = nil
+ fake.getQueryResultWithPaginationReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getQueryResultWithPaginationMutex.Lock()
+ defer fake.getQueryResultWithPaginationMutex.Unlock()
+ fake.GetQueryResultWithPaginationStub = nil
+ if fake.getQueryResultWithPaginationReturnsOnCall == nil {
+ fake.getQueryResultWithPaginationReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ })
+ }
+ fake.getQueryResultWithPaginationReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetSignedProposal() (*peer.SignedProposal, error) {
+ fake.getSignedProposalMutex.Lock()
+ ret, specificReturn := fake.getSignedProposalReturnsOnCall[len(fake.getSignedProposalArgsForCall)]
+ fake.getSignedProposalArgsForCall = append(fake.getSignedProposalArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetSignedProposal", []interface{}{})
+ fake.getSignedProposalMutex.Unlock()
+ if fake.GetSignedProposalStub != nil {
+ return fake.GetSignedProposalStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getSignedProposalReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetSignedProposalCallCount() int {
+ fake.getSignedProposalMutex.RLock()
+ defer fake.getSignedProposalMutex.RUnlock()
+ return len(fake.getSignedProposalArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetSignedProposalCalls(stub func() (*peer.SignedProposal, error)) {
+ fake.getSignedProposalMutex.Lock()
+ defer fake.getSignedProposalMutex.Unlock()
+ fake.GetSignedProposalStub = stub
+}
+
+func (fake *ChaincodeStub) GetSignedProposalReturns(result1 *peer.SignedProposal, result2 error) {
+ fake.getSignedProposalMutex.Lock()
+ defer fake.getSignedProposalMutex.Unlock()
+ fake.GetSignedProposalStub = nil
+ fake.getSignedProposalReturns = struct {
+ result1 *peer.SignedProposal
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetSignedProposalReturnsOnCall(i int, result1 *peer.SignedProposal, result2 error) {
+ fake.getSignedProposalMutex.Lock()
+ defer fake.getSignedProposalMutex.Unlock()
+ fake.GetSignedProposalStub = nil
+ if fake.getSignedProposalReturnsOnCall == nil {
+ fake.getSignedProposalReturnsOnCall = make(map[int]struct {
+ result1 *peer.SignedProposal
+ result2 error
+ })
+ }
+ fake.getSignedProposalReturnsOnCall[i] = struct {
+ result1 *peer.SignedProposal
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetState(arg1 string) ([]byte, error) {
+ fake.getStateMutex.Lock()
+ ret, specificReturn := fake.getStateReturnsOnCall[len(fake.getStateArgsForCall)]
+ fake.getStateArgsForCall = append(fake.getStateArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetState", []interface{}{arg1})
+ fake.getStateMutex.Unlock()
+ if fake.GetStateStub != nil {
+ return fake.GetStateStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getStateReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetStateCallCount() int {
+ fake.getStateMutex.RLock()
+ defer fake.getStateMutex.RUnlock()
+ return len(fake.getStateArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateCalls(stub func(string) ([]byte, error)) {
+ fake.getStateMutex.Lock()
+ defer fake.getStateMutex.Unlock()
+ fake.GetStateStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateArgsForCall(i int) string {
+ fake.getStateMutex.RLock()
+ defer fake.getStateMutex.RUnlock()
+ argsForCall := fake.getStateArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetStateReturns(result1 []byte, result2 error) {
+ fake.getStateMutex.Lock()
+ defer fake.getStateMutex.Unlock()
+ fake.GetStateStub = nil
+ fake.getStateReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getStateMutex.Lock()
+ defer fake.getStateMutex.Unlock()
+ fake.GetStateStub = nil
+ if fake.getStateReturnsOnCall == nil {
+ fake.getStateReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getStateReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKey(arg1 string, arg2 []string) (shim.StateQueryIteratorInterface, error) {
+ var arg2Copy []string
+ if arg2 != nil {
+ arg2Copy = make([]string, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.getStateByPartialCompositeKeyMutex.Lock()
+ ret, specificReturn := fake.getStateByPartialCompositeKeyReturnsOnCall[len(fake.getStateByPartialCompositeKeyArgsForCall)]
+ fake.getStateByPartialCompositeKeyArgsForCall = append(fake.getStateByPartialCompositeKeyArgsForCall, struct {
+ arg1 string
+ arg2 []string
+ }{arg1, arg2Copy})
+ fake.recordInvocation("GetStateByPartialCompositeKey", []interface{}{arg1, arg2Copy})
+ fake.getStateByPartialCompositeKeyMutex.Unlock()
+ if fake.GetStateByPartialCompositeKeyStub != nil {
+ return fake.GetStateByPartialCompositeKeyStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getStateByPartialCompositeKeyReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyCallCount() int {
+ fake.getStateByPartialCompositeKeyMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyMutex.RUnlock()
+ return len(fake.getStateByPartialCompositeKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyCalls(stub func(string, []string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getStateByPartialCompositeKeyMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyArgsForCall(i int) (string, []string) {
+ fake.getStateByPartialCompositeKeyMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyMutex.RUnlock()
+ argsForCall := fake.getStateByPartialCompositeKeyArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getStateByPartialCompositeKeyMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyStub = nil
+ fake.getStateByPartialCompositeKeyReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getStateByPartialCompositeKeyMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyStub = nil
+ if fake.getStateByPartialCompositeKeyReturnsOnCall == nil {
+ fake.getStateByPartialCompositeKeyReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getStateByPartialCompositeKeyReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPagination(arg1 string, arg2 []string, arg3 int32, arg4 string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) {
+ var arg2Copy []string
+ if arg2 != nil {
+ arg2Copy = make([]string, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Lock()
+ ret, specificReturn := fake.getStateByPartialCompositeKeyWithPaginationReturnsOnCall[len(fake.getStateByPartialCompositeKeyWithPaginationArgsForCall)]
+ fake.getStateByPartialCompositeKeyWithPaginationArgsForCall = append(fake.getStateByPartialCompositeKeyWithPaginationArgsForCall, struct {
+ arg1 string
+ arg2 []string
+ arg3 int32
+ arg4 string
+ }{arg1, arg2Copy, arg3, arg4})
+ fake.recordInvocation("GetStateByPartialCompositeKeyWithPagination", []interface{}{arg1, arg2Copy, arg3, arg4})
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Unlock()
+ if fake.GetStateByPartialCompositeKeyWithPaginationStub != nil {
+ return fake.GetStateByPartialCompositeKeyWithPaginationStub(arg1, arg2, arg3, arg4)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.getStateByPartialCompositeKeyWithPaginationReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationCallCount() int {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.RUnlock()
+ return len(fake.getStateByPartialCompositeKeyWithPaginationArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationCalls(stub func(string, []string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyWithPaginationStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationArgsForCall(i int) (string, []string, int32, string) {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.RUnlock()
+ argsForCall := fake.getStateByPartialCompositeKeyWithPaginationArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationReturns(result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyWithPaginationStub = nil
+ fake.getStateByPartialCompositeKeyWithPaginationReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyWithPaginationStub = nil
+ if fake.getStateByPartialCompositeKeyWithPaginationReturnsOnCall == nil {
+ fake.getStateByPartialCompositeKeyWithPaginationReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ })
+ }
+ fake.getStateByPartialCompositeKeyWithPaginationReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetStateByRange(arg1 string, arg2 string) (shim.StateQueryIteratorInterface, error) {
+ fake.getStateByRangeMutex.Lock()
+ ret, specificReturn := fake.getStateByRangeReturnsOnCall[len(fake.getStateByRangeArgsForCall)]
+ fake.getStateByRangeArgsForCall = append(fake.getStateByRangeArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetStateByRange", []interface{}{arg1, arg2})
+ fake.getStateByRangeMutex.Unlock()
+ if fake.GetStateByRangeStub != nil {
+ return fake.GetStateByRangeStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getStateByRangeReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetStateByRangeCallCount() int {
+ fake.getStateByRangeMutex.RLock()
+ defer fake.getStateByRangeMutex.RUnlock()
+ return len(fake.getStateByRangeArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateByRangeCalls(stub func(string, string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getStateByRangeMutex.Lock()
+ defer fake.getStateByRangeMutex.Unlock()
+ fake.GetStateByRangeStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateByRangeArgsForCall(i int) (string, string) {
+ fake.getStateByRangeMutex.RLock()
+ defer fake.getStateByRangeMutex.RUnlock()
+ argsForCall := fake.getStateByRangeArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetStateByRangeReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getStateByRangeMutex.Lock()
+ defer fake.getStateByRangeMutex.Unlock()
+ fake.GetStateByRangeStub = nil
+ fake.getStateByRangeReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByRangeReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getStateByRangeMutex.Lock()
+ defer fake.getStateByRangeMutex.Unlock()
+ fake.GetStateByRangeStub = nil
+ if fake.getStateByRangeReturnsOnCall == nil {
+ fake.getStateByRangeReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getStateByRangeReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPagination(arg1 string, arg2 string, arg3 int32, arg4 string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) {
+ fake.getStateByRangeWithPaginationMutex.Lock()
+ ret, specificReturn := fake.getStateByRangeWithPaginationReturnsOnCall[len(fake.getStateByRangeWithPaginationArgsForCall)]
+ fake.getStateByRangeWithPaginationArgsForCall = append(fake.getStateByRangeWithPaginationArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 int32
+ arg4 string
+ }{arg1, arg2, arg3, arg4})
+ fake.recordInvocation("GetStateByRangeWithPagination", []interface{}{arg1, arg2, arg3, arg4})
+ fake.getStateByRangeWithPaginationMutex.Unlock()
+ if fake.GetStateByRangeWithPaginationStub != nil {
+ return fake.GetStateByRangeWithPaginationStub(arg1, arg2, arg3, arg4)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.getStateByRangeWithPaginationReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationCallCount() int {
+ fake.getStateByRangeWithPaginationMutex.RLock()
+ defer fake.getStateByRangeWithPaginationMutex.RUnlock()
+ return len(fake.getStateByRangeWithPaginationArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationCalls(stub func(string, string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) {
+ fake.getStateByRangeWithPaginationMutex.Lock()
+ defer fake.getStateByRangeWithPaginationMutex.Unlock()
+ fake.GetStateByRangeWithPaginationStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationArgsForCall(i int) (string, string, int32, string) {
+ fake.getStateByRangeWithPaginationMutex.RLock()
+ defer fake.getStateByRangeWithPaginationMutex.RUnlock()
+ argsForCall := fake.getStateByRangeWithPaginationArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationReturns(result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getStateByRangeWithPaginationMutex.Lock()
+ defer fake.getStateByRangeWithPaginationMutex.Unlock()
+ fake.GetStateByRangeWithPaginationStub = nil
+ fake.getStateByRangeWithPaginationReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getStateByRangeWithPaginationMutex.Lock()
+ defer fake.getStateByRangeWithPaginationMutex.Unlock()
+ fake.GetStateByRangeWithPaginationStub = nil
+ if fake.getStateByRangeWithPaginationReturnsOnCall == nil {
+ fake.getStateByRangeWithPaginationReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ })
+ }
+ fake.getStateByRangeWithPaginationReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameter(arg1 string) ([]byte, error) {
+ fake.getStateValidationParameterMutex.Lock()
+ ret, specificReturn := fake.getStateValidationParameterReturnsOnCall[len(fake.getStateValidationParameterArgsForCall)]
+ fake.getStateValidationParameterArgsForCall = append(fake.getStateValidationParameterArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetStateValidationParameter", []interface{}{arg1})
+ fake.getStateValidationParameterMutex.Unlock()
+ if fake.GetStateValidationParameterStub != nil {
+ return fake.GetStateValidationParameterStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getStateValidationParameterReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterCallCount() int {
+ fake.getStateValidationParameterMutex.RLock()
+ defer fake.getStateValidationParameterMutex.RUnlock()
+ return len(fake.getStateValidationParameterArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterCalls(stub func(string) ([]byte, error)) {
+ fake.getStateValidationParameterMutex.Lock()
+ defer fake.getStateValidationParameterMutex.Unlock()
+ fake.GetStateValidationParameterStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterArgsForCall(i int) string {
+ fake.getStateValidationParameterMutex.RLock()
+ defer fake.getStateValidationParameterMutex.RUnlock()
+ argsForCall := fake.getStateValidationParameterArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterReturns(result1 []byte, result2 error) {
+ fake.getStateValidationParameterMutex.Lock()
+ defer fake.getStateValidationParameterMutex.Unlock()
+ fake.GetStateValidationParameterStub = nil
+ fake.getStateValidationParameterReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getStateValidationParameterMutex.Lock()
+ defer fake.getStateValidationParameterMutex.Unlock()
+ fake.GetStateValidationParameterStub = nil
+ if fake.getStateValidationParameterReturnsOnCall == nil {
+ fake.getStateValidationParameterReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getStateValidationParameterReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStringArgs() []string {
+ fake.getStringArgsMutex.Lock()
+ ret, specificReturn := fake.getStringArgsReturnsOnCall[len(fake.getStringArgsArgsForCall)]
+ fake.getStringArgsArgsForCall = append(fake.getStringArgsArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetStringArgs", []interface{}{})
+ fake.getStringArgsMutex.Unlock()
+ if fake.GetStringArgsStub != nil {
+ return fake.GetStringArgsStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getStringArgsReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetStringArgsCallCount() int {
+ fake.getStringArgsMutex.RLock()
+ defer fake.getStringArgsMutex.RUnlock()
+ return len(fake.getStringArgsArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStringArgsCalls(stub func() []string) {
+ fake.getStringArgsMutex.Lock()
+ defer fake.getStringArgsMutex.Unlock()
+ fake.GetStringArgsStub = stub
+}
+
+func (fake *ChaincodeStub) GetStringArgsReturns(result1 []string) {
+ fake.getStringArgsMutex.Lock()
+ defer fake.getStringArgsMutex.Unlock()
+ fake.GetStringArgsStub = nil
+ fake.getStringArgsReturns = struct {
+ result1 []string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetStringArgsReturnsOnCall(i int, result1 []string) {
+ fake.getStringArgsMutex.Lock()
+ defer fake.getStringArgsMutex.Unlock()
+ fake.GetStringArgsStub = nil
+ if fake.getStringArgsReturnsOnCall == nil {
+ fake.getStringArgsReturnsOnCall = make(map[int]struct {
+ result1 []string
+ })
+ }
+ fake.getStringArgsReturnsOnCall[i] = struct {
+ result1 []string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetTransient() (map[string][]byte, error) {
+ fake.getTransientMutex.Lock()
+ ret, specificReturn := fake.getTransientReturnsOnCall[len(fake.getTransientArgsForCall)]
+ fake.getTransientArgsForCall = append(fake.getTransientArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetTransient", []interface{}{})
+ fake.getTransientMutex.Unlock()
+ if fake.GetTransientStub != nil {
+ return fake.GetTransientStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getTransientReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetTransientCallCount() int {
+ fake.getTransientMutex.RLock()
+ defer fake.getTransientMutex.RUnlock()
+ return len(fake.getTransientArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetTransientCalls(stub func() (map[string][]byte, error)) {
+ fake.getTransientMutex.Lock()
+ defer fake.getTransientMutex.Unlock()
+ fake.GetTransientStub = stub
+}
+
+func (fake *ChaincodeStub) GetTransientReturns(result1 map[string][]byte, result2 error) {
+ fake.getTransientMutex.Lock()
+ defer fake.getTransientMutex.Unlock()
+ fake.GetTransientStub = nil
+ fake.getTransientReturns = struct {
+ result1 map[string][]byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetTransientReturnsOnCall(i int, result1 map[string][]byte, result2 error) {
+ fake.getTransientMutex.Lock()
+ defer fake.getTransientMutex.Unlock()
+ fake.GetTransientStub = nil
+ if fake.getTransientReturnsOnCall == nil {
+ fake.getTransientReturnsOnCall = make(map[int]struct {
+ result1 map[string][]byte
+ result2 error
+ })
+ }
+ fake.getTransientReturnsOnCall[i] = struct {
+ result1 map[string][]byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetTxID() string {
+ fake.getTxIDMutex.Lock()
+ ret, specificReturn := fake.getTxIDReturnsOnCall[len(fake.getTxIDArgsForCall)]
+ fake.getTxIDArgsForCall = append(fake.getTxIDArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetTxID", []interface{}{})
+ fake.getTxIDMutex.Unlock()
+ if fake.GetTxIDStub != nil {
+ return fake.GetTxIDStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getTxIDReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetTxIDCallCount() int {
+ fake.getTxIDMutex.RLock()
+ defer fake.getTxIDMutex.RUnlock()
+ return len(fake.getTxIDArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetTxIDCalls(stub func() string) {
+ fake.getTxIDMutex.Lock()
+ defer fake.getTxIDMutex.Unlock()
+ fake.GetTxIDStub = stub
+}
+
+func (fake *ChaincodeStub) GetTxIDReturns(result1 string) {
+ fake.getTxIDMutex.Lock()
+ defer fake.getTxIDMutex.Unlock()
+ fake.GetTxIDStub = nil
+ fake.getTxIDReturns = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetTxIDReturnsOnCall(i int, result1 string) {
+ fake.getTxIDMutex.Lock()
+ defer fake.getTxIDMutex.Unlock()
+ fake.GetTxIDStub = nil
+ if fake.getTxIDReturnsOnCall == nil {
+ fake.getTxIDReturnsOnCall = make(map[int]struct {
+ result1 string
+ })
+ }
+ fake.getTxIDReturnsOnCall[i] = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetTxTimestamp() (*timestamp.Timestamp, error) {
+ fake.getTxTimestampMutex.Lock()
+ ret, specificReturn := fake.getTxTimestampReturnsOnCall[len(fake.getTxTimestampArgsForCall)]
+ fake.getTxTimestampArgsForCall = append(fake.getTxTimestampArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetTxTimestamp", []interface{}{})
+ fake.getTxTimestampMutex.Unlock()
+ if fake.GetTxTimestampStub != nil {
+ return fake.GetTxTimestampStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getTxTimestampReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetTxTimestampCallCount() int {
+ fake.getTxTimestampMutex.RLock()
+ defer fake.getTxTimestampMutex.RUnlock()
+ return len(fake.getTxTimestampArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetTxTimestampCalls(stub func() (*timestamp.Timestamp, error)) {
+ fake.getTxTimestampMutex.Lock()
+ defer fake.getTxTimestampMutex.Unlock()
+ fake.GetTxTimestampStub = stub
+}
+
+func (fake *ChaincodeStub) GetTxTimestampReturns(result1 *timestamp.Timestamp, result2 error) {
+ fake.getTxTimestampMutex.Lock()
+ defer fake.getTxTimestampMutex.Unlock()
+ fake.GetTxTimestampStub = nil
+ fake.getTxTimestampReturns = struct {
+ result1 *timestamp.Timestamp
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetTxTimestampReturnsOnCall(i int, result1 *timestamp.Timestamp, result2 error) {
+ fake.getTxTimestampMutex.Lock()
+ defer fake.getTxTimestampMutex.Unlock()
+ fake.GetTxTimestampStub = nil
+ if fake.getTxTimestampReturnsOnCall == nil {
+ fake.getTxTimestampReturnsOnCall = make(map[int]struct {
+ result1 *timestamp.Timestamp
+ result2 error
+ })
+ }
+ fake.getTxTimestampReturnsOnCall[i] = struct {
+ result1 *timestamp.Timestamp
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) InvokeChaincode(arg1 string, arg2 [][]byte, arg3 string) peer.Response {
+ var arg2Copy [][]byte
+ if arg2 != nil {
+ arg2Copy = make([][]byte, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.invokeChaincodeMutex.Lock()
+ ret, specificReturn := fake.invokeChaincodeReturnsOnCall[len(fake.invokeChaincodeArgsForCall)]
+ fake.invokeChaincodeArgsForCall = append(fake.invokeChaincodeArgsForCall, struct {
+ arg1 string
+ arg2 [][]byte
+ arg3 string
+ }{arg1, arg2Copy, arg3})
+ fake.recordInvocation("InvokeChaincode", []interface{}{arg1, arg2Copy, arg3})
+ fake.invokeChaincodeMutex.Unlock()
+ if fake.InvokeChaincodeStub != nil {
+ return fake.InvokeChaincodeStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.invokeChaincodeReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeCallCount() int {
+ fake.invokeChaincodeMutex.RLock()
+ defer fake.invokeChaincodeMutex.RUnlock()
+ return len(fake.invokeChaincodeArgsForCall)
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeCalls(stub func(string, [][]byte, string) peer.Response) {
+ fake.invokeChaincodeMutex.Lock()
+ defer fake.invokeChaincodeMutex.Unlock()
+ fake.InvokeChaincodeStub = stub
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeArgsForCall(i int) (string, [][]byte, string) {
+ fake.invokeChaincodeMutex.RLock()
+ defer fake.invokeChaincodeMutex.RUnlock()
+ argsForCall := fake.invokeChaincodeArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeReturns(result1 peer.Response) {
+ fake.invokeChaincodeMutex.Lock()
+ defer fake.invokeChaincodeMutex.Unlock()
+ fake.InvokeChaincodeStub = nil
+ fake.invokeChaincodeReturns = struct {
+ result1 peer.Response
+ }{result1}
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeReturnsOnCall(i int, result1 peer.Response) {
+ fake.invokeChaincodeMutex.Lock()
+ defer fake.invokeChaincodeMutex.Unlock()
+ fake.InvokeChaincodeStub = nil
+ if fake.invokeChaincodeReturnsOnCall == nil {
+ fake.invokeChaincodeReturnsOnCall = make(map[int]struct {
+ result1 peer.Response
+ })
+ }
+ fake.invokeChaincodeReturnsOnCall[i] = struct {
+ result1 peer.Response
+ }{result1}
+}
+
+func (fake *ChaincodeStub) PutPrivateData(arg1 string, arg2 string, arg3 []byte) error {
+ var arg3Copy []byte
+ if arg3 != nil {
+ arg3Copy = make([]byte, len(arg3))
+ copy(arg3Copy, arg3)
+ }
+ fake.putPrivateDataMutex.Lock()
+ ret, specificReturn := fake.putPrivateDataReturnsOnCall[len(fake.putPrivateDataArgsForCall)]
+ fake.putPrivateDataArgsForCall = append(fake.putPrivateDataArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 []byte
+ }{arg1, arg2, arg3Copy})
+ fake.recordInvocation("PutPrivateData", []interface{}{arg1, arg2, arg3Copy})
+ fake.putPrivateDataMutex.Unlock()
+ if fake.PutPrivateDataStub != nil {
+ return fake.PutPrivateDataStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.putPrivateDataReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) PutPrivateDataCallCount() int {
+ fake.putPrivateDataMutex.RLock()
+ defer fake.putPrivateDataMutex.RUnlock()
+ return len(fake.putPrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) PutPrivateDataCalls(stub func(string, string, []byte) error) {
+ fake.putPrivateDataMutex.Lock()
+ defer fake.putPrivateDataMutex.Unlock()
+ fake.PutPrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) PutPrivateDataArgsForCall(i int) (string, string, []byte) {
+ fake.putPrivateDataMutex.RLock()
+ defer fake.putPrivateDataMutex.RUnlock()
+ argsForCall := fake.putPrivateDataArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) PutPrivateDataReturns(result1 error) {
+ fake.putPrivateDataMutex.Lock()
+ defer fake.putPrivateDataMutex.Unlock()
+ fake.PutPrivateDataStub = nil
+ fake.putPrivateDataReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) PutPrivateDataReturnsOnCall(i int, result1 error) {
+ fake.putPrivateDataMutex.Lock()
+ defer fake.putPrivateDataMutex.Unlock()
+ fake.PutPrivateDataStub = nil
+ if fake.putPrivateDataReturnsOnCall == nil {
+ fake.putPrivateDataReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.putPrivateDataReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) PutState(arg1 string, arg2 []byte) error {
+ var arg2Copy []byte
+ if arg2 != nil {
+ arg2Copy = make([]byte, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.putStateMutex.Lock()
+ ret, specificReturn := fake.putStateReturnsOnCall[len(fake.putStateArgsForCall)]
+ fake.putStateArgsForCall = append(fake.putStateArgsForCall, struct {
+ arg1 string
+ arg2 []byte
+ }{arg1, arg2Copy})
+ fake.recordInvocation("PutState", []interface{}{arg1, arg2Copy})
+ fake.putStateMutex.Unlock()
+ if fake.PutStateStub != nil {
+ return fake.PutStateStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.putStateReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) PutStateCallCount() int {
+ fake.putStateMutex.RLock()
+ defer fake.putStateMutex.RUnlock()
+ return len(fake.putStateArgsForCall)
+}
+
+func (fake *ChaincodeStub) PutStateCalls(stub func(string, []byte) error) {
+ fake.putStateMutex.Lock()
+ defer fake.putStateMutex.Unlock()
+ fake.PutStateStub = stub
+}
+
+func (fake *ChaincodeStub) PutStateArgsForCall(i int) (string, []byte) {
+ fake.putStateMutex.RLock()
+ defer fake.putStateMutex.RUnlock()
+ argsForCall := fake.putStateArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) PutStateReturns(result1 error) {
+ fake.putStateMutex.Lock()
+ defer fake.putStateMutex.Unlock()
+ fake.PutStateStub = nil
+ fake.putStateReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) PutStateReturnsOnCall(i int, result1 error) {
+ fake.putStateMutex.Lock()
+ defer fake.putStateMutex.Unlock()
+ fake.PutStateStub = nil
+ if fake.putStateReturnsOnCall == nil {
+ fake.putStateReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.putStateReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetEvent(arg1 string, arg2 []byte) error {
+ var arg2Copy []byte
+ if arg2 != nil {
+ arg2Copy = make([]byte, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.setEventMutex.Lock()
+ ret, specificReturn := fake.setEventReturnsOnCall[len(fake.setEventArgsForCall)]
+ fake.setEventArgsForCall = append(fake.setEventArgsForCall, struct {
+ arg1 string
+ arg2 []byte
+ }{arg1, arg2Copy})
+ fake.recordInvocation("SetEvent", []interface{}{arg1, arg2Copy})
+ fake.setEventMutex.Unlock()
+ if fake.SetEventStub != nil {
+ return fake.SetEventStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.setEventReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) SetEventCallCount() int {
+ fake.setEventMutex.RLock()
+ defer fake.setEventMutex.RUnlock()
+ return len(fake.setEventArgsForCall)
+}
+
+func (fake *ChaincodeStub) SetEventCalls(stub func(string, []byte) error) {
+ fake.setEventMutex.Lock()
+ defer fake.setEventMutex.Unlock()
+ fake.SetEventStub = stub
+}
+
+func (fake *ChaincodeStub) SetEventArgsForCall(i int) (string, []byte) {
+ fake.setEventMutex.RLock()
+ defer fake.setEventMutex.RUnlock()
+ argsForCall := fake.setEventArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) SetEventReturns(result1 error) {
+ fake.setEventMutex.Lock()
+ defer fake.setEventMutex.Unlock()
+ fake.SetEventStub = nil
+ fake.setEventReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetEventReturnsOnCall(i int, result1 error) {
+ fake.setEventMutex.Lock()
+ defer fake.setEventMutex.Unlock()
+ fake.SetEventStub = nil
+ if fake.setEventReturnsOnCall == nil {
+ fake.setEventReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.setEventReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameter(arg1 string, arg2 string, arg3 []byte) error {
+ var arg3Copy []byte
+ if arg3 != nil {
+ arg3Copy = make([]byte, len(arg3))
+ copy(arg3Copy, arg3)
+ }
+ fake.setPrivateDataValidationParameterMutex.Lock()
+ ret, specificReturn := fake.setPrivateDataValidationParameterReturnsOnCall[len(fake.setPrivateDataValidationParameterArgsForCall)]
+ fake.setPrivateDataValidationParameterArgsForCall = append(fake.setPrivateDataValidationParameterArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 []byte
+ }{arg1, arg2, arg3Copy})
+ fake.recordInvocation("SetPrivateDataValidationParameter", []interface{}{arg1, arg2, arg3Copy})
+ fake.setPrivateDataValidationParameterMutex.Unlock()
+ if fake.SetPrivateDataValidationParameterStub != nil {
+ return fake.SetPrivateDataValidationParameterStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.setPrivateDataValidationParameterReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterCallCount() int {
+ fake.setPrivateDataValidationParameterMutex.RLock()
+ defer fake.setPrivateDataValidationParameterMutex.RUnlock()
+ return len(fake.setPrivateDataValidationParameterArgsForCall)
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterCalls(stub func(string, string, []byte) error) {
+ fake.setPrivateDataValidationParameterMutex.Lock()
+ defer fake.setPrivateDataValidationParameterMutex.Unlock()
+ fake.SetPrivateDataValidationParameterStub = stub
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterArgsForCall(i int) (string, string, []byte) {
+ fake.setPrivateDataValidationParameterMutex.RLock()
+ defer fake.setPrivateDataValidationParameterMutex.RUnlock()
+ argsForCall := fake.setPrivateDataValidationParameterArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterReturns(result1 error) {
+ fake.setPrivateDataValidationParameterMutex.Lock()
+ defer fake.setPrivateDataValidationParameterMutex.Unlock()
+ fake.SetPrivateDataValidationParameterStub = nil
+ fake.setPrivateDataValidationParameterReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterReturnsOnCall(i int, result1 error) {
+ fake.setPrivateDataValidationParameterMutex.Lock()
+ defer fake.setPrivateDataValidationParameterMutex.Unlock()
+ fake.SetPrivateDataValidationParameterStub = nil
+ if fake.setPrivateDataValidationParameterReturnsOnCall == nil {
+ fake.setPrivateDataValidationParameterReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.setPrivateDataValidationParameterReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameter(arg1 string, arg2 []byte) error {
+ var arg2Copy []byte
+ if arg2 != nil {
+ arg2Copy = make([]byte, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.setStateValidationParameterMutex.Lock()
+ ret, specificReturn := fake.setStateValidationParameterReturnsOnCall[len(fake.setStateValidationParameterArgsForCall)]
+ fake.setStateValidationParameterArgsForCall = append(fake.setStateValidationParameterArgsForCall, struct {
+ arg1 string
+ arg2 []byte
+ }{arg1, arg2Copy})
+ fake.recordInvocation("SetStateValidationParameter", []interface{}{arg1, arg2Copy})
+ fake.setStateValidationParameterMutex.Unlock()
+ if fake.SetStateValidationParameterStub != nil {
+ return fake.SetStateValidationParameterStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.setStateValidationParameterReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterCallCount() int {
+ fake.setStateValidationParameterMutex.RLock()
+ defer fake.setStateValidationParameterMutex.RUnlock()
+ return len(fake.setStateValidationParameterArgsForCall)
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterCalls(stub func(string, []byte) error) {
+ fake.setStateValidationParameterMutex.Lock()
+ defer fake.setStateValidationParameterMutex.Unlock()
+ fake.SetStateValidationParameterStub = stub
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterArgsForCall(i int) (string, []byte) {
+ fake.setStateValidationParameterMutex.RLock()
+ defer fake.setStateValidationParameterMutex.RUnlock()
+ argsForCall := fake.setStateValidationParameterArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterReturns(result1 error) {
+ fake.setStateValidationParameterMutex.Lock()
+ defer fake.setStateValidationParameterMutex.Unlock()
+ fake.SetStateValidationParameterStub = nil
+ fake.setStateValidationParameterReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterReturnsOnCall(i int, result1 error) {
+ fake.setStateValidationParameterMutex.Lock()
+ defer fake.setStateValidationParameterMutex.Unlock()
+ fake.SetStateValidationParameterStub = nil
+ if fake.setStateValidationParameterReturnsOnCall == nil {
+ fake.setStateValidationParameterReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.setStateValidationParameterReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SplitCompositeKey(arg1 string) (string, []string, error) {
+ fake.splitCompositeKeyMutex.Lock()
+ ret, specificReturn := fake.splitCompositeKeyReturnsOnCall[len(fake.splitCompositeKeyArgsForCall)]
+ fake.splitCompositeKeyArgsForCall = append(fake.splitCompositeKeyArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("SplitCompositeKey", []interface{}{arg1})
+ fake.splitCompositeKeyMutex.Unlock()
+ if fake.SplitCompositeKeyStub != nil {
+ return fake.SplitCompositeKeyStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.splitCompositeKeyReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyCallCount() int {
+ fake.splitCompositeKeyMutex.RLock()
+ defer fake.splitCompositeKeyMutex.RUnlock()
+ return len(fake.splitCompositeKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyCalls(stub func(string) (string, []string, error)) {
+ fake.splitCompositeKeyMutex.Lock()
+ defer fake.splitCompositeKeyMutex.Unlock()
+ fake.SplitCompositeKeyStub = stub
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyArgsForCall(i int) string {
+ fake.splitCompositeKeyMutex.RLock()
+ defer fake.splitCompositeKeyMutex.RUnlock()
+ argsForCall := fake.splitCompositeKeyArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyReturns(result1 string, result2 []string, result3 error) {
+ fake.splitCompositeKeyMutex.Lock()
+ defer fake.splitCompositeKeyMutex.Unlock()
+ fake.SplitCompositeKeyStub = nil
+ fake.splitCompositeKeyReturns = struct {
+ result1 string
+ result2 []string
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyReturnsOnCall(i int, result1 string, result2 []string, result3 error) {
+ fake.splitCompositeKeyMutex.Lock()
+ defer fake.splitCompositeKeyMutex.Unlock()
+ fake.SplitCompositeKeyStub = nil
+ if fake.splitCompositeKeyReturnsOnCall == nil {
+ fake.splitCompositeKeyReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 []string
+ result3 error
+ })
+ }
+ fake.splitCompositeKeyReturnsOnCall[i] = struct {
+ result1 string
+ result2 []string
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) Invocations() map[string][][]interface{} {
+ fake.invocationsMutex.RLock()
+ defer fake.invocationsMutex.RUnlock()
+ fake.createCompositeKeyMutex.RLock()
+ defer fake.createCompositeKeyMutex.RUnlock()
+ fake.delPrivateDataMutex.RLock()
+ defer fake.delPrivateDataMutex.RUnlock()
+ fake.delStateMutex.RLock()
+ defer fake.delStateMutex.RUnlock()
+ fake.getArgsMutex.RLock()
+ defer fake.getArgsMutex.RUnlock()
+ fake.getArgsSliceMutex.RLock()
+ defer fake.getArgsSliceMutex.RUnlock()
+ fake.getBindingMutex.RLock()
+ defer fake.getBindingMutex.RUnlock()
+ fake.getChannelIDMutex.RLock()
+ defer fake.getChannelIDMutex.RUnlock()
+ fake.getCreatorMutex.RLock()
+ defer fake.getCreatorMutex.RUnlock()
+ fake.getDecorationsMutex.RLock()
+ defer fake.getDecorationsMutex.RUnlock()
+ fake.getFunctionAndParametersMutex.RLock()
+ defer fake.getFunctionAndParametersMutex.RUnlock()
+ fake.getHistoryForKeyMutex.RLock()
+ defer fake.getHistoryForKeyMutex.RUnlock()
+ fake.getPrivateDataMutex.RLock()
+ defer fake.getPrivateDataMutex.RUnlock()
+ fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.RUnlock()
+ fake.getPrivateDataByRangeMutex.RLock()
+ defer fake.getPrivateDataByRangeMutex.RUnlock()
+ fake.getPrivateDataHashMutex.RLock()
+ defer fake.getPrivateDataHashMutex.RUnlock()
+ fake.getPrivateDataQueryResultMutex.RLock()
+ defer fake.getPrivateDataQueryResultMutex.RUnlock()
+ fake.getPrivateDataValidationParameterMutex.RLock()
+ defer fake.getPrivateDataValidationParameterMutex.RUnlock()
+ fake.getQueryResultMutex.RLock()
+ defer fake.getQueryResultMutex.RUnlock()
+ fake.getQueryResultWithPaginationMutex.RLock()
+ defer fake.getQueryResultWithPaginationMutex.RUnlock()
+ fake.getSignedProposalMutex.RLock()
+ defer fake.getSignedProposalMutex.RUnlock()
+ fake.getStateMutex.RLock()
+ defer fake.getStateMutex.RUnlock()
+ fake.getStateByPartialCompositeKeyMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyMutex.RUnlock()
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.RUnlock()
+ fake.getStateByRangeMutex.RLock()
+ defer fake.getStateByRangeMutex.RUnlock()
+ fake.getStateByRangeWithPaginationMutex.RLock()
+ defer fake.getStateByRangeWithPaginationMutex.RUnlock()
+ fake.getStateValidationParameterMutex.RLock()
+ defer fake.getStateValidationParameterMutex.RUnlock()
+ fake.getStringArgsMutex.RLock()
+ defer fake.getStringArgsMutex.RUnlock()
+ fake.getTransientMutex.RLock()
+ defer fake.getTransientMutex.RUnlock()
+ fake.getTxIDMutex.RLock()
+ defer fake.getTxIDMutex.RUnlock()
+ fake.getTxTimestampMutex.RLock()
+ defer fake.getTxTimestampMutex.RUnlock()
+ fake.invokeChaincodeMutex.RLock()
+ defer fake.invokeChaincodeMutex.RUnlock()
+ fake.putPrivateDataMutex.RLock()
+ defer fake.putPrivateDataMutex.RUnlock()
+ fake.putStateMutex.RLock()
+ defer fake.putStateMutex.RUnlock()
+ fake.setEventMutex.RLock()
+ defer fake.setEventMutex.RUnlock()
+ fake.setPrivateDataValidationParameterMutex.RLock()
+ defer fake.setPrivateDataValidationParameterMutex.RUnlock()
+ fake.setStateValidationParameterMutex.RLock()
+ defer fake.setStateValidationParameterMutex.RUnlock()
+ fake.splitCompositeKeyMutex.RLock()
+ defer fake.splitCompositeKeyMutex.RUnlock()
+ copiedInvocations := map[string][][]interface{}{}
+ for key, value := range fake.invocations {
+ copiedInvocations[key] = value
+ }
+ return copiedInvocations
+}
+
+func (fake *ChaincodeStub) recordInvocation(key string, args []interface{}) {
+ fake.invocationsMutex.Lock()
+ defer fake.invocationsMutex.Unlock()
+ if fake.invocations == nil {
+ fake.invocations = map[string][][]interface{}{}
+ }
+ if fake.invocations[key] == nil {
+ fake.invocations[key] = [][]interface{}{}
+ }
+ fake.invocations[key] = append(fake.invocations[key], args)
+}
diff --git a/asset-transfer-basic/chaincode-go/chaincode/mocks/statequeryiterator.go b/asset-transfer-basic/chaincode-go/chaincode/mocks/statequeryiterator.go
new file mode 100644
index 0000000..27e3034
--- /dev/null
+++ b/asset-transfer-basic/chaincode-go/chaincode/mocks/statequeryiterator.go
@@ -0,0 +1,232 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package mocks
+
+import (
+ "sync"
+
+ "github.com/hyperledger/fabric-protos-go/ledger/queryresult"
+)
+
+type StateQueryIterator struct {
+ CloseStub func() error
+ closeMutex sync.RWMutex
+ closeArgsForCall []struct {
+ }
+ closeReturns struct {
+ result1 error
+ }
+ closeReturnsOnCall map[int]struct {
+ result1 error
+ }
+ HasNextStub func() bool
+ hasNextMutex sync.RWMutex
+ hasNextArgsForCall []struct {
+ }
+ hasNextReturns struct {
+ result1 bool
+ }
+ hasNextReturnsOnCall map[int]struct {
+ result1 bool
+ }
+ NextStub func() (*queryresult.KV, error)
+ nextMutex sync.RWMutex
+ nextArgsForCall []struct {
+ }
+ nextReturns struct {
+ result1 *queryresult.KV
+ result2 error
+ }
+ nextReturnsOnCall map[int]struct {
+ result1 *queryresult.KV
+ result2 error
+ }
+ invocations map[string][][]interface{}
+ invocationsMutex sync.RWMutex
+}
+
+func (fake *StateQueryIterator) Close() error {
+ fake.closeMutex.Lock()
+ ret, specificReturn := fake.closeReturnsOnCall[len(fake.closeArgsForCall)]
+ fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
+ }{})
+ fake.recordInvocation("Close", []interface{}{})
+ fake.closeMutex.Unlock()
+ if fake.CloseStub != nil {
+ return fake.CloseStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.closeReturns
+ return fakeReturns.result1
+}
+
+func (fake *StateQueryIterator) CloseCallCount() int {
+ fake.closeMutex.RLock()
+ defer fake.closeMutex.RUnlock()
+ return len(fake.closeArgsForCall)
+}
+
+func (fake *StateQueryIterator) CloseCalls(stub func() error) {
+ fake.closeMutex.Lock()
+ defer fake.closeMutex.Unlock()
+ fake.CloseStub = stub
+}
+
+func (fake *StateQueryIterator) CloseReturns(result1 error) {
+ fake.closeMutex.Lock()
+ defer fake.closeMutex.Unlock()
+ fake.CloseStub = nil
+ fake.closeReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *StateQueryIterator) CloseReturnsOnCall(i int, result1 error) {
+ fake.closeMutex.Lock()
+ defer fake.closeMutex.Unlock()
+ fake.CloseStub = nil
+ if fake.closeReturnsOnCall == nil {
+ fake.closeReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.closeReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *StateQueryIterator) HasNext() bool {
+ fake.hasNextMutex.Lock()
+ ret, specificReturn := fake.hasNextReturnsOnCall[len(fake.hasNextArgsForCall)]
+ fake.hasNextArgsForCall = append(fake.hasNextArgsForCall, struct {
+ }{})
+ fake.recordInvocation("HasNext", []interface{}{})
+ fake.hasNextMutex.Unlock()
+ if fake.HasNextStub != nil {
+ return fake.HasNextStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.hasNextReturns
+ return fakeReturns.result1
+}
+
+func (fake *StateQueryIterator) HasNextCallCount() int {
+ fake.hasNextMutex.RLock()
+ defer fake.hasNextMutex.RUnlock()
+ return len(fake.hasNextArgsForCall)
+}
+
+func (fake *StateQueryIterator) HasNextCalls(stub func() bool) {
+ fake.hasNextMutex.Lock()
+ defer fake.hasNextMutex.Unlock()
+ fake.HasNextStub = stub
+}
+
+func (fake *StateQueryIterator) HasNextReturns(result1 bool) {
+ fake.hasNextMutex.Lock()
+ defer fake.hasNextMutex.Unlock()
+ fake.HasNextStub = nil
+ fake.hasNextReturns = struct {
+ result1 bool
+ }{result1}
+}
+
+func (fake *StateQueryIterator) HasNextReturnsOnCall(i int, result1 bool) {
+ fake.hasNextMutex.Lock()
+ defer fake.hasNextMutex.Unlock()
+ fake.HasNextStub = nil
+ if fake.hasNextReturnsOnCall == nil {
+ fake.hasNextReturnsOnCall = make(map[int]struct {
+ result1 bool
+ })
+ }
+ fake.hasNextReturnsOnCall[i] = struct {
+ result1 bool
+ }{result1}
+}
+
+func (fake *StateQueryIterator) Next() (*queryresult.KV, error) {
+ fake.nextMutex.Lock()
+ ret, specificReturn := fake.nextReturnsOnCall[len(fake.nextArgsForCall)]
+ fake.nextArgsForCall = append(fake.nextArgsForCall, struct {
+ }{})
+ fake.recordInvocation("Next", []interface{}{})
+ fake.nextMutex.Unlock()
+ if fake.NextStub != nil {
+ return fake.NextStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.nextReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *StateQueryIterator) NextCallCount() int {
+ fake.nextMutex.RLock()
+ defer fake.nextMutex.RUnlock()
+ return len(fake.nextArgsForCall)
+}
+
+func (fake *StateQueryIterator) NextCalls(stub func() (*queryresult.KV, error)) {
+ fake.nextMutex.Lock()
+ defer fake.nextMutex.Unlock()
+ fake.NextStub = stub
+}
+
+func (fake *StateQueryIterator) NextReturns(result1 *queryresult.KV, result2 error) {
+ fake.nextMutex.Lock()
+ defer fake.nextMutex.Unlock()
+ fake.NextStub = nil
+ fake.nextReturns = struct {
+ result1 *queryresult.KV
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *StateQueryIterator) NextReturnsOnCall(i int, result1 *queryresult.KV, result2 error) {
+ fake.nextMutex.Lock()
+ defer fake.nextMutex.Unlock()
+ fake.NextStub = nil
+ if fake.nextReturnsOnCall == nil {
+ fake.nextReturnsOnCall = make(map[int]struct {
+ result1 *queryresult.KV
+ result2 error
+ })
+ }
+ fake.nextReturnsOnCall[i] = struct {
+ result1 *queryresult.KV
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *StateQueryIterator) Invocations() map[string][][]interface{} {
+ fake.invocationsMutex.RLock()
+ defer fake.invocationsMutex.RUnlock()
+ fake.closeMutex.RLock()
+ defer fake.closeMutex.RUnlock()
+ fake.hasNextMutex.RLock()
+ defer fake.hasNextMutex.RUnlock()
+ fake.nextMutex.RLock()
+ defer fake.nextMutex.RUnlock()
+ copiedInvocations := map[string][][]interface{}{}
+ for key, value := range fake.invocations {
+ copiedInvocations[key] = value
+ }
+ return copiedInvocations
+}
+
+func (fake *StateQueryIterator) recordInvocation(key string, args []interface{}) {
+ fake.invocationsMutex.Lock()
+ defer fake.invocationsMutex.Unlock()
+ if fake.invocations == nil {
+ fake.invocations = map[string][][]interface{}{}
+ }
+ if fake.invocations[key] == nil {
+ fake.invocations[key] = [][]interface{}{}
+ }
+ fake.invocations[key] = append(fake.invocations[key], args)
+}
diff --git a/asset-transfer-basic/chaincode-go/chaincode/mocks/transaction.go b/asset-transfer-basic/chaincode-go/chaincode/mocks/transaction.go
new file mode 100644
index 0000000..eea37db
--- /dev/null
+++ b/asset-transfer-basic/chaincode-go/chaincode/mocks/transaction.go
@@ -0,0 +1,164 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package mocks
+
+import (
+ "sync"
+
+ "github.com/hyperledger/fabric-chaincode-go/pkg/cid"
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+)
+
+type TransactionContext struct {
+ GetClientIdentityStub func() cid.ClientIdentity
+ getClientIdentityMutex sync.RWMutex
+ getClientIdentityArgsForCall []struct {
+ }
+ getClientIdentityReturns struct {
+ result1 cid.ClientIdentity
+ }
+ getClientIdentityReturnsOnCall map[int]struct {
+ result1 cid.ClientIdentity
+ }
+ GetStubStub func() shim.ChaincodeStubInterface
+ getStubMutex sync.RWMutex
+ getStubArgsForCall []struct {
+ }
+ getStubReturns struct {
+ result1 shim.ChaincodeStubInterface
+ }
+ getStubReturnsOnCall map[int]struct {
+ result1 shim.ChaincodeStubInterface
+ }
+ invocations map[string][][]interface{}
+ invocationsMutex sync.RWMutex
+}
+
+func (fake *TransactionContext) GetClientIdentity() cid.ClientIdentity {
+ fake.getClientIdentityMutex.Lock()
+ ret, specificReturn := fake.getClientIdentityReturnsOnCall[len(fake.getClientIdentityArgsForCall)]
+ fake.getClientIdentityArgsForCall = append(fake.getClientIdentityArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetClientIdentity", []interface{}{})
+ fake.getClientIdentityMutex.Unlock()
+ if fake.GetClientIdentityStub != nil {
+ return fake.GetClientIdentityStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getClientIdentityReturns
+ return fakeReturns.result1
+}
+
+func (fake *TransactionContext) GetClientIdentityCallCount() int {
+ fake.getClientIdentityMutex.RLock()
+ defer fake.getClientIdentityMutex.RUnlock()
+ return len(fake.getClientIdentityArgsForCall)
+}
+
+func (fake *TransactionContext) GetClientIdentityCalls(stub func() cid.ClientIdentity) {
+ fake.getClientIdentityMutex.Lock()
+ defer fake.getClientIdentityMutex.Unlock()
+ fake.GetClientIdentityStub = stub
+}
+
+func (fake *TransactionContext) GetClientIdentityReturns(result1 cid.ClientIdentity) {
+ fake.getClientIdentityMutex.Lock()
+ defer fake.getClientIdentityMutex.Unlock()
+ fake.GetClientIdentityStub = nil
+ fake.getClientIdentityReturns = struct {
+ result1 cid.ClientIdentity
+ }{result1}
+}
+
+func (fake *TransactionContext) GetClientIdentityReturnsOnCall(i int, result1 cid.ClientIdentity) {
+ fake.getClientIdentityMutex.Lock()
+ defer fake.getClientIdentityMutex.Unlock()
+ fake.GetClientIdentityStub = nil
+ if fake.getClientIdentityReturnsOnCall == nil {
+ fake.getClientIdentityReturnsOnCall = make(map[int]struct {
+ result1 cid.ClientIdentity
+ })
+ }
+ fake.getClientIdentityReturnsOnCall[i] = struct {
+ result1 cid.ClientIdentity
+ }{result1}
+}
+
+func (fake *TransactionContext) GetStub() shim.ChaincodeStubInterface {
+ fake.getStubMutex.Lock()
+ ret, specificReturn := fake.getStubReturnsOnCall[len(fake.getStubArgsForCall)]
+ fake.getStubArgsForCall = append(fake.getStubArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetStub", []interface{}{})
+ fake.getStubMutex.Unlock()
+ if fake.GetStubStub != nil {
+ return fake.GetStubStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getStubReturns
+ return fakeReturns.result1
+}
+
+func (fake *TransactionContext) GetStubCallCount() int {
+ fake.getStubMutex.RLock()
+ defer fake.getStubMutex.RUnlock()
+ return len(fake.getStubArgsForCall)
+}
+
+func (fake *TransactionContext) GetStubCalls(stub func() shim.ChaincodeStubInterface) {
+ fake.getStubMutex.Lock()
+ defer fake.getStubMutex.Unlock()
+ fake.GetStubStub = stub
+}
+
+func (fake *TransactionContext) GetStubReturns(result1 shim.ChaincodeStubInterface) {
+ fake.getStubMutex.Lock()
+ defer fake.getStubMutex.Unlock()
+ fake.GetStubStub = nil
+ fake.getStubReturns = struct {
+ result1 shim.ChaincodeStubInterface
+ }{result1}
+}
+
+func (fake *TransactionContext) GetStubReturnsOnCall(i int, result1 shim.ChaincodeStubInterface) {
+ fake.getStubMutex.Lock()
+ defer fake.getStubMutex.Unlock()
+ fake.GetStubStub = nil
+ if fake.getStubReturnsOnCall == nil {
+ fake.getStubReturnsOnCall = make(map[int]struct {
+ result1 shim.ChaincodeStubInterface
+ })
+ }
+ fake.getStubReturnsOnCall[i] = struct {
+ result1 shim.ChaincodeStubInterface
+ }{result1}
+}
+
+func (fake *TransactionContext) Invocations() map[string][][]interface{} {
+ fake.invocationsMutex.RLock()
+ defer fake.invocationsMutex.RUnlock()
+ fake.getClientIdentityMutex.RLock()
+ defer fake.getClientIdentityMutex.RUnlock()
+ fake.getStubMutex.RLock()
+ defer fake.getStubMutex.RUnlock()
+ copiedInvocations := map[string][][]interface{}{}
+ for key, value := range fake.invocations {
+ copiedInvocations[key] = value
+ }
+ return copiedInvocations
+}
+
+func (fake *TransactionContext) recordInvocation(key string, args []interface{}) {
+ fake.invocationsMutex.Lock()
+ defer fake.invocationsMutex.Unlock()
+ if fake.invocations == nil {
+ fake.invocations = map[string][][]interface{}{}
+ }
+ if fake.invocations[key] == nil {
+ fake.invocations[key] = [][]interface{}{}
+ }
+ fake.invocations[key] = append(fake.invocations[key], args)
+}
diff --git a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go
new file mode 100644
index 0000000..71e8dd8
--- /dev/null
+++ b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go
@@ -0,0 +1,185 @@
+package chaincode
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// SmartContract provides functions for managing an Asset
+type SmartContract struct {
+ contractapi.Contract
+}
+
+// Asset describes basic details of what makes up a simple asset
+type Asset struct {
+ ID string `json:"ID"`
+ Color string `json:"color"`
+ Size int `json:"size"`
+ Owner string `json:"owner"`
+ AppraisedValue int `json:"appraisedValue"`
+}
+
+// InitLedger adds a base set of assets to the ledger
+func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
+ assets := []Asset{
+ {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
+ {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
+ {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
+ {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
+ {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
+ {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
+ }
+
+ for _, asset := range assets {
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ err = ctx.GetStub().PutState(asset.ID, assetJSON)
+ if err != nil {
+ return fmt.Errorf("failed to put to world state. %v", err)
+ }
+ }
+
+ return nil
+}
+
+// CreateAsset issues a new asset to the world state with given details.
+func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
+ exists, err := s.AssetExists(ctx, id)
+ if err != nil {
+ return err
+ }
+ if exists {
+ return fmt.Errorf("the asset %s already exists", id)
+ }
+
+ asset := Asset{
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ }
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// ReadAsset returns the asset stored in the world state with given id.
+func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
+ assetJSON, err := ctx.GetStub().GetState(id)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read from world state: %v", err)
+ }
+ if assetJSON == nil {
+ return nil, fmt.Errorf("the asset %s does not exist", id)
+ }
+
+ var asset Asset
+ err = json.Unmarshal(assetJSON, &asset)
+ if err != nil {
+ return nil, err
+ }
+
+ return &asset, nil
+}
+
+// UpdateAsset updates an existing asset in the world state with provided parameters.
+func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
+ exists, err := s.AssetExists(ctx, id)
+ if err != nil {
+ return err
+ }
+ if !exists {
+ return fmt.Errorf("the asset %s does not exist", id)
+ }
+
+ // overwriting original asset with new asset
+ asset := Asset{
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ }
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// DeleteAsset deletes an given asset from the world state.
+func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
+ exists, err := s.AssetExists(ctx, id)
+ if err != nil {
+ return err
+ }
+ if !exists {
+ return fmt.Errorf("the asset %s does not exist", id)
+ }
+
+ return ctx.GetStub().DelState(id)
+}
+
+// AssetExists returns true when asset with given ID exists in world state
+func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
+ assetJSON, err := ctx.GetStub().GetState(id)
+ if err != nil {
+ return false, fmt.Errorf("failed to read from world state: %v", err)
+ }
+
+ return assetJSON != nil, nil
+}
+
+// TransferAsset updates the owner field of asset with given id in world state.
+func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
+ asset, err := s.ReadAsset(ctx, id)
+ if err != nil {
+ return err
+ }
+
+ asset.Owner = newOwner
+ assetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(id, assetJSON)
+}
+
+// GetAllAssets returns all assets found in world state
+func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
+ // range query with empty string for startKey and endKey does an
+ // open-ended query of all assets in the chaincode namespace.
+ resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ var assets []*Asset
+ for resultsIterator.HasNext() {
+ queryResponse, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ var asset Asset
+ err = json.Unmarshal(queryResponse.Value, &asset)
+ if err != nil {
+ return nil, err
+ }
+ assets = append(assets, &asset)
+ }
+
+ return assets, nil
+}
diff --git a/asset-transfer-basic/chaincode-go/chaincode/smartcontract_test.go b/asset-transfer-basic/chaincode-go/chaincode/smartcontract_test.go
new file mode 100644
index 0000000..cb001de
--- /dev/null
+++ b/asset-transfer-basic/chaincode-go/chaincode/smartcontract_test.go
@@ -0,0 +1,184 @@
+package chaincode_test
+
+import (
+ "encoding/json"
+ "fmt"
+ "testing"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ "github.com/hyperledger/fabric-protos-go/ledger/queryresult"
+ "github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode"
+ "github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode/mocks"
+ "github.com/stretchr/testify/require"
+)
+
+//go:generate counterfeiter -o mocks/transaction.go -fake-name TransactionContext . transactionContext
+type transactionContext interface {
+ contractapi.TransactionContextInterface
+}
+
+//go:generate counterfeiter -o mocks/chaincodestub.go -fake-name ChaincodeStub . chaincodeStub
+type chaincodeStub interface {
+ shim.ChaincodeStubInterface
+}
+
+//go:generate counterfeiter -o mocks/statequeryiterator.go -fake-name StateQueryIterator . stateQueryIterator
+type stateQueryIterator interface {
+ shim.StateQueryIteratorInterface
+}
+
+func TestInitLedger(t *testing.T) {
+ chaincodeStub := &mocks.ChaincodeStub{}
+ transactionContext := &mocks.TransactionContext{}
+ transactionContext.GetStubReturns(chaincodeStub)
+
+ assetTransfer := chaincode.SmartContract{}
+ err := assetTransfer.InitLedger(transactionContext)
+ require.NoError(t, err)
+
+ chaincodeStub.PutStateReturns(fmt.Errorf("failed inserting key"))
+ err = assetTransfer.InitLedger(transactionContext)
+ require.EqualError(t, err, "failed to put to world state. failed inserting key")
+}
+
+func TestCreateAsset(t *testing.T) {
+ chaincodeStub := &mocks.ChaincodeStub{}
+ transactionContext := &mocks.TransactionContext{}
+ transactionContext.GetStubReturns(chaincodeStub)
+
+ assetTransfer := chaincode.SmartContract{}
+ err := assetTransfer.CreateAsset(transactionContext, "", "", 0, "", 0)
+ require.NoError(t, err)
+
+ chaincodeStub.GetStateReturns([]byte{}, nil)
+ err = assetTransfer.CreateAsset(transactionContext, "asset1", "", 0, "", 0)
+ require.EqualError(t, err, "the asset asset1 already exists")
+
+ chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve asset"))
+ err = assetTransfer.CreateAsset(transactionContext, "asset1", "", 0, "", 0)
+ require.EqualError(t, err, "failed to read from world state: unable to retrieve asset")
+}
+
+func TestReadAsset(t *testing.T) {
+ chaincodeStub := &mocks.ChaincodeStub{}
+ transactionContext := &mocks.TransactionContext{}
+ transactionContext.GetStubReturns(chaincodeStub)
+
+ expectedAsset := &chaincode.Asset{ID: "asset1"}
+ bytes, err := json.Marshal(expectedAsset)
+ require.NoError(t, err)
+
+ chaincodeStub.GetStateReturns(bytes, nil)
+ assetTransfer := chaincode.SmartContract{}
+ asset, err := assetTransfer.ReadAsset(transactionContext, "")
+ require.NoError(t, err)
+ require.Equal(t, expectedAsset, asset)
+
+ chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve asset"))
+ _, err = assetTransfer.ReadAsset(transactionContext, "")
+ require.EqualError(t, err, "failed to read from world state: unable to retrieve asset")
+
+ chaincodeStub.GetStateReturns(nil, nil)
+ asset, err = assetTransfer.ReadAsset(transactionContext, "asset1")
+ require.EqualError(t, err, "the asset asset1 does not exist")
+ require.Nil(t, asset)
+}
+
+func TestUpdateAsset(t *testing.T) {
+ chaincodeStub := &mocks.ChaincodeStub{}
+ transactionContext := &mocks.TransactionContext{}
+ transactionContext.GetStubReturns(chaincodeStub)
+
+ expectedAsset := &chaincode.Asset{ID: "asset1"}
+ bytes, err := json.Marshal(expectedAsset)
+ require.NoError(t, err)
+
+ chaincodeStub.GetStateReturns(bytes, nil)
+ assetTransfer := chaincode.SmartContract{}
+ err = assetTransfer.UpdateAsset(transactionContext, "", "", 0, "", 0)
+ require.NoError(t, err)
+
+ chaincodeStub.GetStateReturns(nil, nil)
+ err = assetTransfer.UpdateAsset(transactionContext, "asset1", "", 0, "", 0)
+ require.EqualError(t, err, "the asset asset1 does not exist")
+
+ chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve asset"))
+ err = assetTransfer.UpdateAsset(transactionContext, "asset1", "", 0, "", 0)
+ require.EqualError(t, err, "failed to read from world state: unable to retrieve asset")
+}
+
+func TestDeleteAsset(t *testing.T) {
+ chaincodeStub := &mocks.ChaincodeStub{}
+ transactionContext := &mocks.TransactionContext{}
+ transactionContext.GetStubReturns(chaincodeStub)
+
+ asset := &chaincode.Asset{ID: "asset1"}
+ bytes, err := json.Marshal(asset)
+ require.NoError(t, err)
+
+ chaincodeStub.GetStateReturns(bytes, nil)
+ chaincodeStub.DelStateReturns(nil)
+ assetTransfer := chaincode.SmartContract{}
+ err = assetTransfer.DeleteAsset(transactionContext, "")
+ require.NoError(t, err)
+
+ chaincodeStub.GetStateReturns(nil, nil)
+ err = assetTransfer.DeleteAsset(transactionContext, "asset1")
+ require.EqualError(t, err, "the asset asset1 does not exist")
+
+ chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve asset"))
+ err = assetTransfer.DeleteAsset(transactionContext, "")
+ require.EqualError(t, err, "failed to read from world state: unable to retrieve asset")
+}
+
+func TestTransferAsset(t *testing.T) {
+ chaincodeStub := &mocks.ChaincodeStub{}
+ transactionContext := &mocks.TransactionContext{}
+ transactionContext.GetStubReturns(chaincodeStub)
+
+ asset := &chaincode.Asset{ID: "asset1"}
+ bytes, err := json.Marshal(asset)
+ require.NoError(t, err)
+
+ chaincodeStub.GetStateReturns(bytes, nil)
+ assetTransfer := chaincode.SmartContract{}
+ err = assetTransfer.TransferAsset(transactionContext, "", "")
+ require.NoError(t, err)
+
+ chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve asset"))
+ err = assetTransfer.TransferAsset(transactionContext, "", "")
+ require.EqualError(t, err, "failed to read from world state: unable to retrieve asset")
+}
+
+func TestGetAllAssets(t *testing.T) {
+ asset := &chaincode.Asset{ID: "asset1"}
+ bytes, err := json.Marshal(asset)
+ require.NoError(t, err)
+
+ iterator := &mocks.StateQueryIterator{}
+ iterator.HasNextReturnsOnCall(0, true)
+ iterator.HasNextReturnsOnCall(1, false)
+ iterator.NextReturns(&queryresult.KV{Value: bytes}, nil)
+
+ chaincodeStub := &mocks.ChaincodeStub{}
+ transactionContext := &mocks.TransactionContext{}
+ transactionContext.GetStubReturns(chaincodeStub)
+
+ chaincodeStub.GetStateByRangeReturns(iterator, nil)
+ assetTransfer := &chaincode.SmartContract{}
+ assets, err := assetTransfer.GetAllAssets(transactionContext)
+ require.NoError(t, err)
+ require.Equal(t, []*chaincode.Asset{asset}, assets)
+
+ iterator.HasNextReturns(true)
+ iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item"))
+ assets, err = assetTransfer.GetAllAssets(transactionContext)
+ require.EqualError(t, err, "failed retrieving next item")
+ require.Nil(t, assets)
+
+ chaincodeStub.GetStateByRangeReturns(nil, fmt.Errorf("failed retrieving all assets"))
+ assets, err = assetTransfer.GetAllAssets(transactionContext)
+ require.EqualError(t, err, "failed retrieving all assets")
+ require.Nil(t, assets)
+}
diff --git a/asset-transfer-basic/chaincode-go/go.mod b/asset-transfer-basic/chaincode-go/go.mod
new file mode 100644
index 0000000..a39adb0
--- /dev/null
+++ b/asset-transfer-basic/chaincode-go/go.mod
@@ -0,0 +1,11 @@
+module github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go
+
+go 1.14
+
+require (
+ github.com/golang/protobuf v1.3.2
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212
+ github.com/hyperledger/fabric-contract-api-go v1.1.0
+ github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e
+ github.com/stretchr/testify v1.5.1
+)
diff --git a/asset-transfer-basic/chaincode-go/go.sum b/asset-transfer-basic/chaincode-go/go.sum
new file mode 100644
index 0000000..a159a45
--- /dev/null
+++ b/asset-transfer-basic/chaincode-go/go.sum
@@ -0,0 +1,146 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/asset-transfer-basic/chaincode-java/.gitattributes b/asset-transfer-basic/chaincode-java/.gitattributes
new file mode 100644
index 0000000..00a51af
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat text eol=crlf
+
diff --git a/asset-transfer-basic/chaincode-java/build.gradle b/asset-transfer-basic/chaincode-java/build.gradle
new file mode 100644
index 0000000..5f90c5a
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/build.gradle
@@ -0,0 +1,75 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+plugins {
+ id 'application'
+ id 'checkstyle'
+ id 'jacoco'
+}
+
+group 'org.hyperledger.fabric.samples'
+version '1.0-SNAPSHOT'
+
+dependencies {
+
+ compileOnly 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+ implementation 'com.owlike:genson:1.5'
+ testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
+ testImplementation 'org.assertj:assertj-core:3.11.1'
+ testImplementation 'org.mockito:mockito-core:2.+'
+}
+
+repositories {
+ maven {
+ url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
+ }
+ jcenter()
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+application {
+ mainClass = 'org.hyperledger.fabric.contract.ContractRouter'
+}
+
+checkstyle {
+ toolVersion '8.21'
+ configFile file("config/checkstyle/checkstyle.xml")
+}
+
+checkstyleMain {
+ source ='src/main/java'
+}
+
+checkstyleTest {
+ source ='src/test/java'
+}
+
+jacocoTestReport {
+ dependsOn test
+}
+
+jacocoTestCoverageVerification {
+ violationRules {
+ rule {
+ limit {
+ minimum = 0.9
+ }
+ }
+ }
+
+ finalizedBy jacocoTestReport
+}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
+
+check.dependsOn jacocoTestCoverageVerification
+installDist.dependsOn check
diff --git a/asset-transfer-basic/chaincode-java/config/checkstyle/checkstyle.xml b/asset-transfer-basic/chaincode-java/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..acd5df4
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/config/checkstyle/checkstyle.xml
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/asset-transfer-basic/chaincode-java/config/checkstyle/suppressions.xml b/asset-transfer-basic/chaincode-java/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000..8c44b0a
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/config/checkstyle/suppressions.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
Binary files /dev/null and b/asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bb8b2fc
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/asset-transfer-basic/chaincode-java/gradlew b/asset-transfer-basic/chaincode-java/gradlew
new file mode 100755
index 0000000..83f2acf
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/asset-transfer-basic/chaincode-java/gradlew.bat b/asset-transfer-basic/chaincode-java/gradlew.bat
new file mode 100644
index 0000000..9618d8d
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/asset-transfer-basic/chaincode-java/settings.gradle b/asset-transfer-basic/chaincode-java/settings.gradle
new file mode 100644
index 0000000..2633c4b
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/settings.gradle
@@ -0,0 +1,5 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+rootProject.name = 'basic'
diff --git a/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/Asset.java b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/Asset.java
new file mode 100644
index 0000000..803f22f
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/Asset.java
@@ -0,0 +1,93 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.assettransfer;
+
+import java.util.Objects;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+
+import com.owlike.genson.annotation.JsonProperty;
+
+@DataType()
+public final class Asset {
+
+ @Property()
+ private final String assetID;
+
+ @Property()
+ private final String color;
+
+ @Property()
+ private final int size;
+
+ @Property()
+ private final String owner;
+
+ @Property()
+ private final int appraisedValue;
+
+ public String getAssetID() {
+ return assetID;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public int getAppraisedValue() {
+ return appraisedValue;
+ }
+
+ public Asset(@JsonProperty("assetID") final String assetID, @JsonProperty("color") final String color,
+ @JsonProperty("size") final int size, @JsonProperty("owner") final String owner,
+ @JsonProperty("appraisedValue") final int appraisedValue) {
+ this.assetID = assetID;
+ this.color = color;
+ this.size = size;
+ this.owner = owner;
+ this.appraisedValue = appraisedValue;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if ((obj == null) || (getClass() != obj.getClass())) {
+ return false;
+ }
+
+ Asset other = (Asset) obj;
+
+ return Objects.deepEquals(
+ new String[] {getAssetID(), getColor(), getOwner()},
+ new String[] {other.getAssetID(), other.getColor(), other.getOwner()})
+ &&
+ Objects.deepEquals(
+ new int[] {getSize(), getAppraisedValue()},
+ new int[] {other.getSize(), other.getAppraisedValue()});
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getAssetID(), getColor(), getSize(), getOwner(), getAppraisedValue());
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + " [assetID=" + assetID + ", color="
+ + color + ", size=" + size + ", owner=" + owner + ", appraisedValue=" + appraisedValue + "]";
+ }
+}
diff --git a/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java
new file mode 100644
index 0000000..465d469
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java
@@ -0,0 +1,236 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.assettransfer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.contract.ContractInterface;
+import org.hyperledger.fabric.contract.annotation.Contact;
+import org.hyperledger.fabric.contract.annotation.Contract;
+import org.hyperledger.fabric.contract.annotation.Default;
+import org.hyperledger.fabric.contract.annotation.Info;
+import org.hyperledger.fabric.contract.annotation.License;
+import org.hyperledger.fabric.contract.annotation.Transaction;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ledger.KeyValue;
+import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
+
+import com.owlike.genson.Genson;
+
+@Contract(
+ name = "basic",
+ info = @Info(
+ title = "Asset Transfer",
+ description = "The hyperlegendary asset transfer",
+ version = "0.0.1-SNAPSHOT",
+ license = @License(
+ name = "Apache 2.0 License",
+ url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
+ contact = @Contact(
+ email = "a.transfer@example.com",
+ name = "Adrian Transfer",
+ url = "https://hyperledger.example.com")))
+@Default
+public final class AssetTransfer implements ContractInterface {
+
+ private final Genson genson = new Genson();
+
+ private enum AssetTransferErrors {
+ ASSET_NOT_FOUND,
+ ASSET_ALREADY_EXISTS
+ }
+
+ /**
+ * Creates some initial assets on the ledger.
+ *
+ * @param ctx the transaction context
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public void InitLedger(final Context ctx) {
+ ChaincodeStub stub = ctx.getStub();
+
+ CreateAsset(ctx, "asset1", "blue", 5, "Tomoko", 300);
+ CreateAsset(ctx, "asset2", "red", 5, "Brad", 400);
+ CreateAsset(ctx, "asset3", "green", 10, "Jin Soo", 500);
+ CreateAsset(ctx, "asset4", "yellow", 10, "Max", 600);
+ CreateAsset(ctx, "asset5", "black", 15, "Adrian", 700);
+ CreateAsset(ctx, "asset6", "white", 15, "Michel", 700);
+
+ }
+
+ /**
+ * Creates a new asset on the ledger.
+ *
+ * @param ctx the transaction context
+ * @param assetID the ID of the new asset
+ * @param color the color of the new asset
+ * @param size the size for the new asset
+ * @param owner the owner of the new asset
+ * @param appraisedValue the appraisedValue of the new asset
+ * @return the created asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size,
+ final String owner, final int appraisedValue) {
+ ChaincodeStub stub = ctx.getStub();
+
+ if (AssetExists(ctx, assetID)) {
+ String errorMessage = String.format("Asset %s already exists", assetID);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_ALREADY_EXISTS.toString());
+ }
+
+ Asset asset = new Asset(assetID, color, size, owner, appraisedValue);
+ String assetJSON = genson.serialize(asset);
+ stub.putStringState(assetID, assetJSON);
+
+ return asset;
+ }
+
+ /**
+ * Retrieves an asset with the specified ID from the ledger.
+ *
+ * @param ctx the transaction context
+ * @param assetID the ID of the asset
+ * @return the asset found on the ledger if there was one
+ */
+ @Transaction(intent = Transaction.TYPE.EVALUATE)
+ public Asset ReadAsset(final Context ctx, final String assetID) {
+ ChaincodeStub stub = ctx.getStub();
+ String assetJSON = stub.getStringState(assetID);
+
+ if (assetJSON == null || assetJSON.isEmpty()) {
+ String errorMessage = String.format("Asset %s does not exist", assetID);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+
+ Asset asset = genson.deserialize(assetJSON, Asset.class);
+ return asset;
+ }
+
+ /**
+ * Updates the properties of an asset on the ledger.
+ *
+ * @param ctx the transaction context
+ * @param assetID the ID of the asset being updated
+ * @param color the color of the asset being updated
+ * @param size the size of the asset being updated
+ * @param owner the owner of the asset being updated
+ * @param appraisedValue the appraisedValue of the asset being updated
+ * @return the transferred asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size,
+ final String owner, final int appraisedValue) {
+ ChaincodeStub stub = ctx.getStub();
+
+ if (!AssetExists(ctx, assetID)) {
+ String errorMessage = String.format("Asset %s does not exist", assetID);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+
+ Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue);
+ String newAssetJSON = genson.serialize(newAsset);
+ stub.putStringState(assetID, newAssetJSON);
+
+ return newAsset;
+ }
+
+ /**
+ * Deletes asset on the ledger.
+ *
+ * @param ctx the transaction context
+ * @param assetID the ID of the asset being deleted
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public void DeleteAsset(final Context ctx, final String assetID) {
+ ChaincodeStub stub = ctx.getStub();
+
+ if (!AssetExists(ctx, assetID)) {
+ String errorMessage = String.format("Asset %s does not exist", assetID);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+
+ stub.delState(assetID);
+ }
+
+ /**
+ * Checks the existence of the asset on the ledger
+ *
+ * @param ctx the transaction context
+ * @param assetID the ID of the asset
+ * @return boolean indicating the existence of the asset
+ */
+ @Transaction(intent = Transaction.TYPE.EVALUATE)
+ public boolean AssetExists(final Context ctx, final String assetID) {
+ ChaincodeStub stub = ctx.getStub();
+ String assetJSON = stub.getStringState(assetID);
+
+ return (assetJSON != null && !assetJSON.isEmpty());
+ }
+
+ /**
+ * Changes the owner of a asset on the ledger.
+ *
+ * @param ctx the transaction context
+ * @param assetID the ID of the asset being transferred
+ * @param newOwner the new owner
+ * @return the updated asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) {
+ ChaincodeStub stub = ctx.getStub();
+ String assetJSON = stub.getStringState(assetID);
+
+ if (assetJSON == null || assetJSON.isEmpty()) {
+ String errorMessage = String.format("Asset %s does not exist", assetID);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+
+ Asset asset = genson.deserialize(assetJSON, Asset.class);
+
+ Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue());
+ String newAssetJSON = genson.serialize(newAsset);
+ stub.putStringState(assetID, newAssetJSON);
+
+ return newAsset;
+ }
+
+ /**
+ * Retrieves all assets from the ledger.
+ *
+ * @param ctx the transaction context
+ * @return array of assets found on the ledger
+ */
+ @Transaction(intent = Transaction.TYPE.EVALUATE)
+ public String GetAllAssets(final Context ctx) {
+ ChaincodeStub stub = ctx.getStub();
+
+ List queryResults = new ArrayList();
+
+ // To retrieve all assets from the ledger use getStateByRange with empty startKey & endKey.
+ // Giving empty startKey & endKey is interpreted as all the keys from beginning to end.
+ // As another example, if you use startKey = 'asset0', endKey = 'asset9' ,
+ // then getStateByRange will retrieve asset with keys between asset0 (inclusive) and asset9 (exclusive) in lexical order.
+ QueryResultsIterator results = stub.getStateByRange("", "");
+
+ for (KeyValue result: results) {
+ Asset asset = genson.deserialize(result.getStringValue(), Asset.class);
+ queryResults.add(asset);
+ System.out.println(asset.toString());
+ }
+
+ final String response = genson.serialize(queryResults);
+
+ return response;
+ }
+}
diff --git a/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTest.java b/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTest.java
new file mode 100644
index 0000000..7da94ca
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTest.java
@@ -0,0 +1,74 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.assettransfer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+public final class AssetTest {
+
+ @Nested
+ class Equality {
+
+ @Test
+ public void isReflexive() {
+ Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100);
+
+ assertThat(asset).isEqualTo(asset);
+ }
+
+ @Test
+ public void isSymmetric() {
+ Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100);
+ Asset assetB = new Asset("asset1", "Blue", 20, "Guy", 100);
+
+ assertThat(assetA).isEqualTo(assetB);
+ assertThat(assetB).isEqualTo(assetA);
+ }
+
+ @Test
+ public void isTransitive() {
+ Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100);
+ Asset assetB = new Asset("asset1", "Blue", 20, "Guy", 100);
+ Asset assetC = new Asset("asset1", "Blue", 20, "Guy", 100);
+
+ assertThat(assetA).isEqualTo(assetB);
+ assertThat(assetB).isEqualTo(assetC);
+ assertThat(assetA).isEqualTo(assetC);
+ }
+
+ @Test
+ public void handlesInequality() {
+ Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100);
+ Asset assetB = new Asset("asset2", "Red", 40, "Lady", 200);
+
+ assertThat(assetA).isNotEqualTo(assetB);
+ }
+
+ @Test
+ public void handlesOtherObjects() {
+ Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100);
+ String assetB = "not a asset";
+
+ assertThat(assetA).isNotEqualTo(assetB);
+ }
+
+ @Test
+ public void handlesNull() {
+ Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100);
+
+ assertThat(asset).isNotEqualTo(null);
+ }
+ }
+
+ @Test
+ public void toStringIdentifiesAsset() {
+ Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100);
+
+ assertThat(asset.toString()).isEqualTo("Asset@e04f6c53 [assetID=asset1, color=Blue, size=20, owner=Guy, appraisedValue=100]");
+ }
+}
diff --git a/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.java b/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.java
new file mode 100644
index 0000000..cbbb172
--- /dev/null
+++ b/asset-transfer-basic/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.java
@@ -0,0 +1,305 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.assettransfer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.ThrowableAssert.catchThrowable;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ledger.KeyValue;
+import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InOrder;
+
+public final class AssetTransferTest {
+
+ private final class MockKeyValue implements KeyValue {
+
+ private final String key;
+ private final String value;
+
+ MockKeyValue(final String key, final String value) {
+ super();
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String getKey() {
+ return this.key;
+ }
+
+ @Override
+ public String getStringValue() {
+ return this.value;
+ }
+
+ @Override
+ public byte[] getValue() {
+ return this.value.getBytes();
+ }
+
+ }
+
+ private final class MockAssetResultsIterator implements QueryResultsIterator {
+
+ private final List assetList;
+
+ MockAssetResultsIterator() {
+ super();
+
+ assetList = new ArrayList();
+
+ assetList.add(new MockKeyValue("asset1",
+ "{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }"));
+ assetList.add(new MockKeyValue("asset2",
+ "{ \"assetID\": \"asset2\", \"color\": \"red\", \"size\": 5,\"owner\": \"Brad\", \"appraisedValue\": 400 }"));
+ assetList.add(new MockKeyValue("asset3",
+ "{ \"assetID\": \"asset3\", \"color\": \"green\", \"size\": 10,\"owner\": \"Jin Soo\", \"appraisedValue\": 500 }"));
+ assetList.add(new MockKeyValue("asset4",
+ "{ \"assetID\": \"asset4\", \"color\": \"yellow\", \"size\": 10,\"owner\": \"Max\", \"appraisedValue\": 600 }"));
+ assetList.add(new MockKeyValue("asset5",
+ "{ \"assetID\": \"asset5\", \"color\": \"black\", \"size\": 15,\"owner\": \"Adrian\", \"appraisedValue\": 700 }"));
+ assetList.add(new MockKeyValue("asset6",
+ "{ \"assetID\": \"asset6\", \"color\": \"white\", \"size\": 15,\"owner\": \"Michel\", \"appraisedValue\": 800 }"));
+ }
+
+ @Override
+ public Iterator iterator() {
+ return assetList.iterator();
+ }
+
+ @Override
+ public void close() throws Exception {
+ // do nothing
+ }
+
+ }
+
+ @Test
+ public void invokeUnknownTransaction() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.unknownTransaction(ctx);
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Undefined contract method called");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo(null);
+
+ verifyZeroInteractions(ctx);
+ }
+
+ @Nested
+ class InvokeReadAssetTransaction {
+
+ @Test
+ public void whenAssetExists() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1"))
+ .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }");
+
+ Asset asset = contract.ReadAsset(ctx, "asset1");
+
+ assertThat(asset).isEqualTo(new Asset("asset1", "blue", 5, "Tomoko", 300));
+ }
+
+ @Test
+ public void whenAssetDoesNotExist() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1")).thenReturn("");
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.ReadAsset(ctx, "asset1");
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Asset asset1 does not exist");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes());
+ }
+ }
+
+ @Test
+ void invokeInitLedgerTransaction() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+
+ contract.InitLedger(ctx);
+
+ InOrder inOrder = inOrder(stub);
+ inOrder.verify(stub).putStringState("asset1", "{\"appraisedValue\":300,\"assetID\":\"asset1\",\"color\":\"blue\",\"owner\":\"Tomoko\",\"size\":5}");
+ inOrder.verify(stub).putStringState("asset2", "{\"appraisedValue\":400,\"assetID\":\"asset2\",\"color\":\"red\",\"owner\":\"Brad\",\"size\":5}");
+ inOrder.verify(stub).putStringState("asset3", "{\"appraisedValue\":500,\"assetID\":\"asset3\",\"color\":\"green\",\"owner\":\"Jin Soo\",\"size\":10}");
+ inOrder.verify(stub).putStringState("asset4", "{\"appraisedValue\":600,\"assetID\":\"asset4\",\"color\":\"yellow\",\"owner\":\"Max\",\"size\":10}");
+ inOrder.verify(stub).putStringState("asset5", "{\"appraisedValue\":700,\"assetID\":\"asset5\",\"color\":\"black\",\"owner\":\"Adrian\",\"size\":15}");
+
+ }
+
+ @Nested
+ class InvokeCreateAssetTransaction {
+
+ @Test
+ public void whenAssetExists() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1"))
+ .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }");
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.CreateAsset(ctx, "asset1", "blue", 45, "Siobhán", 60);
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Asset asset1 already exists");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_ALREADY_EXISTS".getBytes());
+ }
+
+ @Test
+ public void whenAssetDoesNotExist() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1")).thenReturn("");
+
+ Asset asset = contract.CreateAsset(ctx, "asset1", "blue", 45, "Siobhán", 60);
+
+ assertThat(asset).isEqualTo(new Asset("asset1", "blue", 45, "Siobhán", 60));
+ }
+ }
+
+ @Test
+ void invokeGetAllAssetsTransaction() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStateByRange("", "")).thenReturn(new MockAssetResultsIterator());
+
+ String assets = contract.GetAllAssets(ctx);
+
+ assertThat(assets).isEqualTo("[{\"appraisedValue\":300,\"assetID\":\"asset1\",\"color\":\"blue\",\"owner\":\"Tomoko\",\"size\":5},"
+ + "{\"appraisedValue\":400,\"assetID\":\"asset2\",\"color\":\"red\",\"owner\":\"Brad\",\"size\":5},"
+ + "{\"appraisedValue\":500,\"assetID\":\"asset3\",\"color\":\"green\",\"owner\":\"Jin Soo\",\"size\":10},"
+ + "{\"appraisedValue\":600,\"assetID\":\"asset4\",\"color\":\"yellow\",\"owner\":\"Max\",\"size\":10},"
+ + "{\"appraisedValue\":700,\"assetID\":\"asset5\",\"color\":\"black\",\"owner\":\"Adrian\",\"size\":15},"
+ + "{\"appraisedValue\":800,\"assetID\":\"asset6\",\"color\":\"white\",\"owner\":\"Michel\",\"size\":15}]");
+
+ }
+
+ @Nested
+ class TransferAssetTransaction {
+
+ @Test
+ public void whenAssetExists() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1"))
+ .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }");
+
+ Asset asset = contract.TransferAsset(ctx, "asset1", "Dr Evil");
+
+ assertThat(asset).isEqualTo(new Asset("asset1", "blue", 5, "Dr Evil", 300));
+ }
+
+ @Test
+ public void whenAssetDoesNotExist() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1")).thenReturn("");
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.TransferAsset(ctx, "asset1", "Dr Evil");
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Asset asset1 does not exist");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes());
+ }
+ }
+
+ @Nested
+ class UpdateAssetTransaction {
+
+ @Test
+ public void whenAssetExists() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1"))
+ .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 45, \"owner\": \"Arturo\", \"appraisedValue\": 60 }");
+
+ Asset asset = contract.UpdateAsset(ctx, "asset1", "pink", 45, "Arturo", 600);
+
+ assertThat(asset).isEqualTo(new Asset("asset1", "pink", 45, "Arturo", 600));
+ }
+
+ @Test
+ public void whenAssetDoesNotExist() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1")).thenReturn("");
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.TransferAsset(ctx, "asset1", "Alex");
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Asset asset1 does not exist");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes());
+ }
+ }
+
+ @Nested
+ class DeleteAssetTransaction {
+
+ @Test
+ public void whenAssetDoesNotExist() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("asset1")).thenReturn("");
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.DeleteAsset(ctx, "asset1");
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Asset asset1 does not exist");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes());
+ }
+ }
+}
diff --git a/asset-transfer-basic/chaincode-javascript/.eslintignore b/asset-transfer-basic/chaincode-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-basic/chaincode-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-basic/chaincode-javascript/.eslintrc.js b/asset-transfer-basic/chaincode-javascript/.eslintrc.js
new file mode 100644
index 0000000..555c0cf
--- /dev/null
+++ b/asset-transfer-basic/chaincode-javascript/.eslintrc.js
@@ -0,0 +1,39 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true,
+ es6: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 4],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-tabs': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed'],
+ 'no-constant-condition': ["error", { "checkLoops": false }]
+ }
+};
diff --git a/asset-transfer-basic/chaincode-javascript/.gitignore b/asset-transfer-basic/chaincode-javascript/.gitignore
new file mode 100644
index 0000000..eeace29
--- /dev/null
+++ b/asset-transfer-basic/chaincode-javascript/.gitignore
@@ -0,0 +1,15 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Report cache used by istanbul
+.nyc_output
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+package-lock.json
diff --git a/asset-transfer-basic/chaincode-javascript/index.js b/asset-transfer-basic/chaincode-javascript/index.js
new file mode 100644
index 0000000..55a0dcf
--- /dev/null
+++ b/asset-transfer-basic/chaincode-javascript/index.js
@@ -0,0 +1,12 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const assetTransfer = require('./lib/assetTransfer');
+
+module.exports.AssetTransfer = assetTransfer;
+module.exports.contracts = [assetTransfer];
diff --git a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js
new file mode 100644
index 0000000..d3a4fc4
--- /dev/null
+++ b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js
@@ -0,0 +1,153 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Contract } = require('fabric-contract-api');
+
+class AssetTransfer extends Contract {
+
+ async InitLedger(ctx) {
+ const assets = [
+ {
+ ID: 'asset1',
+ Color: 'blue',
+ Size: 5,
+ Owner: 'Tomoko',
+ AppraisedValue: 300,
+ },
+ {
+ ID: 'asset2',
+ Color: 'red',
+ Size: 5,
+ Owner: 'Brad',
+ AppraisedValue: 400,
+ },
+ {
+ ID: 'asset3',
+ Color: 'green',
+ Size: 10,
+ Owner: 'Jin Soo',
+ AppraisedValue: 500,
+ },
+ {
+ ID: 'asset4',
+ Color: 'yellow',
+ Size: 10,
+ Owner: 'Max',
+ AppraisedValue: 600,
+ },
+ {
+ ID: 'asset5',
+ Color: 'black',
+ Size: 15,
+ Owner: 'Adriana',
+ AppraisedValue: 700,
+ },
+ {
+ ID: 'asset6',
+ Color: 'white',
+ Size: 15,
+ Owner: 'Michel',
+ AppraisedValue: 800,
+ },
+ ];
+
+ for (const asset of assets) {
+ asset.docType = 'asset';
+ await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset)));
+ console.info(`Asset ${asset.ID} initialized`);
+ }
+ }
+
+ // CreateAsset issues a new asset to the world state with given details.
+ async CreateAsset(ctx, id, color, size, owner, appraisedValue) {
+ const asset = {
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ };
+ ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
+ return JSON.stringify(asset);
+ }
+
+ // ReadAsset returns the asset stored in the world state with given id.
+ async ReadAsset(ctx, id) {
+ const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state
+ if (!assetJSON || assetJSON.length === 0) {
+ throw new Error(`The asset ${id} does not exist`);
+ }
+ return assetJSON.toString();
+ }
+
+ // UpdateAsset updates an existing asset in the world state with provided parameters.
+ async UpdateAsset(ctx, id, color, size, owner, appraisedValue) {
+ const exists = await this.AssetExists(ctx, id);
+ if (!exists) {
+ throw new Error(`The asset ${id} does not exist`);
+ }
+
+ // overwriting original asset with new asset
+ const updatedAsset = {
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ };
+ return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset)));
+ }
+
+ // DeleteAsset deletes an given asset from the world state.
+ async DeleteAsset(ctx, id) {
+ const exists = await this.AssetExists(ctx, id);
+ if (!exists) {
+ throw new Error(`The asset ${id} does not exist`);
+ }
+ return ctx.stub.deleteState(id);
+ }
+
+ // AssetExists returns true when asset with given ID exists in world state.
+ async AssetExists(ctx, id) {
+ const assetJSON = await ctx.stub.getState(id);
+ return assetJSON && assetJSON.length > 0;
+ }
+
+ // TransferAsset updates the owner field of asset with given id in the world state.
+ async TransferAsset(ctx, id, newOwner) {
+ const assetString = await this.ReadAsset(ctx, id);
+ const asset = JSON.parse(assetString);
+ asset.Owner = newOwner;
+ return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
+ }
+
+ // GetAllAssets returns all assets found in the world state.
+ async GetAllAssets(ctx) {
+ const allResults = [];
+ // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace.
+ const iterator = await ctx.stub.getStateByRange('', '');
+ let result = await iterator.next();
+ while (!result.done) {
+ const strValue = Buffer.from(result.value.value.toString()).toString('utf8');
+ let record;
+ try {
+ record = JSON.parse(strValue);
+ } catch (err) {
+ console.log(err);
+ record = strValue;
+ }
+ allResults.push({ Key: result.value.key, Record: record });
+ result = await iterator.next();
+ }
+ return JSON.stringify(allResults);
+ }
+
+
+}
+
+module.exports = AssetTransfer;
diff --git a/asset-transfer-basic/chaincode-javascript/package.json b/asset-transfer-basic/chaincode-javascript/package.json
new file mode 100644
index 0000000..9cc2224
--- /dev/null
+++ b/asset-transfer-basic/chaincode-javascript/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "asset-transfer-basic",
+ "version": "1.0.0",
+ "description": "Asset-Transfer-Basic contract implemented in JavaScript",
+ "main": "index.js",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "test": "nyc mocha --recursive",
+ "start": "fabric-chaincode-node start"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ },
+ "devDependencies": {
+ "chai": "^4.1.2",
+ "eslint": "^4.19.1",
+ "mocha": "^8.0.1",
+ "nyc": "^14.1.1",
+ "sinon": "^6.0.0",
+ "sinon-chai": "^3.2.0"
+ },
+ "nyc": {
+ "exclude": [
+ "coverage/**",
+ "test/**",
+ "index.js",
+ ".eslintrc.js"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js b/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js
new file mode 100644
index 0000000..e9db032
--- /dev/null
+++ b/asset-transfer-basic/chaincode-javascript/test/assetTransfer.test.js
@@ -0,0 +1,262 @@
+'use strict';
+const sinon = require('sinon');
+const chai = require('chai');
+const sinonChai = require('sinon-chai');
+const expect = chai.expect;
+
+const { Context } = require('fabric-contract-api');
+const { ChaincodeStub } = require('fabric-shim');
+
+const AssetTransfer = require('../lib/assetTransfer.js');
+
+let assert = sinon.assert;
+chai.use(sinonChai);
+
+describe('Asset Transfer Basic Tests', () => {
+ let transactionContext, chaincodeStub, asset;
+ beforeEach(() => {
+ transactionContext = new Context();
+
+ chaincodeStub = sinon.createStubInstance(ChaincodeStub);
+ transactionContext.setChaincodeStub(chaincodeStub);
+
+ chaincodeStub.putState.callsFake((key, value) => {
+ if (!chaincodeStub.states) {
+ chaincodeStub.states = {};
+ }
+ chaincodeStub.states[key] = value;
+ });
+
+ chaincodeStub.getState.callsFake(async (key) => {
+ let ret;
+ if (chaincodeStub.states) {
+ ret = chaincodeStub.states[key];
+ }
+ return Promise.resolve(ret);
+ });
+
+ chaincodeStub.deleteState.callsFake(async (key) => {
+ if (chaincodeStub.states) {
+ delete chaincodeStub.states[key];
+ }
+ return Promise.resolve(key);
+ });
+
+ chaincodeStub.getStateByRange.callsFake(async () => {
+ function* internalGetStateByRange() {
+ if (chaincodeStub.states) {
+ // Shallow copy
+ const copied = Object.assign({}, chaincodeStub.states);
+
+ for (let key in copied) {
+ yield {value: copied[key]};
+ }
+ }
+ }
+
+ return Promise.resolve(internalGetStateByRange());
+ });
+
+ asset = {
+ ID: 'asset1',
+ Color: 'blue',
+ Size: 5,
+ Owner: 'Tomoko',
+ AppraisedValue: 300,
+ };
+ });
+
+ describe('Test InitLedger', () => {
+ it('should return error on InitLedger', async () => {
+ chaincodeStub.putState.rejects('failed inserting key');
+ let assetTransfer = new AssetTransfer();
+ try {
+ await assetTransfer.InitLedger(transactionContext);
+ assert.fail('InitLedger should have failed');
+ } catch (err) {
+ expect(err.name).to.equal('failed inserting key');
+ }
+ });
+
+ it('should return success on InitLedger', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.InitLedger(transactionContext);
+ let ret = JSON.parse((await chaincodeStub.getState('asset1')).toString());
+ expect(ret).to.eql(Object.assign({docType: 'asset'}, asset));
+ });
+ });
+
+ describe('Test CreateAsset', () => {
+ it('should return error on CreateAsset', async () => {
+ chaincodeStub.putState.rejects('failed inserting key');
+
+ let assetTransfer = new AssetTransfer();
+ try {
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+ assert.fail('CreateAsset should have failed');
+ } catch(err) {
+ expect(err.name).to.equal('failed inserting key');
+ }
+ });
+
+ it('should return success on CreateAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ let ret = JSON.parse((await chaincodeStub.getState(asset.ID)).toString());
+ expect(ret).to.eql(asset);
+ });
+ });
+
+ describe('Test ReadAsset', () => {
+ it('should return error on ReadAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ try {
+ await assetTransfer.ReadAsset(transactionContext, 'asset2');
+ assert.fail('ReadAsset should have failed');
+ } catch (err) {
+ expect(err.message).to.equal('The asset asset2 does not exist');
+ }
+ });
+
+ it('should return success on ReadAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ let ret = JSON.parse(await chaincodeStub.getState(asset.ID));
+ expect(ret).to.eql(asset);
+ });
+ });
+
+ describe('Test UpdateAsset', () => {
+ it('should return error on UpdateAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ try {
+ await assetTransfer.UpdateAsset(transactionContext, 'asset2', 'orange', 10, 'Me', 500);
+ assert.fail('UpdateAsset should have failed');
+ } catch (err) {
+ expect(err.message).to.equal('The asset asset2 does not exist');
+ }
+ });
+
+ it('should return success on UpdateAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ await assetTransfer.UpdateAsset(transactionContext, 'asset1', 'orange', 10, 'Me', 500);
+ let ret = JSON.parse(await chaincodeStub.getState(asset.ID));
+ let expected = {
+ ID: 'asset1',
+ Color: 'orange',
+ Size: 10,
+ Owner: 'Me',
+ AppraisedValue: 500
+ };
+ expect(ret).to.eql(expected);
+ });
+ });
+
+ describe('Test DeleteAsset', () => {
+ it('should return error on DeleteAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ try {
+ await assetTransfer.DeleteAsset(transactionContext, 'asset2');
+ assert.fail('DeleteAsset should have failed');
+ } catch (err) {
+ expect(err.message).to.equal('The asset asset2 does not exist');
+ }
+ });
+
+ it('should return success on DeleteAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ await assetTransfer.DeleteAsset(transactionContext, asset.ID);
+ let ret = await chaincodeStub.getState(asset.ID);
+ expect(ret).to.equal(undefined);
+ });
+ });
+
+ describe('Test TransferAsset', () => {
+ it('should return error on TransferAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ try {
+ await assetTransfer.TransferAsset(transactionContext, 'asset2', 'Me');
+ assert.fail('DeleteAsset should have failed');
+ } catch (err) {
+ expect(err.message).to.equal('The asset asset2 does not exist');
+ }
+ });
+
+ it('should return success on TransferAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ await assetTransfer.TransferAsset(transactionContext, asset.ID, 'Me');
+ let ret = JSON.parse((await chaincodeStub.getState(asset.ID)).toString());
+ expect(ret).to.eql(Object.assign({}, asset, {Owner: 'Me'}));
+ });
+ });
+
+ describe('Test GetAllAssets', () => {
+ it('should return success on GetAllAssets', async () => {
+ let assetTransfer = new AssetTransfer();
+
+ await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Robert', 100);
+ await assetTransfer.CreateAsset(transactionContext, 'asset2', 'orange', 10, 'Paul', 200);
+ await assetTransfer.CreateAsset(transactionContext, 'asset3', 'red', 15, 'Troy', 300);
+ await assetTransfer.CreateAsset(transactionContext, 'asset4', 'pink', 20, 'Van', 400);
+
+ let ret = await assetTransfer.GetAllAssets(transactionContext);
+ ret = JSON.parse(ret);
+ expect(ret.length).to.equal(4);
+
+ let expected = [
+ {Record: {ID: 'asset1', Color: 'blue', Size: 5, Owner: 'Robert', AppraisedValue: 100}},
+ {Record: {ID: 'asset2', Color: 'orange', Size: 10, Owner: 'Paul', AppraisedValue: 200}},
+ {Record: {ID: 'asset3', Color: 'red', Size: 15, Owner: 'Troy', AppraisedValue: 300}},
+ {Record: {ID: 'asset4', Color: 'pink', Size: 20, Owner: 'Van', AppraisedValue: 400}}
+ ];
+
+ expect(ret).to.eql(expected);
+ });
+
+ it('should return success on GetAllAssets for non JSON value', async () => {
+ let assetTransfer = new AssetTransfer();
+
+ chaincodeStub.putState.onFirstCall().callsFake((key, value) => {
+ if (!chaincodeStub.states) {
+ chaincodeStub.states = {};
+ }
+ chaincodeStub.states[key] = 'non-json-value';
+ });
+
+ await assetTransfer.CreateAsset(transactionContext, 'asset1', 'blue', 5, 'Robert', 100);
+ await assetTransfer.CreateAsset(transactionContext, 'asset2', 'orange', 10, 'Paul', 200);
+ await assetTransfer.CreateAsset(transactionContext, 'asset3', 'red', 15, 'Troy', 300);
+ await assetTransfer.CreateAsset(transactionContext, 'asset4', 'pink', 20, 'Van', 400);
+
+ let ret = await assetTransfer.GetAllAssets(transactionContext);
+ ret = JSON.parse(ret);
+ expect(ret.length).to.equal(4);
+
+ let expected = [
+ {Record: 'non-json-value'},
+ {Record: {ID: 'asset2', Color: 'orange', Size: 10, Owner: 'Paul', AppraisedValue: 200}},
+ {Record: {ID: 'asset3', Color: 'red', Size: 15, Owner: 'Troy', AppraisedValue: 300}},
+ {Record: {ID: 'asset4', Color: 'pink', Size: 20, Owner: 'Van', AppraisedValue: 400}}
+ ];
+
+ expect(ret).to.eql(expected);
+ });
+ });
+});
diff --git a/asset-transfer-basic/chaincode-typescript/.gitignore b/asset-transfer-basic/chaincode-typescript/.gitignore
new file mode 100644
index 0000000..79bfe1a
--- /dev/null
+++ b/asset-transfer-basic/chaincode-typescript/.gitignore
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+package-lock.json
+
+# Compiled TypeScript files
+dist
+
diff --git a/asset-transfer-basic/chaincode-typescript/package.json b/asset-transfer-basic/chaincode-typescript/package.json
new file mode 100644
index 0000000..2b681fc
--- /dev/null
+++ b/asset-transfer-basic/chaincode-typescript/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "asset-transfer-basic",
+ "version": "1.0.0",
+ "description": "Asset Transfer Basic contract implemented in TypeScript",
+ "main": "dist/index.js",
+ "typings": "dist/index.d.ts",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "tslint -c tslint.json 'src/**/*.ts'",
+ "pretest": "npm run lint",
+ "test": "nyc mocha -r ts-node/register src/**/*.spec.ts",
+ "start": "fabric-chaincode-node start",
+ "build": "tsc",
+ "build:watch": "tsc -w",
+ "prepublishOnly": "npm run build"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ },
+ "devDependencies": {
+ "@types/chai": "^4.1.7",
+ "@types/mocha": "^5.2.5",
+ "@types/node": "^10.12.10",
+ "@types/sinon": "^5.0.7",
+ "@types/sinon-chai": "^3.2.1",
+ "chai": "^4.2.0",
+ "mocha": "^5.2.0",
+ "nyc": "^14.1.1",
+ "sinon": "^7.1.1",
+ "sinon-chai": "^3.3.0",
+ "ts-node": "^7.0.1",
+ "tslint": "^5.11.0",
+ "typescript": "^3.1.6"
+ },
+ "nyc": {
+ "extension": [
+ ".ts",
+ ".tsx"
+ ],
+ "exclude": [
+ "coverage/**",
+ "dist/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/asset-transfer-basic/chaincode-typescript/src/asset.ts b/asset-transfer-basic/chaincode-typescript/src/asset.ts
new file mode 100644
index 0000000..9fd62d3
--- /dev/null
+++ b/asset-transfer-basic/chaincode-typescript/src/asset.ts
@@ -0,0 +1,26 @@
+/*
+ SPDX-License-Identifier: Apache-2.0
+*/
+
+import {Object, Property} from 'fabric-contract-api';
+
+@Object()
+export class Asset {
+ @Property()
+ public docType?: string;
+
+ @Property()
+ public ID: string;
+
+ @Property()
+ public Color: string;
+
+ @Property()
+ public Size: number;
+
+ @Property()
+ public Owner: string;
+
+ @Property()
+ public AppraisedValue: number;
+}
diff --git a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts
new file mode 100644
index 0000000..b21f12e
--- /dev/null
+++ b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts
@@ -0,0 +1,157 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {Context, Contract, Info, Returns, Transaction} from 'fabric-contract-api';
+import {Asset} from './asset';
+
+@Info({title: 'AssetTransfer', description: 'Smart contract for trading assets'})
+export class AssetTransferContract extends Contract {
+
+ @Transaction()
+ public async InitLedger(ctx: Context): Promise {
+ const assets: Asset[] = [
+ {
+ ID: 'asset1',
+ Color: 'blue',
+ Size: 5,
+ Owner: 'Tomoko',
+ AppraisedValue: 300,
+ },
+ {
+ ID: 'asset2',
+ Color: 'red',
+ Size: 5,
+ Owner: 'Brad',
+ AppraisedValue: 400,
+ },
+ {
+ ID: 'asset3',
+ Color: 'green',
+ Size: 10,
+ Owner: 'Jin Soo',
+ AppraisedValue: 500,
+ },
+ {
+ ID: 'asset4',
+ Color: 'yellow',
+ Size: 10,
+ Owner: 'Max',
+ AppraisedValue: 600,
+ },
+ {
+ ID: 'asset5',
+ Color: 'black',
+ Size: 15,
+ Owner: 'Adriana',
+ AppraisedValue: 700,
+ },
+ {
+ ID: 'asset6',
+ Color: 'white',
+ Size: 15,
+ Owner: 'Michel',
+ AppraisedValue: 800,
+ },
+ ];
+
+ for (const asset of assets) {
+ asset.docType = 'asset';
+ await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset)));
+ console.info(`Asset ${asset.ID} initialized`);
+ }
+ }
+
+ // CreateAsset issues a new asset to the world state with given details.
+ @Transaction()
+ public async CreateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number): Promise {
+ const asset = {
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ };
+ await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
+ }
+
+ // ReadAsset returns the asset stored in the world state with given id.
+ @Transaction(false)
+ public async ReadAsset(ctx: Context, id: string): Promise {
+ const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state
+ if (!assetJSON || assetJSON.length === 0) {
+ throw new Error(`The asset ${id} does not exist`);
+ }
+ return assetJSON.toString();
+ }
+
+ // UpdateAsset updates an existing asset in the world state with provided parameters.
+ @Transaction()
+ public async UpdateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number): Promise {
+ const exists = await this.AssetExists(ctx, id);
+ if (!exists) {
+ throw new Error(`The asset ${id} does not exist`);
+ }
+
+ // overwriting original asset with new asset
+ const updatedAsset = {
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ };
+ return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset)));
+ }
+
+ // DeleteAsset deletes an given asset from the world state.
+ @Transaction()
+ public async DeleteAsset(ctx: Context, id: string): Promise {
+ const exists = await this.AssetExists(ctx, id);
+ if (!exists) {
+ throw new Error(`The asset ${id} does not exist`);
+ }
+ return ctx.stub.deleteState(id);
+ }
+
+ // AssetExists returns true when asset with given ID exists in world state.
+ @Transaction(false)
+ @Returns('boolean')
+ public async AssetExists(ctx: Context, id: string): Promise {
+ const assetJSON = await ctx.stub.getState(id);
+ return assetJSON && assetJSON.length > 0;
+ }
+
+ // TransferAsset updates the owner field of asset with given id in the world state.
+ @Transaction()
+ public async TransferAsset(ctx: Context, id: string, newOwner: string): Promise {
+ const assetString = await this.ReadAsset(ctx, id);
+ const asset = JSON.parse(assetString);
+ asset.Owner = newOwner;
+ await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
+ }
+
+ // GetAllAssets returns all assets found in the world state.
+ @Transaction(false)
+ @Returns('string')
+ public async GetAllAssets(ctx: Context): Promise {
+ const allResults = [];
+ // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace.
+ const iterator = await ctx.stub.getStateByRange('', '');
+ let result = await iterator.next();
+ while (!result.done) {
+ const strValue = Buffer.from(result.value.value.toString()).toString('utf8');
+ let record;
+ try {
+ record = JSON.parse(strValue);
+ } catch (err) {
+ console.log(err);
+ record = strValue;
+ }
+ allResults.push({Key: result.value.key, Record: record});
+ result = await iterator.next();
+ }
+ return JSON.stringify(allResults);
+ }
+
+}
diff --git a/asset-transfer-basic/chaincode-typescript/src/index.ts b/asset-transfer-basic/chaincode-typescript/src/index.ts
new file mode 100644
index 0000000..cdd7b58
--- /dev/null
+++ b/asset-transfer-basic/chaincode-typescript/src/index.ts
@@ -0,0 +1,9 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {AssetTransferContract} from './assetTransfer';
+
+export {AssetTransferContract} from './assetTransfer';
+
+export const contracts: any[] = [AssetTransferContract];
diff --git a/asset-transfer-basic/chaincode-typescript/tsconfig.json b/asset-transfer-basic/chaincode-typescript/tsconfig.json
new file mode 100644
index 0000000..80d8e12
--- /dev/null
+++ b/asset-transfer-basic/chaincode-typescript/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "outDir": "dist",
+ "target": "es2017",
+ "moduleResolution": "node",
+ "module": "commonjs",
+ "declaration": true,
+ "sourceMap": true
+ },
+ "include": [
+ "./src/**/*"
+ ],
+ "exclude": [
+ "./src/**/*.spec.ts"
+ ]
+}
diff --git a/asset-transfer-basic/chaincode-typescript/tslint.json b/asset-transfer-basic/chaincode-typescript/tslint.json
new file mode 100644
index 0000000..a52c3ee
--- /dev/null
+++ b/asset-transfer-basic/chaincode-typescript/tslint.json
@@ -0,0 +1,23 @@
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint:recommended"
+ ],
+ "jsRules": {},
+ "rules": {
+ "indent": [true, "spaces", 4],
+ "linebreak-style": [true, "LF"],
+ "quotemark": [true, "single"],
+ "semicolon": [true, "always"],
+ "no-console": false,
+ "curly": true,
+ "triple-equals": true,
+ "no-string-throw": true,
+ "no-var-keyword": true,
+ "no-trailing-whitespace": true,
+ "object-literal-key-quotes": [true, "as-needed"],
+ "object-literal-sort-keys": false,
+ "max-line-length": false
+ },
+ "rulesDirectory": []
+}
diff --git a/asset-transfer-events/README.md b/asset-transfer-events/README.md
new file mode 100644
index 0000000..34e90bd
--- /dev/null
+++ b/asset-transfer-events/README.md
@@ -0,0 +1,91 @@
+# Asset Transfer Events Sample
+
+The asset transfer events sample demonstrates chaincode events send/receive
+and the receive of block events. The chaincode events are set by your
+chaincode which adds the event data to the transaction and are sent when the
+transaction is committed to the ledger. The block events are published when
+a block is committed to the ledger, containing all the transaction details
+within that block.
+
+For more information about event services on per-channel basis, visit the
+[Channel-based event service](https://hyperledger-fabric.readthedocs.io/en/latest/peer_event_services.html)
+page in the Fabric documentation.
+
+
+## About the Sample
+
+This sample includes chaincodes and application code in multiple languages.
+In a use-case similar to basic asset transfer ( see `../asset-transfer-basic` folder)
+this sample shows sending and receiving of events during create/update/delete of an asset
+and during transfer of an asset to a new owner.
+
+### Application
+The application demonstrates this, using two types of listeners in subsequent sections of `main` function:
+1. Contract Listener: listen for events in a specific Contract
+- How to register a contract listener in an application, for chaincode events
+- How to get the chaincode event name and value from the chaincode event
+- How to retrieve the transaction and block information from the chaincode event
+
+2. Block Listener: listen for block level events and parse private-data events
+- How to register a block listener for full block events
+- How to retrieve the transaction and block information from the block event
+- How to register to receive private data associated with transactions, when registering a block listener
+- How to retrieve the private data collection details from the full block event
+- This section also shows how to connect to a Gateway with listener that will not listen for commit events. This may be useful when the application does not want to wait for the peer to commit blocks and notify the application.
+
+
+Follow the comments in `application-javascript/app.js` file, and corresponding output on running this application.
+Pay attention to the sequence of
+- smart contract calls (console output like `--> Submit Transaction or --> Evaluate`)
+- the events received at application end (console output like `<-- Contract Event Received: or <-- Block Event Received`)
+
+The listener will be notified of an event asynchronously. Notice that events will
+be posted by the listener after the application code sends the transaction (or after the
+change is committed to the ledger), but during other application activity unrelated to the event.
+
+### Smart Contract
+The smart contract implements (in folder `chaincode-xyz`) following functions to support the application:
+- CreateAsset
+- ReadAsset
+- UpdateAsset
+- DeleteAsset
+- TransferAsset
+
+Note that the asset transfer implemented by the smart contract is a simplified scenario, without ownership validation, meant only to
+demonstrate the use of sending and receiving events.
+
+
+## Running the sample
+
+Like other samples, we will use the Fabric test network to deploy and run ths sample. Follow these step in order.
+- Create the test network and a channel
+```
+cd test-network
+./network.sh up createChannel -c mychannel -ca
+```
+
+- Deploy the chaincode (smart contract)
+```
+# to deploy javascript version
+./network.sh deployCC -ccs 1 -ccv 1 -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -ccl javascript -ccp ./../asset-transfer-events/chaincode-javascript/ -ccn asset-transfer-events-javascript
+
+# or to deploy java version
+./network.sh deployCC -ccs 1 -ccv 1 -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -ccl java -ccp ./../asset-transfer-events/chaincode-java/ -ccn asset-transfer-events-java
+```
+
+- Run the application
+```
+cd application-javascript
+npm install
+# ensure this line in app.js have correct chaincode deploy name
+# const chaincodeName = '...';
+node app.js
+```
+
+
+## Clean up
+When you are finished, you can bring down the test network. The command will remove all the nodes of the test network, and delete any ledger data that you created:
+
+```
+./network.sh down
+```
diff --git a/asset-transfer-events/application-javascript/.eslintignore b/asset-transfer-events/application-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-events/application-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-events/application-javascript/.eslintrc.js b/asset-transfer-events/application-javascript/.eslintrc.js
new file mode 100644
index 0000000..8422f8a
--- /dev/null
+++ b/asset-transfer-events/application-javascript/.eslintrc.js
@@ -0,0 +1,36 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+'use strict';
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: 'eslint:recommended',
+ rules: {
+ indent: ['error', 'tab'],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/asset-transfer-events/application-javascript/.gitignore b/asset-transfer-events/application-javascript/.gitignore
new file mode 100644
index 0000000..21b287f
--- /dev/null
+++ b/asset-transfer-events/application-javascript/.gitignore
@@ -0,0 +1,14 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+package-lock.json
+
+wallet
+!wallet/.gitkeep
diff --git a/asset-transfer-events/application-javascript/app.js b/asset-transfer-events/application-javascript/app.js
new file mode 100644
index 0000000..4a8a1c2
--- /dev/null
+++ b/asset-transfer-events/application-javascript/app.js
@@ -0,0 +1,545 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+/**
+ * Application that shows events when creating and updating an asset
+ * -- How to register a contract listener for chaincode events
+ * -- How to get the chaincode event name and value from the chaincode event
+ * -- How to retrieve the transaction and block information from the chaincode event
+ * -- How to register a block listener for full block events
+ * -- How to retrieve the transaction and block information from the full block event
+ * -- How to register to recieve private data associated with transactions when
+ * registering a block listener
+ * -- How to retreive the private data from the full block event
+ * -- The listener will be notified of an event at anytime. Notice that events will
+ * be posted by the listener after the application activity causing the ledger change
+ * and during other application activity unrelated to the event
+ * -- How to connect to a Gateway that will not use events when submitting transactions.
+ * This may be useful when the application does not want to wait for the peer to commit
+ * blocks and notify the application.
+ *
+ * To see the SDK workings, try setting the logging to be displayed on the console
+ * before executing this application.
+ * export HFC_LOGGING='{"debug":"console"}'
+ * See the following on how the SDK is working with the Peer's Event Services
+ * https://hyperledger-fabric.readthedocs.io/en/latest/peer_event_services.html
+ *
+ * See the following for more details on using the Node SDK
+ * https://hyperledger.github.io/fabric-sdk-node/release-2.2/module-fabric-network.html
+ */
+
+// pre-requisites:
+// - fabric-sample two organization test-network setup with two peers, ordering service,
+// and 2 certificate authorities
+// ===> from directory test-network
+// ./network.sh up createChannel -ca
+//
+// - Use the asset-transfer-events/chaincode-javascript chaincode deployed on
+// the channel "mychannel". The following deploy command will package, install,
+// approve, and commit the javascript chaincode, all the actions it takes
+// to deploy a chaincode to a channel.
+// ===> from directory test-network
+// ./network.sh deployCC -ccn events -ccp ../asset-transfer-events/chaincode-javacript/ -ccl javascript -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
+//
+// - Be sure that node.js is installed
+// ===> from directory asset-transfer-sbe/application-javascript
+// node -v
+// - npm installed code dependencies
+// ===> from directory asset-transfer-sbe/application-javascript
+// npm install
+// - to run this test application
+// ===> from directory asset-transfer-sbe/application-javascript
+// node app.js
+
+// NOTE: If you see an error like these:
+/*
+
+ Error in setup: Error: DiscoveryService: mychannel error: access denied
+
+ OR
+
+ Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
+
+ */
+// Delete the /fabric-samples/asset-transfer-sbe/application-javascript/wallet directory
+// and retry this application.
+//
+// The certificate authority must have been restarted and the saved certificates for the
+// admin and application user are not valid. Deleting the wallet store will force these to be reset
+// with the new certificate authority.
+//
+
+// use this to set logging, must be set before the require('fabric-network');
+process.env.HFC_LOGGING = '{"debug": "./debug.log"}';
+
+const { Gateway, Wallets } = require('fabric-network');
+const EventStrategies = require('fabric-network/lib/impl/event/defaulteventhandlerstrategies');
+const FabricCAServices = require('fabric-ca-client');
+const path = require('path');
+const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
+const { buildCCPOrg1, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const channelName = 'mychannel';
+const chaincodeName = 'events';
+
+const org1 = 'Org1MSP';
+const Org1UserId = 'appUser1';
+
+const RED = '\x1b[31m\n';
+const GREEN = '\x1b[32m\n';
+const BLUE = '\x1b[34m';
+const RESET = '\x1b[0m';
+
+/**
+ * Perform a sleep -- asynchronous wait
+ * @param ms the time in milliseconds to sleep for
+ */
+function sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+async function initGatewayForOrg1(useCommitEvents) {
+ console.log(`${GREEN}--> Fabric client user & Gateway init: Using Org1 identity to Org1 Peer${RESET}`);
+ // build an in memory object with the network configuration (also known as a connection profile)
+ const ccpOrg1 = buildCCPOrg1();
+
+ // build an instance of the fabric ca services client based on
+ // the information in the network configuration
+ const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
+
+ // setup the wallet to cache the credentials of the application user, on the app server locally
+ const walletPathOrg1 = path.join(__dirname, 'wallet', 'org1');
+ const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
+
+ // in a real application this would be done on an administrative flow, and only once
+ // stores admin identity in local wallet, if needed
+ await enrollAdmin(caOrg1Client, walletOrg1, org1);
+ // register & enroll application user with CA, which is used as client identify to make chaincode calls
+ // and stores app user identity in local wallet
+ // In a real application this would be done only when a new user was required to be added
+ // and would be part of an administrative flow
+ await registerAndEnrollUser(caOrg1Client, walletOrg1, org1, Org1UserId, 'org1.department1');
+
+ try {
+ // Create a new gateway for connecting to Org's peer node.
+ const gatewayOrg1 = new Gateway();
+
+ if (useCommitEvents) {
+ await gatewayOrg1.connect(ccpOrg1, {
+ wallet: walletOrg1,
+ identity: Org1UserId,
+ discovery: { enabled: true, asLocalhost: true }
+ });
+ } else {
+ await gatewayOrg1.connect(ccpOrg1, {
+ wallet: walletOrg1,
+ identity: Org1UserId,
+ discovery: { enabled: true, asLocalhost: true },
+ eventHandlerOptions: EventStrategies.NONE
+ });
+ }
+
+
+ return gatewayOrg1;
+ } catch (error) {
+ console.error(`Error in connecting to gateway for Org1: ${error}`);
+ process.exit(1);
+ }
+}
+
+function checkAsset(org, resultBuffer, color, size, owner, appraisedValue, price) {
+ console.log(`${GREEN}<-- Query results from ${org}${RESET}`);
+
+ let asset;
+ if (resultBuffer) {
+ asset = JSON.parse(resultBuffer.toString('utf8'));
+ } else {
+ console.log(`${RED}*** Failed to read asset${RESET}`);
+ }
+ console.log(`*** verify asset ${asset.ID}`);
+
+ if (asset) {
+ if (asset.Color === color) {
+ console.log(`*** asset ${asset.ID} has color ${asset.Color}`);
+ } else {
+ console.log(`${RED}*** asset ${asset.ID} has color of ${asset.Color}${RESET}`);
+ }
+ if (asset.Size === size) {
+ console.log(`*** asset ${asset.ID} has size ${asset.Size}`);
+ } else {
+ console.log(`${RED}*** Failed size check from ${org} - asset ${asset.ID} has size of ${asset.Size}${RESET}`);
+ }
+ if (asset.Owner === owner) {
+ console.log(`*** asset ${asset.ID} owned by ${asset.Owner}`);
+ } else {
+ console.log(`${RED}*** Failed owner check from ${org} - asset ${asset.ID} owned by ${asset.Owner}${RESET}`);
+ }
+ if (asset.AppraisedValue === appraisedValue) {
+ console.log(`*** asset ${asset.ID} has appraised value ${asset.AppraisedValue}`);
+ } else {
+ console.log(`${RED}*** Failed appraised value check from ${org} - asset ${asset.ID} has appraised value of ${asset.AppraisedValue}${RESET}`);
+ }
+ if (price) {
+ if (asset.asset_properties && asset.asset_properties.Price === price) {
+ console.log(`*** asset ${asset.ID} has price ${asset.asset_properties.Price}`);
+ } else {
+ console.log(`${RED}*** Failed price check from ${org} - asset ${asset.ID} has price of ${asset.asset_properties.Price}${RESET}`);
+ }
+ }
+ }
+}
+
+function showTransactionData(transactionData) {
+ const creator = transactionData.actions[0].header.creator;
+ console.log(` - submitted by: ${creator.mspid}-${creator.id_bytes.toString('hex')}`);
+ for (const endorsement of transactionData.actions[0].payload.action.endorsements) {
+ console.log(` - endorsed by: ${endorsement.endorser.mspid}-${endorsement.endorser.id_bytes.toString('hex')}`);
+ }
+ const chaincode = transactionData.actions[0].payload.chaincode_proposal_payload.input.chaincode_spec;
+ console.log(` - chaincode:${chaincode.chaincode_id.name}`);
+ console.log(` - function:${chaincode.input.args[0].toString()}`);
+ for (let x = 1; x < chaincode.input.args.length; x++) {
+ console.log(` - arg:${chaincode.input.args[x].toString()}`);
+ }
+}
+
+async function main() {
+ console.log(`${BLUE} **** START ****${RESET}`);
+ try {
+ let randomNumber = Math.floor(Math.random() * 1000) + 1;
+ // use a random key so that we can run multiple times
+ let assetKey = `item-${randomNumber}`;
+
+ /** ******* Fabric client init: Using Org1 identity to Org1 Peer ******* */
+ const gateway1Org1 = await initGatewayForOrg1(true); // transaction handling uses commit events
+ const gateway2Org1 = await initGatewayForOrg1();
+
+ try {
+ //
+ // - - - - - - C H A I N C O D E E V E N T S
+ //
+ console.log(`${BLUE} **** CHAINCODE EVENTS ****${RESET}`);
+ let transaction;
+ let listener;
+ const network1Org1 = await gateway1Org1.getNetwork(channelName);
+ const contract1Org1 = network1Org1.getContract(chaincodeName);
+
+ try {
+ // first create a listener to be notified of chaincode code events
+ // coming from the chaincode ID "events"
+ listener = async (event) => {
+ // The payload of the chaincode event is the value place there by the
+ // chaincode. Notice it is a byte data and the application will have
+ // to know how to deserialize.
+ // In this case we know that the chaincode will always place the asset
+ // being worked with as the payload for all events produced.
+ const asset = JSON.parse(event.payload.toString());
+ console.log(`${GREEN}<-- Contract Event Received: ${event.eventName} - ${JSON.stringify(asset)}${RESET}`);
+ // show the information available with the event
+ console.log(`*** Event: ${event.eventName}:${asset.ID}`);
+ // notice how we have access to the transaction information that produced this chaincode event
+ const eventTransaction = event.getTransactionEvent();
+ console.log(`*** transaction: ${eventTransaction.transactionId} status:${eventTransaction.status}`);
+ showTransactionData(eventTransaction.transactionData);
+ // notice how we have access to the full block that contains this transaction
+ const eventBlock = eventTransaction.getBlockEvent();
+ console.log(`*** block: ${eventBlock.blockNumber.toString()}`);
+ };
+ // now start the client side event service and register the listener
+ console.log(`${GREEN}--> Start contract event stream to peer in Org1${RESET}`);
+ await contract1Org1.addContractListener(listener);
+ } catch (eventError) {
+ console.log(`${RED}<-- Failed: Setup contract events - ${eventError}${RESET}`);
+ }
+
+ try {
+ // C R E A T E
+ console.log(`${GREEN}--> Submit Transaction: CreateAsset, ${assetKey} owned by Sam${RESET}`);
+ transaction = contract1Org1.createTransaction('CreateAsset');
+ await transaction.submit(assetKey, 'blue', '10', 'Sam', '100');
+ console.log(`${GREEN}<-- Submit CreateAsset Result: committed, asset ${assetKey}${RESET}`);
+ } catch (createError) {
+ console.log(`${RED}<-- Submit Failed: CreateAsset - ${createError}${RESET}`);
+ }
+ try {
+ // R E A D
+ console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should be owned by Sam${RESET}`);
+ const resultBuffer = await contract1Org1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset(org1, resultBuffer, 'blue', '10', 'Sam', '100');
+ } catch (readError) {
+ console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
+ }
+
+ try {
+ // U P D A T E
+ console.log(`${GREEN}--> Submit Transaction: UpdateAsset ${assetKey} update appraised value to 200`);
+ transaction = contract1Org1.createTransaction('UpdateAsset');
+ await transaction.submit(assetKey, 'blue', '10', 'Sam', '200');
+ console.log(`${GREEN}<-- Submit UpdateAsset Result: committed, asset ${assetKey}${RESET}`);
+ } catch (updateError) {
+ console.log(`${RED}<-- Failed: UpdateAsset - ${updateError}${RESET}`);
+ }
+ try {
+ // R E A D
+ console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now have appraised value of 200${RESET}`);
+ const resultBuffer = await contract1Org1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset(org1, resultBuffer, 'blue', '10', 'Sam', '200');
+ } catch (readError) {
+ console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
+ }
+
+ try {
+ // T R A N S F E R
+ console.log(`${GREEN}--> Submit Transaction: TransferAsset ${assetKey} to Mary`);
+ transaction = contract1Org1.createTransaction('TransferAsset');
+ await transaction.submit(assetKey, 'Mary');
+ console.log(`${GREEN}<-- Submit TransferAsset Result: committed, asset ${assetKey}${RESET}`);
+ } catch (transferError) {
+ console.log(`${RED}<-- Failed: TransferAsset - ${transferError}${RESET}`);
+ }
+ try {
+ // R E A D
+ console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now be owned by Mary${RESET}`);
+ const resultBuffer = await contract1Org1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset(org1, resultBuffer, 'blue', '10', 'Mary', '200');
+ } catch (readError) {
+ console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
+ }
+
+ try {
+ // D E L E T E
+ console.log(`${GREEN}--> Submit Transaction: DeleteAsset ${assetKey}`);
+ transaction = contract1Org1.createTransaction('DeleteAsset');
+ await transaction.submit(assetKey);
+ console.log(`${GREEN}<-- Submit DeleteAsset Result: committed, asset ${assetKey}${RESET}`);
+ } catch (deleteError) {
+ console.log(`${RED}<-- Failed: DeleteAsset - ${deleteError}${RESET}`);
+ if (deleteError.toString().includes('ENDORSEMENT_POLICY_FAILURE')) {
+ console.log(`${RED}Be sure that chaincode was deployed with the endorsement policy "OR('Org1MSP.peer','Org2MSP.peer')"${RESET}`)
+ }
+ }
+ try {
+ // R E A D
+ console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now be deleted${RESET}`);
+ const resultBuffer = await contract1Org1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset(org1, resultBuffer, 'blue', '10', 'Mary', '200');
+ console.log(`${RED}<-- Failed: ReadAsset - should not have read this asset${RESET}`);
+ } catch (readError) {
+ console.log(`${GREEN}<-- Success: ReadAsset - ${readError}${RESET}`);
+ }
+
+ // all done with this listener
+ contract1Org1.removeContractListener(listener);
+
+ //
+ // - - - - - - B L O C K E V E N T S with P R I V A T E D A T A
+ //
+ console.log(`${BLUE} **** BLOCK EVENTS with PRIVATE DATA ****${RESET}`);
+ const network2Org1 = await gateway2Org1.getNetwork(channelName);
+ const contract2Org1 = network2Org1.getContract(chaincodeName);
+
+ randomNumber = Math.floor(Math.random() * 1000) + 1;
+ assetKey = `item-${randomNumber}`;
+
+ let firstBlock = true; // simple indicator to track blocks
+
+ try {
+ let listener;
+
+ // create a block listener
+ listener = async (event) => {
+ if (firstBlock) {
+ console.log(`${GREEN}<-- Block Event Received - block number: ${event.blockNumber.toString()}` +
+ '\n### Note:' +
+ '\n This block event represents the current top block of the ledger.' +
+ `\n All block events after this one are events that represent new blocks added to the ledger${RESET}`);
+ firstBlock = false;
+ } else {
+ console.log(`${GREEN}<-- Block Event Received - block number: ${event.blockNumber.toString()}${RESET}`);
+ }
+ const transEvents = event.getTransactionEvents();
+ for (const transEvent of transEvents) {
+ console.log(`*** transaction event: ${transEvent.transactionId}`);
+ if (transEvent.privateData) {
+ for (const namespace of transEvent.privateData.ns_pvt_rwset) {
+ console.log(` - private data: ${namespace.namespace}`);
+ for (const collection of namespace.collection_pvt_rwset) {
+ console.log(` - collection: ${collection.collection_name}`);
+ if (collection.rwset.reads) {
+ for (const read of collection.rwset.reads) {
+ console.log(` - read set - ${BLUE}key:${RESET} ${read.key} ${BLUE}value:${read.value.toString()}`);
+ }
+ }
+ if (collection.rwset.writes) {
+ for (const write of collection.rwset.writes) {
+ console.log(` - write set - ${BLUE}key:${RESET}${write.key} ${BLUE}is_delete:${RESET}${write.is_delete} ${BLUE}value:${RESET}${write.value.toString()}`);
+ }
+ }
+ }
+ }
+ }
+ if (transEvent.transactionData) {
+ showTransactionData(transEvent.transactionData);
+ }
+ }
+ };
+ // now start the client side event service and register the listener
+ console.log(`${GREEN}--> Start private data block event stream to peer in Org1${RESET}`);
+ await network2Org1.addBlockListener(listener, {type: 'private'});
+ } catch (eventError) {
+ console.log(`${RED}<-- Failed: Setup block events - ${eventError}${RESET}`);
+ }
+
+ try {
+ // C R E A T E
+ console.log(`${GREEN}--> Submit Transaction: CreateAsset, ${assetKey} owned by Sam${RESET}`);
+ transaction = contract2Org1.createTransaction('CreateAsset');
+
+ // create the private data with salt and assign to the transaction
+ const randomNumber = Math.floor(Math.random() * 100) + 1;
+ const asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: assetKey,
+ Price: '90',
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ const asset_properties_string = JSON.stringify(asset_properties);
+ transaction.setTransient({
+ asset_properties: Buffer.from(asset_properties_string)
+ });
+ // With the addition of private data to the transaction
+ // We must only send this to the organization that will be
+ // saving the private data or we will get an endorsement policy failure
+ transaction.setEndorsingOrganizations(org1);
+ // endorse and commit - private data (transient data) will be
+ // saved to the implicit collection on the peer
+ await transaction.submit(assetKey, 'blue', '10', 'Sam', '100');
+ console.log(`${GREEN}<-- Submit CreateAsset Result: committed, asset ${assetKey}${RESET}`);
+ } catch (createError) {
+ console.log(`${RED}<-- Failed: CreateAsset - ${createError}${RESET}`);
+ }
+ await sleep(5000); // need to wait for event to be committed
+ try {
+ // R E A D
+ console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should be owned by Sam${RESET}`);
+ const resultBuffer = await contract2Org1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset(org1, resultBuffer, 'blue', '10', 'Sam', '100', '90');
+ } catch (readError) {
+ console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
+ }
+
+ try {
+ // U P D A T E
+ console.log(`${GREEN}--> Submit Transaction: UpdateAsset ${assetKey} update appraised value to 200`);
+ transaction = contract2Org1.createTransaction('UpdateAsset');
+
+ // update the private data with new salt and assign to the transaction
+ const randomNumber = Math.floor(Math.random() * 100) + 1;
+ const asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: assetKey,
+ Price: '90',
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ const asset_properties_string = JSON.stringify(asset_properties);
+ transaction.setTransient({
+ asset_properties: Buffer.from(asset_properties_string)
+ });
+ transaction.setEndorsingOrganizations(org1);
+
+ await transaction.submit(assetKey, 'blue', '10', 'Sam', '200');
+ console.log(`${GREEN}<-- Submit UpdateAsset Result: committed, asset ${assetKey}${RESET}`);
+ } catch (updateError) {
+ console.log(`${RED}<-- Failed: UpdateAsset - ${updateError}${RESET}`);
+ }
+ await sleep(5000); // need to wait for event to be committed
+ try {
+ // R E A D
+ console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now have appraised value of 200${RESET}`);
+ const resultBuffer = await contract2Org1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset(org1, resultBuffer, 'blue', '10', 'Sam', '200', '90');
+ } catch (readError) {
+ console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
+ }
+
+ try {
+ // T R A N S F E R
+ console.log(`${GREEN}--> Submit Transaction: TransferAsset ${assetKey} to Mary`);
+ transaction = contract2Org1.createTransaction('TransferAsset');
+
+ // update the private data with new salt and assign to the transaction
+ const randomNumber = Math.floor(Math.random() * 100) + 1;
+ const asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: assetKey,
+ Price: '180',
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ const asset_properties_string = JSON.stringify(asset_properties);
+ transaction.setTransient({
+ asset_properties: Buffer.from(asset_properties_string)
+ });
+ transaction.setEndorsingOrganizations(org1);
+
+ await transaction.submit(assetKey, 'Mary');
+ console.log(`${GREEN}<-- Submit TransferAsset Result: committed, asset ${assetKey}${RESET}`);
+ } catch (transferError) {
+ console.log(`${RED}<-- Failed: TransferAsset - ${transferError}${RESET}`);
+ }
+ await sleep(5000); // need to wait for event to be committed
+ try {
+ // R E A D
+ console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now be owned by Mary${RESET}`);
+ const resultBuffer = await contract2Org1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset(org1, resultBuffer, 'blue', '10', 'Mary', '200', '180');
+ } catch (readError) {
+ console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
+ }
+
+ try {
+ // D E L E T E
+ console.log(`${GREEN}--> Submit Transaction: DeleteAsset ${assetKey}`);
+ transaction = contract2Org1.createTransaction('DeleteAsset');
+ await transaction.submit(assetKey);
+ console.log(`${GREEN}<-- Submit DeleteAsset Result: committed, asset ${assetKey}${RESET}`);
+ } catch (deleteError) {
+ console.log(`${RED}<-- Failed: DeleteAsset - ${deleteError}${RESET}`);
+ }
+ await sleep(5000); // need to wait for event to be committed
+ try {
+ // R E A D
+ console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now be deleted${RESET}`);
+ const resultBuffer = await contract2Org1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset(org1, resultBuffer, 'blue', '10', 'Mary', '200');
+ console.log(`${RED}<-- Failed: ReadAsset - should not have read this asset${RESET}`);
+ } catch (readError) {
+ console.log(`${GREEN}<-- Success: ReadAsset - ${readError}${RESET}`);
+ }
+
+ // all done with this listener
+ network2Org1.removeBlockListener(listener);
+
+ } catch (runError) {
+ console.error(`Error in transaction: ${runError}`);
+ if (runError.stack) {
+ console.error(runError.stack);
+ }
+ }
+ } catch (error) {
+ console.error(`Error in setup: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+
+ await sleep(5000);
+ console.log(`${BLUE} **** END ****${RESET}`);
+ process.exit(0);
+}
+main();
diff --git a/asset-transfer-events/application-javascript/package.json b/asset-transfer-events/application-javascript/package.json
new file mode 100644
index 0000000..2767aed
--- /dev/null
+++ b/asset-transfer-events/application-javascript/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "asset-transfer-events",
+ "version": "1.0.0",
+ "description": "Javascript application that uses chaincode events and block events with private data",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.2",
+ "fabric-network": "^2.2.2"
+ }
+}
diff --git a/asset-transfer-events/chaincode-java/build.gradle b/asset-transfer-events/chaincode-java/build.gradle
new file mode 100644
index 0000000..f68178d
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/build.gradle
@@ -0,0 +1,51 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+plugins {
+ id 'application'
+ id 'checkstyle'
+ id 'jacoco'
+}
+
+group 'org.hyperledger.fabric.samples'
+version '1.0-SNAPSHOT'
+
+dependencies {
+ compileOnly 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+ testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+}
+
+repositories {
+ maven {
+ url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
+ }
+ jcenter()
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+application {
+ mainClassName = 'org.hyperledger.fabric.contract.ContractRouter'
+}
+
+
+checkstyle {
+ toolVersion '8.21'
+ configFile file("config/checkstyle/checkstyle.xml")
+}
+
+checkstyleMain {
+ source ='src/main/java'
+}
+
+checkstyleTest {
+ source ='src/test/java'
+}
+
+jacocoTestReport {
+ dependsOn test
+}
+
+installDist.dependsOn check
\ No newline at end of file
diff --git a/asset-transfer-events/chaincode-java/config/checkstyle/checkstyle.xml b/asset-transfer-events/chaincode-java/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..acd5df4
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/config/checkstyle/checkstyle.xml
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/asset-transfer-events/chaincode-java/config/checkstyle/suppressions.xml b/asset-transfer-events/chaincode-java/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000..33dda04
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/config/checkstyle/suppressions.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/asset-transfer-events/chaincode-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-events/chaincode-java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f3d88b1
Binary files /dev/null and b/asset-transfer-events/chaincode-java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/asset-transfer-events/chaincode-java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-events/chaincode-java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1b16c34
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/asset-transfer-events/chaincode-java/gradlew b/asset-transfer-events/chaincode-java/gradlew
new file mode 100755
index 0000000..2fe81a7
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/gradlew
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/asset-transfer-events/chaincode-java/gradlew.bat b/asset-transfer-events/chaincode-java/gradlew.bat
new file mode 100644
index 0000000..9618d8d
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/asset-transfer-events/chaincode-java/settings.gradle b/asset-transfer-events/chaincode-java/settings.gradle
new file mode 100644
index 0000000..7b5f82c
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/settings.gradle
@@ -0,0 +1,5 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+rootProject.name = 'asset-transfer-events-java'
diff --git a/asset-transfer-events/chaincode-java/src/main/java/org/hyperledger/fabric/samples/events/Asset.java b/asset-transfer-events/chaincode-java/src/main/java/org/hyperledger/fabric/samples/events/Asset.java
new file mode 100644
index 0000000..f9bdc18
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/src/main/java/org/hyperledger/fabric/samples/events/Asset.java
@@ -0,0 +1,158 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.events;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+
+
+import org.json.JSONObject;
+
+@DataType()
+public final class Asset {
+
+ @Property()
+ private final String assetID;
+
+ @Property()
+ private String color;
+
+ @Property()
+ private int size;
+
+ @Property()
+ private String owner;
+
+ @Property()
+ private int appraisedValue;
+
+ public Asset(final String assetID, final String color,
+ final int size, final String owner, final int value) {
+
+ this.assetID = assetID;
+ this.color = color;
+ this.size = size;
+ this.owner = owner;
+ this.appraisedValue = value;
+ }
+
+ public String getAssetID() {
+ return assetID;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public int getAppraisedValue() {
+ return appraisedValue;
+ }
+
+ public void setOwner(final String newowner) {
+ this.owner = newowner;
+ }
+
+ public void setAppraisedValue(final int value) {
+ this.appraisedValue = value;
+ }
+
+ public void setColor(final String c) {
+ this.color = c;
+ }
+
+ public void setSize(final int s) {
+ this.size = s;
+ }
+
+ // Serialize asset without private properties
+ public byte[] serialize() {
+ return serialize(null).getBytes(UTF_8);
+ }
+
+ public String serialize(final String privateProps) {
+ Map tMap = new HashMap();
+ tMap.put("ID", assetID);
+ tMap.put("Color", color);
+ tMap.put("Owner", owner);
+ tMap.put("Size", Integer.toString(size));
+ tMap.put("AppraisedValue", Integer.toString(appraisedValue));
+ if (privateProps != null && privateProps.length() > 0) {
+ tMap.put("asset_properties", new JSONObject(privateProps));
+ }
+ return new JSONObject(tMap).toString();
+ }
+
+ public static Asset deserialize(final byte[] assetJSON) {
+ return deserialize(new String(assetJSON, UTF_8));
+ }
+
+ public static Asset deserialize(final String assetJSON) {
+
+ JSONObject json = new JSONObject(assetJSON);
+ Map tMap = json.toMap();
+ final String id = (String) tMap.get("ID");
+
+ final String color = (String) tMap.get("Color");
+ final String owner = (String) tMap.get("Owner");
+ int size = 0;
+ int appraisedValue = 0;
+ if (tMap.containsKey("Size")) {
+ size = Integer.parseInt((String) tMap.get("Size"));
+ }
+ if (tMap.containsKey("AppraisedValue")) {
+ appraisedValue = Integer.parseInt((String) tMap.get("AppraisedValue"));
+ }
+ return new Asset(id, color, size, owner, appraisedValue);
+
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if ((obj == null) || (getClass() != obj.getClass())) {
+ return false;
+ }
+
+ Asset other = (Asset) obj;
+
+ return Objects.deepEquals(
+ new String[]{getAssetID(), getColor(), getOwner()},
+ new String[]{other.getAssetID(), other.getColor(), other.getOwner()})
+ &&
+ Objects.deepEquals(
+ new int[]{getSize(), getAppraisedValue()},
+ new int[]{other.getSize(), other.getAppraisedValue()});
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getAssetID(), getColor(), getSize(), getOwner(), getAppraisedValue());
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode())
+ + " [assetID=" + assetID + ", appraisedValue=" + appraisedValue + ", color="
+ + color + ", size=" + size + ", owner=" + owner + "]";
+ }
+
+}
diff --git a/asset-transfer-events/chaincode-java/src/main/java/org/hyperledger/fabric/samples/events/AssetTransfer.java b/asset-transfer-events/chaincode-java/src/main/java/org/hyperledger/fabric/samples/events/AssetTransfer.java
new file mode 100644
index 0000000..536167a
--- /dev/null
+++ b/asset-transfer-events/chaincode-java/src/main/java/org/hyperledger/fabric/samples/events/AssetTransfer.java
@@ -0,0 +1,299 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.events;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.contract.ContractInterface;
+import org.hyperledger.fabric.contract.annotation.Contact;
+import org.hyperledger.fabric.contract.annotation.Contract;
+import org.hyperledger.fabric.contract.annotation.Default;
+import org.hyperledger.fabric.contract.annotation.Info;
+import org.hyperledger.fabric.contract.annotation.License;
+import org.hyperledger.fabric.contract.annotation.Transaction;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+
+import java.util.Map;
+
+/**
+ * Main Chaincode class.
+ *
+ * @see org.hyperledger.fabric.shim.Chaincode
+ *
+ * Each chaincode transaction function must take, Context as first parameter.
+ * Unless specified otherwise via annotation (@Contract or @Transaction), the contract name
+ * is the class name (without package)
+ * and the transaction name is the method name.
+ */
+@Contract(
+ name = "asset-transfer-events-java",
+ info = @Info(
+ title = "Asset Transfer Events",
+ description = "The hyperlegendary asset transfer events sample",
+ version = "0.0.1-SNAPSHOT",
+ license = @License(
+ name = "Apache 2.0 License",
+ url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
+ contact = @Contact(
+ email = "a.transfer@example.com",
+ name = "Fabric Development Team",
+ url = "https://hyperledger.example.com")))
+@Default
+public final class AssetTransfer implements ContractInterface {
+
+ static final String IMPLICIT_COLLECTION_NAME_PREFIX = "_implicit_org_";
+ static final String PRIVATE_PROPS_KEY = "asset_properties";
+
+ /**
+ * Retrieves the asset details with the specified ID
+ *
+ * @param ctx the transaction context
+ * @param assetID the ID of the asset
+ * @return the asset found on the ledger. Returns error if asset is not found
+ */
+ @Transaction(intent = Transaction.TYPE.EVALUATE)
+ public String ReadAsset(final Context ctx, final String assetID) {
+ System.out.printf("ReadAsset: ID %s\n", assetID);
+
+ Asset asset = getState(ctx, assetID);
+ String privData = readPrivateData(ctx, assetID);
+ return asset.serialize(privData);
+ }
+
+ /**
+ * Creates a new asset on the ledger. Saves the passed private data (asset properties) from transient map input.
+ *
+ * @param ctx the transaction context
+ * Transient map with asset_properties key with asset json as value
+ * @param assetID
+ * @param color
+ * @param size
+ * @param owner
+ * @param appraisedValue
+ * @return the created asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size, final String owner, final int appraisedValue) {
+ ChaincodeStub stub = ctx.getStub();
+ //input validations
+ String errorMessage = null;
+ if (assetID == null || assetID.equals("")) {
+ errorMessage = String.format("Empty input: assetID");
+ }
+ if (owner == null || owner.equals("")) {
+ errorMessage = String.format("Empty input: owner");
+ }
+
+ if (errorMessage != null) {
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+ // Check if asset already exists
+ byte[] assetJSON = ctx.getStub().getState(assetID);
+ if (assetJSON != null && assetJSON.length > 0) {
+ errorMessage = String.format("Asset %s already exists", assetID);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_ALREADY_EXISTS.toString());
+ }
+
+ Asset asset = new Asset(assetID, color, size, owner, appraisedValue);
+
+ savePrivateData(ctx, assetID);
+ assetJSON = asset.serialize();
+ System.out.printf("CreateAsset Put: ID %s Data %s\n", assetID, new String(assetJSON));
+
+ stub.putState(assetID, assetJSON);
+ // add Event data to the transaction data. Event will be published after the block containing
+ // this transaction is committed
+ stub.setEvent("CreateAsset", assetJSON);
+ return asset;
+ }
+
+
+ /**
+ * TransferAsset transfers the asset to the new owner
+ * Save any private data, if provided in transient map
+ *
+ * @param ctx the transaction context
+ * @param assetID asset to delete
+ * @param newOwner new owner for the asset
+ * @return none
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public void TransferAsset(final Context ctx, final String assetID, final String newOwner) {
+ ChaincodeStub stub = ctx.getStub();
+ String errorMessage = null;
+
+ if (assetID == null || assetID.equals("")) {
+ errorMessage = "Empty input: assetID";
+ }
+ if (newOwner == null || newOwner.equals("")) {
+ errorMessage = "Empty input: newOwner";
+ }
+ if (errorMessage != null) {
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+ System.out.printf("TransferAsset: verify asset %s exists\n", assetID);
+ Asset thisAsset = getState(ctx, assetID);
+ // Transfer asset to new owner
+ thisAsset.setOwner(newOwner);
+
+ System.out.printf(" Transfer Asset: ID %s to owner %s\n", assetID, newOwner);
+ savePrivateData(ctx, assetID); // save private data if any
+ byte[] assetJSON = thisAsset.serialize();
+
+ stub.putState(assetID, assetJSON);
+ stub.setEvent("TransferAsset", assetJSON); //publish Event
+ }
+
+ /**
+ * Update existing asset on the ledger with provided parameters.
+ * Saves the passed private data (asset properties) from transient map input.
+ *
+ * @param ctx the transaction context
+ * Transient map with asset_properties key with asset json as value
+ * @param assetID
+ * @param color
+ * @param size
+ * @param owner
+ * @param appraisedValue
+ * @return the created asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size, final String owner, final int appraisedValue) {
+ ChaincodeStub stub = ctx.getStub();
+ //input validations
+ String errorMessage = null;
+ if (assetID == null || assetID.equals("")) {
+ errorMessage = String.format("Empty input: assetID");
+ }
+
+ if (errorMessage != null) {
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+ // reads from the Statedb. Check if asset already exists
+ Asset asset = getState(ctx, assetID);
+
+ if (owner != null) {
+ asset.setOwner(owner);
+ }
+ if (color != null) {
+ asset.setColor(color);
+ }
+ if (size > 0) {
+ asset.setSize(size);
+ }
+ if (appraisedValue > 0) {
+ asset.setAppraisedValue(appraisedValue);
+ }
+
+ savePrivateData(ctx, assetID);
+ byte[] assetJSON = asset.serialize();
+ System.out.printf("UpdateAsset Put: ID %s Data %s\n", assetID, new String(assetJSON));
+ stub.putState(assetID, assetJSON);
+ stub.setEvent("UpdateAsset", assetJSON); //publish Event
+ return asset;
+ }
+
+ /**
+ * Deletes a asset & related details from the ledger.
+ *
+ * @param ctx the transaction context
+ * @param assetID asset to delete
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public void DeleteAsset(final Context ctx, final String assetID) {
+ ChaincodeStub stub = ctx.getStub();
+ System.out.printf("DeleteAsset: verify asset %s exists\n", assetID);
+ Asset asset = getState(ctx, assetID);
+
+ System.out.printf(" DeleteAsset: ID %s\n", assetID);
+ // delete private details of asset
+ removePrivateData(ctx, assetID);
+ stub.delState(assetID); // delete the key from Statedb
+ stub.setEvent("DeleteAsset", asset.serialize()); //publish Event
+ }
+
+ private Asset getState(final Context ctx, final String assetID) {
+ byte[] assetJSON = ctx.getStub().getState(assetID);
+ if (assetJSON == null || assetJSON.length == 0) {
+ String errorMessage = String.format("Asset %s does not exist", assetID);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+
+ try {
+ Asset asset = Asset.deserialize(assetJSON);
+ return asset;
+ } catch (Exception e) {
+ throw new ChaincodeException("Deserialize error: " + e.getMessage(), AssetTransferErrors.DATA_ERROR.toString());
+ }
+ }
+
+ private String readPrivateData(final Context ctx, final String assetKey) {
+ String peerMSPID = ctx.getStub().getMspId();
+ String clientMSPID = ctx.getClientIdentity().getMSPID();
+ String implicitCollectionName = getCollectionName(ctx);
+ String privData = null;
+ //only if ClientOrgMatchesPeerOrg
+ if (peerMSPID.equals(clientMSPID)) {
+ System.out.printf(" ReadPrivateData from collection %s, ID %s\n", implicitCollectionName, assetKey);
+ byte[] propJSON = ctx.getStub().getPrivateData(implicitCollectionName, assetKey);
+
+ if (propJSON != null && propJSON.length > 0) {
+ privData = new String(propJSON, UTF_8);
+ }
+ }
+ return privData;
+ }
+
+ private void savePrivateData(final Context ctx, final String assetKey) {
+ String peerMSPID = ctx.getStub().getMspId();
+ String clientMSPID = ctx.getClientIdentity().getMSPID();
+ String implicitCollectionName = getCollectionName(ctx);
+
+ if (peerMSPID.equals(clientMSPID)) {
+ Map transientMap = ctx.getStub().getTransient();
+ if (transientMap != null && transientMap.containsKey(PRIVATE_PROPS_KEY)) {
+ byte[] transientAssetJSON = transientMap.get(PRIVATE_PROPS_KEY);
+
+ System.out.printf("Asset's PrivateData Put in collection %s, ID %s\n", implicitCollectionName, assetKey);
+ ctx.getStub().putPrivateData(implicitCollectionName, assetKey, transientAssetJSON);
+ }
+ }
+ }
+
+ private void removePrivateData(final Context ctx, final String assetKey) {
+ String peerMSPID = ctx.getStub().getMspId();
+ String clientMSPID = ctx.getClientIdentity().getMSPID();
+ String implicitCollectionName = getCollectionName(ctx);
+
+ if (peerMSPID.equals(clientMSPID)) {
+ System.out.printf("PrivateData Delete from collection %s, ID %s\n", implicitCollectionName, assetKey);
+ ctx.getStub().delPrivateData(implicitCollectionName, assetKey);
+ }
+ }
+
+ // Return the implicit collection name, to use for private property persistance
+ private String getCollectionName(final Context ctx) {
+ // Get the MSP ID of submitting client identity
+ String clientMSPID = ctx.getClientIdentity().getMSPID();
+ String collectionName = IMPLICIT_COLLECTION_NAME_PREFIX + clientMSPID;
+ return collectionName;
+ }
+
+ private enum AssetTransferErrors {
+ INCOMPLETE_INPUT,
+ INVALID_ACCESS,
+ ASSET_NOT_FOUND,
+ ASSET_ALREADY_EXISTS,
+ DATA_ERROR
+ }
+
+}
diff --git a/asset-transfer-events/chaincode-javascript/.eslintignore b/asset-transfer-events/chaincode-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-events/chaincode-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-events/chaincode-javascript/.eslintrc.js b/asset-transfer-events/chaincode-javascript/.eslintrc.js
new file mode 100644
index 0000000..cb00fa9
--- /dev/null
+++ b/asset-transfer-events/chaincode-javascript/.eslintrc.js
@@ -0,0 +1,39 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+'use strict';
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true,
+ es6: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: 'eslint:recommended',
+ rules: {
+ indent: ['error', 'tab'],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed'],
+ 'no-constant-condition': ['error', { checkLoops: false }]
+ }
+};
diff --git a/asset-transfer-events/chaincode-javascript/.gitignore b/asset-transfer-events/chaincode-javascript/.gitignore
new file mode 100644
index 0000000..eeace29
--- /dev/null
+++ b/asset-transfer-events/chaincode-javascript/.gitignore
@@ -0,0 +1,15 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Report cache used by istanbul
+.nyc_output
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+package-lock.json
diff --git a/asset-transfer-events/chaincode-javascript/index.js b/asset-transfer-events/chaincode-javascript/index.js
new file mode 100644
index 0000000..3244ced
--- /dev/null
+++ b/asset-transfer-events/chaincode-javascript/index.js
@@ -0,0 +1,12 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const assetTransferEvents = require('./lib/assetTransferEvents');
+
+module.exports.AssetTransferEvents = assetTransferEvents;
+module.exports.contracts = [assetTransferEvents];
diff --git a/asset-transfer-events/chaincode-javascript/lib/assetTransferEvents.js b/asset-transfer-events/chaincode-javascript/lib/assetTransferEvents.js
new file mode 100644
index 0000000..27c5acb
--- /dev/null
+++ b/asset-transfer-events/chaincode-javascript/lib/assetTransferEvents.js
@@ -0,0 +1,128 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Contract } = require('fabric-contract-api');
+
+async function savePrivateData(ctx, assetKey) {
+ const clientOrg = ctx.clientIdentity.getMSPID();
+ const peerOrg = ctx.stub.getMspID();
+ const collection = '_implicit_org_' + peerOrg;
+
+ if (clientOrg === peerOrg) {
+ const transientMap = ctx.stub.getTransient();
+ if (transientMap) {
+ const properties = transientMap.get('asset_properties');
+ if (properties) {
+ await ctx.stub.putPrivateData(collection, assetKey, properties);
+ }
+ }
+ }
+}
+
+async function removePrivateData(ctx, assetKey) {
+ const clientOrg = ctx.clientIdentity.getMSPID();
+ const peerOrg = ctx.stub.getMspID();
+ const collection = '_implicit_org_' + peerOrg;
+
+ if (clientOrg === peerOrg) {
+ const propertiesBuffer = await ctx.stub.getPrivateData(collection, assetKey);
+ if (propertiesBuffer && propertiesBuffer.length > 0) {
+ await ctx.stub.deletePrivateData(collection, assetKey);
+ }
+ }
+}
+
+async function addPrivateData(ctx, assetKey, asset) {
+ const clientOrg = ctx.clientIdentity.getMSPID();
+ const peerOrg = ctx.stub.getMspID();
+ const collection = '_implicit_org_' + peerOrg;
+
+ if (clientOrg === peerOrg) {
+ const propertiesBuffer = await ctx.stub.getPrivateData(collection, assetKey);
+ if (propertiesBuffer && propertiesBuffer.length > 0) {
+ const properties = JSON.parse(propertiesBuffer.toString());
+ asset.asset_properties = properties;
+ }
+ }
+}
+
+async function readState(ctx, id) {
+ const assetBuffer = await ctx.stub.getState(id); // get the asset from chaincode state
+ if (!assetBuffer || assetBuffer.length === 0) {
+ throw new Error(`The asset ${id} does not exist`);
+ }
+ const assetString = assetBuffer.toString();
+ const asset = JSON.parse(assetString);
+
+ return asset;
+}
+
+class AssetTransferEvents extends Contract {
+
+ // CreateAsset issues a new asset to the world state with given details.
+ async CreateAsset(ctx, id, color, size, owner, appraisedValue) {
+ const asset = {
+ ID: id,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ };
+ await savePrivateData(ctx, id);
+ const assetBuffer = Buffer.from(JSON.stringify(asset));
+
+ ctx.stub.setEvent('CreateAsset', assetBuffer);
+ return ctx.stub.putState(id, assetBuffer);
+ }
+
+ // TransferAsset updates the owner field of an asset with the given id in
+ // the world state.
+ async TransferAsset(ctx, id, newOwner) {
+ const asset = await readState(ctx, id);
+ asset.Owner = newOwner;
+ const assetBuffer = Buffer.from(JSON.stringify(asset));
+ await savePrivateData(ctx, id);
+
+ ctx.stub.setEvent('TransferAsset', assetBuffer);
+ return ctx.stub.putState(id, assetBuffer);
+ }
+
+ // ReadAsset returns the asset stored in the world state with given id.
+ async ReadAsset(ctx, id) {
+ const asset = await readState(ctx, id);
+ await addPrivateData(ctx, asset.ID, asset);
+
+ return JSON.stringify(asset);
+ }
+
+ // UpdateAsset updates an existing asset in the world state with provided parameters.
+ async UpdateAsset(ctx, id, color, size, owner, appraisedValue) {
+ const asset = await readState(ctx, id);
+ asset.Color = color;
+ asset.Size = size;
+ asset.Owner = owner;
+ asset.AppraisedValue = appraisedValue;
+ const assetBuffer = Buffer.from(JSON.stringify(asset));
+ await savePrivateData(ctx, id);
+
+ ctx.stub.setEvent('UpdateAsset', assetBuffer);
+ return ctx.stub.putState(id, assetBuffer);
+ }
+
+ // DeleteAsset deletes an given asset from the world state.
+ async DeleteAsset(ctx, id) {
+ const asset = await readState(ctx, id);
+ const assetBuffer = Buffer.from(JSON.stringify(asset));
+ await removePrivateData(ctx, id);
+
+ ctx.stub.setEvent('DeleteAsset', assetBuffer);
+ return ctx.stub.deleteState(id);
+ }
+}
+
+module.exports = AssetTransferEvents;
diff --git a/asset-transfer-events/chaincode-javascript/package.json b/asset-transfer-events/chaincode-javascript/package.json
new file mode 100644
index 0000000..143b35c
--- /dev/null
+++ b/asset-transfer-events/chaincode-javascript/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "asset-transfer-events",
+ "version": "1.0.0",
+ "description": "Asset-Transfer-Events contract implemented in JavaScript",
+ "main": "index.js",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "test": "nyc mocha --recursive",
+ "start": "fabric-chaincode-node start"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ },
+ "devDependencies": {
+ "chai": "^4.1.2",
+ "eslint": "^4.19.1",
+ "mocha": "^8.0.1",
+ "nyc": "^14.1.1",
+ "sinon": "^6.0.0",
+ "sinon-chai": "^3.2.0"
+ },
+ "nyc": {
+ "exclude": [
+ "coverage/**",
+ "test/**",
+ "index.js",
+ ".eslintrc.js"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/asset-transfer-events/chaincode-javascript/test/assetTransferEvents.test.js b/asset-transfer-events/chaincode-javascript/test/assetTransferEvents.test.js
new file mode 100644
index 0000000..b243cdb
--- /dev/null
+++ b/asset-transfer-events/chaincode-javascript/test/assetTransferEvents.test.js
@@ -0,0 +1,224 @@
+'use strict';
+const sinon = require('sinon');
+const chai = require('chai');
+const sinonChai = require('sinon-chai');
+const expect = chai.expect;
+
+const { Context } = require('fabric-contract-api');
+const { ChaincodeStub, ClientIdentity } = require('fabric-shim');
+
+const AssetTransfer = require('../lib/assetTransferEvents.js');
+
+let assert = sinon.assert;
+chai.use(sinonChai);
+
+describe('Asset Transfer Events Tests', () => {
+ let transactionContext, chaincodeStub, clientIdentity, asset;
+ let transientMap, asset_properties;
+
+ beforeEach(() => {
+ transactionContext = new Context();
+
+ chaincodeStub = sinon.createStubInstance(ChaincodeStub);
+ chaincodeStub.getMspID.returns('org1');
+ transactionContext.setChaincodeStub(chaincodeStub);
+
+ clientIdentity = sinon.createStubInstance(ClientIdentity);
+ clientIdentity.getMSPID.returns('org1');
+ transactionContext.clientIdentity = clientIdentity;
+
+ chaincodeStub.putState.callsFake((key, value) => {
+ if (!chaincodeStub.states) {
+ chaincodeStub.states = {};
+ }
+ chaincodeStub.states[key] = value;
+ });
+
+ chaincodeStub.getState.callsFake(async (key) => {
+ let ret;
+ if (chaincodeStub.states) {
+ ret = chaincodeStub.states[key];
+ }
+ return Promise.resolve(ret);
+ });
+
+ chaincodeStub.deleteState.callsFake(async (key) => {
+ if (chaincodeStub.states) {
+ delete chaincodeStub.states[key];
+ }
+ return Promise.resolve(key);
+ });
+
+ chaincodeStub.getStateByRange.callsFake(async () => {
+ function* internalGetStateByRange() {
+ if (chaincodeStub.states) {
+ // Shallow copy
+ const copied = Object.assign({}, chaincodeStub.states);
+
+ for (let key in copied) {
+ yield {value: copied[key]};
+ }
+ }
+ }
+
+ return Promise.resolve(internalGetStateByRange());
+ });
+
+ asset = {
+ ID: 'asset1',
+ Color: 'blue',
+ Size: 5,
+ Owner: 'Tomoko',
+ AppraisedValue: 300,
+ };
+ const randomNumber = Math.floor(Math.random() * 100) + 1;
+ asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: 'asset1',
+ Price: '90',
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ transientMap = {
+ asset_properties: Buffer.from(JSON.stringify(asset_properties))
+ };
+ });
+
+ describe('Test CreateAsset', () => {
+ it('should return error on CreateAsset', async () => {
+ chaincodeStub.putState.rejects('failed inserting key');
+
+ let assetTransfer = new AssetTransfer();
+ try {
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+ assert.fail('CreateAsset should have failed');
+ } catch(err) {
+ expect(err.name).to.equal('failed inserting key');
+ }
+ });
+
+ it('should return success on CreateAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ let ret = JSON.parse((await chaincodeStub.getState(asset.ID)).toString());
+ expect(ret).to.eql(asset);
+ });
+ it('should return success on CreateAsset with transient data', async () => {
+ let assetTransfer = new AssetTransfer();
+ chaincodeStub.getTransient.returns(transientMap);
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ let ret = JSON.parse((await chaincodeStub.getState(asset.ID)).toString());
+ expect(ret).to.eql(asset);
+ });
+ });
+
+ describe('Test ReadAsset', () => {
+ it('should return error on ReadAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ try {
+ await assetTransfer.ReadAsset(transactionContext, 'asset2');
+ assert.fail('ReadAsset should have failed');
+ } catch (err) {
+ expect(err.message).to.equal('The asset asset2 does not exist');
+ }
+ });
+
+ it('should return success on ReadAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+ const assetString = await assetTransfer.ReadAsset(transactionContext, 'asset1');
+ const readAsset = JSON.parse(assetString);
+ expect(readAsset).to.eql(asset);
+ });
+
+ it('should return success on ReadAsset with private data', async () => {
+ asset.asset_properties = asset_properties;
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+ chaincodeStub.getPrivateData.returns(Buffer.from(JSON.stringify(asset_properties)));
+ const assetString = await assetTransfer.ReadAsset(transactionContext, 'asset1');
+ const readAsset = JSON.parse(assetString);
+ expect(readAsset).to.eql(asset);
+ });
+ });
+
+ describe('Test UpdateAsset', () => {
+ it('should return error on UpdateAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ try {
+ await assetTransfer.UpdateAsset(transactionContext, 'asset2', 'orange', 10, 'Me', 500);
+ assert.fail('UpdateAsset should have failed');
+ } catch (err) {
+ expect(err.message).to.equal('The asset asset2 does not exist');
+ }
+ });
+
+ it('should return success on UpdateAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ await assetTransfer.UpdateAsset(transactionContext, 'asset1', 'orange', 10, 'Me', 500);
+ let ret = JSON.parse(await chaincodeStub.getState(asset.ID));
+ let expected = {
+ ID: 'asset1',
+ Color: 'orange',
+ Size: 10,
+ Owner: 'Me',
+ AppraisedValue: 500
+ };
+ expect(ret).to.eql(expected);
+ });
+ });
+
+ describe('Test DeleteAsset', () => {
+ it('should return error on DeleteAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ try {
+ await assetTransfer.DeleteAsset(transactionContext, 'asset2');
+ assert.fail('DeleteAsset should have failed');
+ } catch (err) {
+ expect(err.message).to.equal('The asset asset2 does not exist');
+ }
+ });
+
+ it('should return success on DeleteAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ await assetTransfer.DeleteAsset(transactionContext, asset.ID);
+ let ret = await chaincodeStub.getState(asset.ID);
+ expect(ret).to.equal(undefined);
+ });
+ });
+
+ describe('Test TransferAsset', () => {
+ it('should return error on TransferAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ try {
+ await assetTransfer.TransferAsset(transactionContext, 'asset2', 'Me');
+ assert.fail('DeleteAsset should have failed');
+ } catch (err) {
+ expect(err.message).to.equal('The asset asset2 does not exist');
+ }
+ });
+
+ it('should return success on TransferAsset', async () => {
+ let assetTransfer = new AssetTransfer();
+ await assetTransfer.CreateAsset(transactionContext, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue);
+
+ await assetTransfer.TransferAsset(transactionContext, asset.ID, 'Me');
+ let ret = JSON.parse((await chaincodeStub.getState(asset.ID)).toString());
+ expect(ret).to.eql(Object.assign({}, asset, {Owner: 'Me'}));
+ });
+ });
+});
diff --git a/asset-transfer-ledger-queries/application-java/.gitattributes b/asset-transfer-ledger-queries/application-java/.gitattributes
new file mode 100644
index 0000000..00a51af
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat text eol=crlf
+
diff --git a/asset-transfer-ledger-queries/application-java/build.gradle b/asset-transfer-ledger-queries/application-java/build.gradle
new file mode 100644
index 0000000..e2eed62
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * This generated file contains a sample Java project to get you started.
+ * For more details take a look at the Java Quickstart chapter in the Gradle
+ * User Manual available at https://docs.gradle.org/6.5/userguide/tutorial_java_projects.html
+ */
+
+plugins {
+ // Apply the java plugin to add support for Java
+ id 'java'
+
+ // Apply the application plugin to add support for building a CLI application.
+ id 'application'
+}
+ext {
+ javaMainClass = "application.java.App"
+}
+
+repositories {
+ // Use jcenter for resolving dependencies.
+ // You can declare any Maven/Ivy/file repository here.
+ jcenter()
+}
+
+dependencies {
+ // This dependency is used by the application.
+ implementation 'com.google.guava:guava:29.0-jre'
+ implementation 'org.hyperledger.fabric:fabric-gateway-java:2.1.1'
+}
+
+application {
+ // Define the main class for the application.
+ mainClassName = 'application.java.App'
+}
+
+// task for running the app after building dependencies
+task runApp(type: Exec) {
+ dependsOn build
+ group = "Execution"
+ description = "Run the main class with ExecTask"
+ commandLine "java", "-classpath", sourceSets.main.runtimeClasspath.getAsPath(), javaMainClass
+}
\ No newline at end of file
diff --git a/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..62d4c05
Binary files /dev/null and b/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..622ab64
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/asset-transfer-ledger-queries/application-java/gradlew b/asset-transfer-ledger-queries/application-java/gradlew
new file mode 100755
index 0000000..fbd7c51
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/asset-transfer-ledger-queries/application-java/gradlew.bat b/asset-transfer-ledger-queries/application-java/gradlew.bat
new file mode 100644
index 0000000..5093609
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/asset-transfer-ledger-queries/application-java/settings.gradle b/asset-transfer-ledger-queries/application-java/settings.gradle
new file mode 100644
index 0000000..5423bc7
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/settings.gradle
@@ -0,0 +1,10 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * The settings file is used to specify which projects to include in your build.
+ *
+ * Detailed information about configuring a multi-project build in Gradle can be found
+ * in the user manual at https://docs.gradle.org/6.5/userguide/multi_project_builds.html
+ */
+
+rootProject.name = 'application-java'
diff --git a/asset-transfer-ledger-queries/application-java/src/main/java/application/java/App.java b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/App.java
new file mode 100644
index 0000000..2a1b74c
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/App.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// Running TestApp:
+// gradle runApp
+
+package application.java;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.hyperledger.fabric.gateway.Contract;
+import org.hyperledger.fabric.gateway.Gateway;
+import org.hyperledger.fabric.gateway.Network;
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+
+
+public class App {
+
+ static {
+ System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
+ }
+
+ // helper function for getting connected to the gateway
+ public static Gateway connect() throws Exception{
+ // Load a file system based wallet for managing identities.
+ Path walletPath = Paths.get("wallet");
+ Wallet wallet = Wallets.newFileSystemWallet(walletPath);
+ // load a CCP
+ Path networkConfigPath = Paths.get("..", "..", "test-network", "organizations", "peerOrganizations", "org1.example.com", "connection-org1.yaml");
+
+ Gateway.Builder builder = Gateway.createBuilder();
+ builder.identity(wallet, "appUser").networkConfig(networkConfigPath).discovery(true);
+ return builder.connect();
+ }
+
+ public static void main(String[] args) throws Exception {
+ // enrolls the admin and registers the user
+ try {
+ EnrollAdmin.main(null);
+ RegisterUser.main(null);
+ } catch (Exception e) {
+ System.err.println(e);
+ }
+
+ // connect to the network and invoke the smart contract
+ try (Gateway gateway = connect()) {
+
+ // get the network and contract
+ Network network = gateway.getNetwork("mychannel");
+ Contract contract = network.getContract("ledger");
+
+ byte[] result;
+
+ System.out.println("Submit Transaction: InitLedger creates the initial set of assets on the ledger.");
+ contract.submitTransaction("InitLedger");
+
+ System.out.println("\n");
+ // passing in 2 empty strings will query all the assets
+ result = contract.evaluateTransaction("GetAssetsByRange", "", "");
+ System.out.println("Evaluate Transaction: GetAssetsByRange, result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Submit Transaction: CreateAsset asset13");
+ //CreateAsset creates an asset with ID asset13, color yellow, owner Tom, size 5 and appraisedValue of 1300
+ contract.submitTransaction("CreateAsset", "asset13", "yellow", "5", "Tom", "1300");
+
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction: ReadAsset asset13");
+ // ReadAsset returns an asset with given assetID
+ result = contract.evaluateTransaction("ReadAsset", "asset13");
+ System.out.println("result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction: AssetExists asset1");
+ // AssetExists returns "true" if an asset with given assetID exist
+ result = contract.evaluateTransaction("AssetExists", "asset1");
+ System.out.println("result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Submit Transaction: DeleteAsset asset1");
+ contract.submitTransaction("DeleteAsset", "asset1");
+
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction: AssetExists asset1");
+ // AssetExists returns "true" if an asset with given assetID exist
+ result = contract.evaluateTransaction("AssetExists", "asset1");
+ System.out.println("result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Submit Transaction: TransferAsset asset2 from owner Tomoko > owner Tom");
+ // TransferAsset transfers an asset with given ID to new owner Tom
+ contract.submitTransaction("TransferAsset", "asset2", "Tom");
+
+ // Rich Query with Pagination (Only supported if CouchDB is used as state database)
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction:QueryAssetsWithPagination Tom's assets");
+ result = contract.evaluateTransaction("QueryAssetsWithPagination","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","3","");
+ System.out.println("result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Submit Transaction: TransferAssetByColor yellow assets > newOwner Michel");
+ contract.submitTransaction("TransferAssetByColor", "yellow", "Michel");
+
+ // Rich Query (Only supported if CouchDB is used as state database):
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction:QueryAssetsByOwner Michel");
+ result = contract.evaluateTransaction("QueryAssetsByOwner", "Michel");
+ System.out.println("result: " + new String(result));
+
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction:GetAssetHistory asset13");
+ result = contract.evaluateTransaction("GetAssetHistory", "asset13");
+ System.out.println("result: " + new String(result));
+
+ // Rich Query (Only supported if CouchDB is used as state database):
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction:QueryAssets assets of size 15");
+ result = contract.evaluateTransaction("QueryAssets", "{\"selector\":{\"size\":15}}");
+ System.out.println("result: " + new String(result));
+
+ // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction:QueryAssets Jin Soo's assets");
+ result = contract.evaluateTransaction("QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Jin Soo\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}");
+ System.out.println("result: " + new String(result));
+
+ // Rich Query with Pagination (Only supported if CouchDB is used as state database)
+ System.out.println("\n");
+ System.out.println("Evaluate Transaction:GetAssetsByRangeWithPagination assets 3-5");
+ result = contract.evaluateTransaction("GetAssetsByRangeWithPagination", "asset3", "asset6", "3","");
+ System.out.println("result: " + new String(result));
+ }
+ catch(Exception e){
+ System.err.println(e);
+ }
+
+ }
+}
diff --git a/asset-transfer-ledger-queries/application-java/src/main/java/application/java/EnrollAdmin.java b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/EnrollAdmin.java
new file mode 100644
index 0000000..563a35f
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/EnrollAdmin.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package application.java;
+
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.hyperledger.fabric.gateway.Identities;
+import org.hyperledger.fabric.gateway.Identity;
+import org.hyperledger.fabric.sdk.Enrollment;
+import org.hyperledger.fabric.sdk.security.CryptoSuite;
+import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
+import org.hyperledger.fabric_ca.sdk.EnrollmentRequest;
+import org.hyperledger.fabric_ca.sdk.HFCAClient;
+
+public class EnrollAdmin {
+
+ public static void main(String[] args) throws Exception {
+
+ // Create a CA client for interacting with the CA.
+ Properties props = new Properties();
+ props.put("pemFile",
+ "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
+ props.put("allowAllHostNames", "true");
+ HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
+ CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
+ caClient.setCryptoSuite(cryptoSuite);
+
+ // Create a wallet for managing identities
+ Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));
+
+ // Check to see if we've already enrolled the admin user.
+ if (wallet.get("admin") != null) {
+ System.out.println("An identity for the admin user \"admin\" already exists in the wallet");
+ return;
+ }
+
+ // Enroll the admin user, and import the new identity into the wallet.
+ final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
+ enrollmentRequestTLS.addHost("localhost");
+ enrollmentRequestTLS.setProfile("tls");
+ Enrollment enrollment = caClient.enroll("admin", "adminpw", enrollmentRequestTLS);
+ Identity user = Identities.newX509Identity("Org1MSP", enrollment);
+ wallet.put("admin", user);
+ System.out.println("Successfully enrolled user \"admin\" and imported it into the wallet");
+ }
+}
diff --git a/asset-transfer-ledger-queries/application-java/src/main/java/application/java/RegisterUser.java b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/RegisterUser.java
new file mode 100644
index 0000000..367b4a3
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/src/main/java/application/java/RegisterUser.java
@@ -0,0 +1,107 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package application.java;
+
+import java.nio.file.Paths;
+import java.security.PrivateKey;
+import java.util.Properties;
+import java.util.Set;
+
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.hyperledger.fabric.gateway.Identities;
+import org.hyperledger.fabric.gateway.Identity;
+import org.hyperledger.fabric.gateway.X509Identity;
+import org.hyperledger.fabric.sdk.Enrollment;
+import org.hyperledger.fabric.sdk.User;
+import org.hyperledger.fabric.sdk.security.CryptoSuite;
+import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
+import org.hyperledger.fabric_ca.sdk.HFCAClient;
+import org.hyperledger.fabric_ca.sdk.RegistrationRequest;
+
+public class RegisterUser {
+
+ public static void main(String[] args) throws Exception {
+
+ // Create a CA client for interacting with the CA.
+ Properties props = new Properties();
+ props.put("pemFile",
+ "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
+ props.put("allowAllHostNames", "true");
+ HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
+ CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
+ caClient.setCryptoSuite(cryptoSuite);
+
+ // Create a wallet for managing identities
+ Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));
+
+ // Check to see if we've already enrolled the user.
+ if (wallet.get("appUser") != null) {
+ System.out.println("An identity for the user \"appUser\" already exists in the wallet");
+ return;
+ }
+
+ X509Identity adminIdentity = (X509Identity)wallet.get("admin");
+ if (adminIdentity == null) {
+ System.out.println("\"admin\" needs to be enrolled and added to the wallet first");
+ return;
+ }
+ User admin = new User() {
+
+ @Override
+ public String getName() {
+ return "admin";
+ }
+
+ @Override
+ public Set getRoles() {
+ return null;
+ }
+
+ @Override
+ public String getAccount() {
+ return null;
+ }
+
+ @Override
+ public String getAffiliation() {
+ return "org1.department1";
+ }
+
+ @Override
+ public Enrollment getEnrollment() {
+ return new Enrollment() {
+
+ @Override
+ public PrivateKey getKey() {
+ return adminIdentity.getPrivateKey();
+ }
+
+ @Override
+ public String getCert() {
+ return Identities.toPemString(adminIdentity.getCertificate());
+ }
+ };
+ }
+
+ @Override
+ public String getMspId() {
+ return "Org1MSP";
+ }
+
+ };
+
+ // Register the user, enroll the user, and import the new identity into the wallet.
+ RegistrationRequest registrationRequest = new RegistrationRequest("appUser");
+ registrationRequest.setAffiliation("org1.department1");
+ registrationRequest.setEnrollmentID("appUser");
+ String enrollmentSecret = caClient.register(registrationRequest, admin);
+ Enrollment enrollment = caClient.enroll("appUser", enrollmentSecret);
+ Identity user = Identities.newX509Identity("Org1MSP", enrollment);
+ wallet.put("appUser", user);
+ System.out.println("Successfully enrolled user \"appUser\" and imported it into the wallet");
+ }
+
+}
diff --git a/asset-transfer-ledger-queries/application-java/src/main/resources/log4j.properties b/asset-transfer-ledger-queries/application-java/src/main/resources/log4j.properties
new file mode 100644
index 0000000..f1f841f
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-java/src/main/resources/log4j.properties
@@ -0,0 +1,19 @@
+# initialize root logger with level ERROR for stdout and fout
+log4j.rootLogger=ERROR,stdout,fout
+# set the log level for these components
+log4j.logger.com.endeca=INFO
+log4j.logger.com.endeca.itl.web.metrics=INFO
+
+# add a ConsoleAppender to the logger stdout to write to the console
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+# use a simple message format
+log4j.appender.stdout.layout.ConversionPattern=%m%n
+
+# add a FileAppender to the logger fout
+log4j.appender.fout=org.apache.log4j.FileAppender
+# create a log file
+log4j.appender.fout.File=crawl.log
+log4j.appender.fout.layout=org.apache.log4j.PatternLayout
+# use a more detailed message pattern
+log4j.appender.fout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n
diff --git a/asset-transfer-ledger-queries/application-javascript/.eslintignore b/asset-transfer-ledger-queries/application-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-ledger-queries/application-javascript/.eslintrc.js b/asset-transfer-ledger-queries/application-javascript/.eslintrc.js
new file mode 100644
index 0000000..6fa636b
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-javascript/.eslintrc.js
@@ -0,0 +1,36 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 'tab'],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/asset-transfer-ledger-queries/application-javascript/.gitignore b/asset-transfer-ledger-queries/application-javascript/.gitignore
new file mode 100644
index 0000000..21b287f
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-javascript/.gitignore
@@ -0,0 +1,14 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+package-lock.json
+
+wallet
+!wallet/.gitkeep
diff --git a/asset-transfer-ledger-queries/application-javascript/app.js b/asset-transfer-ledger-queries/application-javascript/app.js
new file mode 100644
index 0000000..8047f55
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-javascript/app.js
@@ -0,0 +1,244 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const FabricCAServices = require('fabric-ca-client');
+const path = require('path');
+const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
+const { buildCCPOrg1, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const channelName = 'mychannel';
+const chaincodeName = 'ledger';
+const mspOrg1 = 'Org1MSP';
+
+const walletPath = path.join(__dirname, 'wallet');
+const userId = 'appUser';
+
+function prettyJSONString(inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+}
+
+// pre-requisites:
+// - fabric-sample two organization test-network setup with two peers, ordering service,
+// and 2 certificate authorities, with the state database using couchdb
+// ===> from directory /fabric-samples/test-network
+// ./network.sh up createChannel -ca -s couchdb
+// - Use any of the asset-transfer-ledger-queries chaincodes deployed on the channel "mychannel"
+// with the chaincode name of "ledger". The following deploy command will package,
+// install, approve, and commit the javascript chaincode, all the actions it takes
+// to deploy a chaincode to a channel.
+// ===> from directory /fabric-samples/test-network
+// ./network.sh deployCC -ccn ledger -ccp ../asset-transfer-ledger-queries/chaincode-javascript/ -ccl javascript
+// - Be sure that node.js is installed
+// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript
+// node -v
+// - npm installed code dependencies
+// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript
+// npm install
+// - to run this test application
+// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript
+// node app.js
+
+// NOTE: If you see kind an error like these:
+/*
+ 2020-08-07T20:23:17.590Z - error: [DiscoveryService]: send[mychannel] - Channel:mychannel received discovery error:access denied
+ ******** FAILED to run the application: Error: DiscoveryService: mychannel error: access denied
+
+ OR
+
+ Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
+ ******** FAILED to run the application: Error: Identity not found in wallet: appUser
+*/
+// Delete the /fabric-samples/asset-transfer-ledger-queries/application-javascript/wallet directory
+// and retry this application.
+//
+// The certificate authority must have been restarted and the saved certificates for the
+// admin and application user are not valid. Deleting the wallet store will force these to be reset
+// with the new certificate authority.
+//
+
+/**
+ * A test application to show ledger queries operations with any of the asset-transfer-ledger-queries chaincodes
+ * -- How to submit a transaction
+ * -- How to query and check the results
+ *
+ * To see the SDK workings, try setting the logging to show on the console before running
+ * export HFC_LOGGING='{"debug":"console"}'
+ */
+async function main() {
+ let skipInit = false;
+ if (process.argv.length > 2) {
+ if (process.argv[2] === 'skipInit') {
+ skipInit = true;
+ }
+ }
+
+ try {
+ // build an in memory object with the network configuration (also known as a connection profile)
+ const ccp = buildCCPOrg1();
+
+ // build an instance of the fabric ca services client based on
+ // the information in the network configuration
+ const caClient = buildCAClient(FabricCAServices, ccp, 'ca.org1.example.com');
+
+ // setup the wallet to hold the credentials of the application user
+ const wallet = await buildWallet(Wallets, walletPath);
+
+ // in a real application this would be done on an administrative flow, and only once
+ await enrollAdmin(caClient, wallet, mspOrg1);
+
+ // in a real application this would be done only when a new user was required to be added
+ // and would be part of an administrative flow
+ await registerAndEnrollUser(caClient, wallet, mspOrg1, userId, 'org1.department1');
+
+ // Create a new gateway instance for interacting with the fabric network.
+ // In a real application this would be done as the backend server session is setup for
+ // a user that has been verified.
+ const gateway = new Gateway();
+
+ try {
+ // setup the gateway instance
+ // The user will now be able to create connections to the fabric network and be able to
+ // submit transactions and query. All transactions submitted by this gateway will be
+ // signed by this user using the credentials stored in the wallet.
+ await gateway.connect(ccp, {
+ wallet,
+ identity: userId,
+ discovery: { enabled: true, asLocalhost: true } // using asLocalhost as this gateway is using a fabric network deployed locally
+ });
+
+ // Build a network instance based on the channel where the smart contract is deployed
+ const network = await gateway.getNetwork(channelName);
+
+ // Get the contract from the network.
+ const contract = network.getContract(chaincodeName);
+
+ // Initialize a set of asset data on the channel using the chaincode 'InitLedger' function.
+ // This type of transaction would only be run once by an application the first time it was started after it
+ // deployed the first time. Any updates to the chaincode deployed later would likely not need to run
+ // an "init" type function.
+ if (!skipInit) {
+ try {
+ console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger');
+ await contract.submitTransaction('InitLedger');
+ console.log('*** Result: committed');
+ } catch (initError) {
+ // this is error is OK if we are rerunning this app without restarting
+ console.log(`******** initLedger failed :: ${initError}`)
+ }
+ } else {
+ console.log('*** not executing "InitLedger');
+ }
+
+ let result;
+
+ // Let's try a query operation (function).
+ // This will be sent to just one peer and the results will be shown.
+ console.log('\n--> Evaluate Transaction: GetAssetsByRange, function returns assets in a specific range from asset1 to before asset6');
+ result = await contract.evaluateTransaction('GetAssetsByRange', 'asset1', 'asset6');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an open start and open end range to return assest1 to asset6');
+ result = await contract.evaluateTransaction('GetAssetsByRange', '', '');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an fixed start (asset3) and open end range to return assest3 to asset6');
+ result = await contract.evaluateTransaction('GetAssetsByRange', 'asset3', '');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an open start and fixed end (asset3) range to return assest1 to asset2');
+ result = await contract.evaluateTransaction('GetAssetsByRange', '', 'asset3');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Now let's try to submit a transaction.
+ // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent
+ // to the orderer to be committed by each of the peer's to the channel ledger.
+ console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID(asset7), color(yellow), size(5), owner(Tom), and appraisedValue(1300) arguments');
+ await contract.submitTransaction('CreateAsset', 'asset7', 'yellow', '5', 'Tom', '1300');
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: ReadAsset, function returns information about an asset with ID(asset7)');
+ result = await contract.evaluateTransaction('ReadAsset', 'asset7');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with ID(asset7) exist');
+ result = await contract.evaluateTransaction('AssetExists', 'asset7');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Now let's try to submit a transaction that deletes an asset
+ // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent
+ // to the orderer to be committed by each of the peer's to the channel ledger.
+ console.log('\n--> Submit Transaction: DeleteAsset with ID(asset7)');
+ await contract.submitTransaction('DeleteAsset', 'asset7');
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: AssetExists, function returns "false" if an asset with ID(asset7) does not exist');
+ result = await contract.evaluateTransaction('AssetExists', 'asset7');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`)
+
+ console.log('\n--> Submit Transaction: TransferAsset, transfer asset(asset2) to new owner(Tom)');
+ await contract.submitTransaction('TransferAsset', 'asset2', 'Tom');
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: ReadAsset, function returns information about an asset with ID(asset2)');
+ result = await contract.evaluateTransaction('ReadAsset', 'asset2');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Rich Query with Pagination (Only supported if CouchDB is used as state database)
+ console.log('\n--> Evaluate Transaction: QueryAssetsWithPagination, function returns "Tom" assets');
+ result = await contract.evaluateTransaction('QueryAssetsWithPagination', '{"selector":{"docType":"asset","owner":"Tom"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}', '3', '');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Submit Transaction: TransferAssetByColor, transfer all yellow assets to new owner(Michel)');
+ await contract.submitTransaction('TransferAssetByColor', 'yellow', 'Michel');
+ console.log('*** Result: committed');
+
+ // Rich Query (Only supported if CouchDB is used as state database):
+ console.log('\n--> Evaluate Transaction: QueryAssetsByOwner, find all assets with owner(Michel)');
+ result = await contract.evaluateTransaction('QueryAssetsByOwner', 'Michel');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('\n--> Evaluate Transaction: GetAssetHistory, get the history of an asset(asset7)');
+ result = await contract.evaluateTransaction('GetAssetHistory', 'asset7');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Rich Query (Only supported if CouchDB is used as state database):
+ console.log('\n--> Evaluate Transaction: QueryAssets, assets of size 15');
+ result = await contract.evaluateTransaction('QueryAssets', '{"selector":{"size":15}}');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
+ console.log('\n--> Evaluate Transaction: QueryAssets, Jin Soo\'s assets');
+ result = await contract.evaluateTransaction('QueryAssets', '{"selector":{"docType":"asset","owner":"Jin Soo"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Rich Query with Pagination (Only supported if CouchDB is used as state database)
+ console.log('\n--> Evaluate Transaction: GetAssetsByRangeWithPagination - get page 1 of assets from asset3 to asset6 (asset3, asset4)');
+ result = await contract.evaluateTransaction('GetAssetsByRangeWithPagination', 'asset3', 'asset6', '2', '');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ // Rich Query with Pagination (Only supported if CouchDB is used as state database)
+ console.log('\n--> Evaluate Transaction: GetAssetsByRangeWithPagination - get page 2 of assets from asset3 to asset6 (asset4, asset5)');
+ result = await contract.evaluateTransaction('GetAssetsByRangeWithPagination', 'asset3', 'asset6', '2', 'asset4');
+ console.log(`*** Result: ${prettyJSONString(result.toString())}`);
+
+ console.log('*** all tests completed');
+ } finally {
+ // Disconnect from the gateway when the application is closing
+ // This will close all connections to the network
+ gateway.disconnect();
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ }
+
+ console.log('*** application ending');
+
+}
+
+main();
diff --git a/asset-transfer-ledger-queries/application-javascript/package.json b/asset-transfer-ledger-queries/application-javascript/package.json
new file mode 100644
index 0000000..f06a831
--- /dev/null
+++ b/asset-transfer-ledger-queries/application-javascript/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "asset-transfer-ledger-queries",
+ "version": "1.0.0",
+ "description": "Asset transfer ledger queries application implemented in JavaScript",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.4",
+ "fabric-network": "^2.2.4"
+ }
+}
diff --git a/asset-transfer-ledger-queries/chaincode-go/META-INF/statedb/couchdb/indexes/indexOwner.json b/asset-transfer-ledger-queries/chaincode-go/META-INF/statedb/couchdb/indexes/indexOwner.json
new file mode 100644
index 0000000..305f090
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-go/META-INF/statedb/couchdb/indexes/indexOwner.json
@@ -0,0 +1 @@
+{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
diff --git a/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go b/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go
new file mode 100644
index 0000000..44cbdb1
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go
@@ -0,0 +1,466 @@
+/*
+ SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+====CHAINCODE EXECUTION SAMPLES (CLI) ==================
+
+==== Invoke assets ====
+peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset1","blue","5","tom","35"]}'
+peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset2","red","4","tom","50"]}'
+peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["CreateAsset","asset3","blue","6","tom","70"]}'
+peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["TransferAsset","asset2","jerry"]}'
+peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["TransferAssetByColor","blue","jerry"]}'
+peer chaincode invoke -C myc1 -n asset_transfer -c '{"Args":["DeleteAsset","asset1"]}'
+
+==== Query assets ====
+peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["ReadAsset","asset1"]}'
+peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["GetAssetsByRange","asset1","asset3"]}'
+peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["GetAssetHistory","asset1"]}'
+
+Rich Query (Only supported if CouchDB is used as state database):
+peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssetsByOwner","tom"]}'
+peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"owner\":\"tom\"}}"]}'
+
+Rich Query with Pagination (Only supported if CouchDB is used as state database):
+peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssetsWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}'
+
+INDEXES TO SUPPORT COUCHDB RICH QUERIES
+
+Indexes in CouchDB are required in order to make JSON queries efficient and are required for
+any JSON query with a sort. Indexes may be packaged alongside
+chaincode in a META-INF/statedb/couchdb/indexes directory. Each index must be defined in its own
+text file with extension *.json with the index definition formatted in JSON following the
+CouchDB index JSON syntax as documented at:
+http://docs.couchdb.org/en/2.3.1/api/database/find.html#db-index
+
+This asset transfer ledger example chaincode demonstrates a packaged
+index which you can find in META-INF/statedb/couchdb/indexes/indexOwner.json.
+
+If you have access to the your peer's CouchDB state database in a development environment,
+you may want to iteratively test various indexes in support of your chaincode queries. You
+can use the CouchDB Fauxton interface or a command line curl utility to create and update
+indexes. Then once you finalize an index, include the index definition alongside your
+chaincode in the META-INF/statedb/couchdb/indexes directory, for packaging and deployment
+to managed environments.
+
+In the examples below you can find index definitions that support asset transfer ledger
+chaincode queries, along with the syntax that you can use in development environments
+to create the indexes in the CouchDB Fauxton interface or a curl command line utility.
+
+
+Index for docType, owner.
+
+Example curl command line to define index in the CouchDB channel_chaincode database
+curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"docType\",\"owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index
+
+
+Index for docType, owner, size (descending order).
+
+Example curl command line to define index in the CouchDB channel_chaincode database:
+curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index
+
+Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
+peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
+
+Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
+peer chaincode query -C myc1 -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'
+*/
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/golang/protobuf/ptypes"
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+const index = "color~name"
+
+// SimpleChaincode implements the fabric-contract-api-go programming model
+type SimpleChaincode struct {
+ contractapi.Contract
+}
+
+type Asset struct {
+ DocType string `json:"docType"` //docType is used to distinguish the various types of objects in state database
+ ID string `json:"ID"` //the field tags are needed to keep case from bouncing around
+ Color string `json:"color"`
+ Size int `json:"size"`
+ Owner string `json:"owner"`
+ AppraisedValue int `json:"appraisedValue"`
+}
+
+// HistoryQueryResult structure used for returning result of history query
+type HistoryQueryResult struct {
+ Record *Asset `json:"record"`
+ TxId string `json:"txId"`
+ Timestamp time.Time `json:"timestamp"`
+ IsDelete bool `json:"isDelete"`
+}
+
+// PaginatedQueryResult structure used for returning paginated query results and metadata
+type PaginatedQueryResult struct {
+ Records []*Asset `json:"records"`
+ FetchedRecordsCount int32 `json:"fetchedRecordsCount"`
+ Bookmark string `json:"bookmark"`
+}
+
+// CreateAsset initializes a new asset in the ledger
+func (t *SimpleChaincode) CreateAsset(ctx contractapi.TransactionContextInterface, assetID, color string, size int, owner string, appraisedValue int) error {
+ exists, err := t.AssetExists(ctx, assetID)
+ if err != nil {
+ return fmt.Errorf("failed to get asset: %v", err)
+ }
+ if exists {
+ return fmt.Errorf("asset already exists: %s", assetID)
+ }
+
+ asset := &Asset{
+ DocType: "asset",
+ ID: assetID,
+ Color: color,
+ Size: size,
+ Owner: owner,
+ AppraisedValue: appraisedValue,
+ }
+ assetBytes, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ err = ctx.GetStub().PutState(assetID, assetBytes)
+ if err != nil {
+ return err
+ }
+
+ // Create an index to enable color-based range queries, e.g. return all blue assets.
+ // An 'index' is a normal key-value entry in the ledger.
+ // The key is a composite key, with the elements that you want to range query on listed first.
+ // In our case, the composite key is based on indexName~color~name.
+ // This will enable very efficient state range queries based on composite keys matching indexName~color~*
+ colorNameIndexKey, err := ctx.GetStub().CreateCompositeKey(index, []string{asset.Color, asset.ID})
+ if err != nil {
+ return err
+ }
+ // Save index entry to world state. Only the key name is needed, no need to store a duplicate copy of the asset.
+ // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value
+ value := []byte{0x00}
+ return ctx.GetStub().PutState(colorNameIndexKey, value)
+}
+
+// ReadAsset retrieves an asset from the ledger
+func (t *SimpleChaincode) ReadAsset(ctx contractapi.TransactionContextInterface, assetID string) (*Asset, error) {
+ assetBytes, err := ctx.GetStub().GetState(assetID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get asset %s: %v", assetID, err)
+ }
+ if assetBytes == nil {
+ return nil, fmt.Errorf("asset %s does not exist", assetID)
+ }
+
+ var asset Asset
+ err = json.Unmarshal(assetBytes, &asset)
+ if err != nil {
+ return nil, err
+ }
+
+ return &asset, nil
+}
+
+// DeleteAsset removes an asset key-value pair from the ledger
+func (t *SimpleChaincode) DeleteAsset(ctx contractapi.TransactionContextInterface, assetID string) error {
+ asset, err := t.ReadAsset(ctx, assetID)
+ if err != nil {
+ return err
+ }
+
+ err = ctx.GetStub().DelState(assetID)
+ if err != nil {
+ return fmt.Errorf("failed to delete asset %s: %v", assetID, err)
+ }
+
+ colorNameIndexKey, err := ctx.GetStub().CreateCompositeKey(index, []string{asset.Color, asset.ID})
+ if err != nil {
+ return err
+ }
+
+ // Delete index entry
+ return ctx.GetStub().DelState(colorNameIndexKey)
+}
+
+// TransferAsset transfers an asset by setting a new owner name on the asset
+func (t *SimpleChaincode) TransferAsset(ctx contractapi.TransactionContextInterface, assetID, newOwner string) error {
+ asset, err := t.ReadAsset(ctx, assetID)
+ if err != nil {
+ return err
+ }
+
+ asset.Owner = newOwner
+ assetBytes, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ return ctx.GetStub().PutState(assetID, assetBytes)
+}
+
+// constructQueryResponseFromIterator constructs a slice of assets from the resultsIterator
+func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) ([]*Asset, error) {
+ var assets []*Asset
+ for resultsIterator.HasNext() {
+ queryResult, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+ var asset Asset
+ err = json.Unmarshal(queryResult.Value, &asset)
+ if err != nil {
+ return nil, err
+ }
+ assets = append(assets, &asset)
+ }
+
+ return assets, nil
+}
+
+// GetAssetsByRange performs a range query based on the start and end keys provided.
+// Read-only function results are not typically submitted to ordering. If the read-only
+// results are submitted to ordering, or if the query is used in an update transaction
+// and submitted to ordering, then the committing peers will re-execute to guarantee that
+// result sets are stable between endorsement time and commit time. The transaction is
+// invalidated by the committing peers if the result set has changed between endorsement
+// time and commit time.
+// Therefore, range queries are a safe option for performing update transactions based on query results.
+func (t *SimpleChaincode) GetAssetsByRange(ctx contractapi.TransactionContextInterface, startKey, endKey string) ([]*Asset, error) {
+ resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ return constructQueryResponseFromIterator(resultsIterator)
+}
+
+// TransferAssetByColor will transfer assets of a given color to a certain new owner.
+// Uses GetStateByPartialCompositeKey (range query) against color~name 'index'.
+// Committing peers will re-execute range queries to guarantee that result sets are stable
+// between endorsement time and commit time. The transaction is invalidated by the
+// committing peers if the result set has changed between endorsement time and commit time.
+// Therefore, range queries are a safe option for performing update transactions based on query results.
+// Example: GetStateByPartialCompositeKey/RangeQuery
+func (t *SimpleChaincode) TransferAssetByColor(ctx contractapi.TransactionContextInterface, color, newOwner string) error {
+ // Execute a key range query on all keys starting with 'color'
+ coloredAssetResultsIterator, err := ctx.GetStub().GetStateByPartialCompositeKey(index, []string{color})
+ if err != nil {
+ return err
+ }
+ defer coloredAssetResultsIterator.Close()
+
+ for coloredAssetResultsIterator.HasNext() {
+ responseRange, err := coloredAssetResultsIterator.Next()
+ if err != nil {
+ return err
+ }
+
+ _, compositeKeyParts, err := ctx.GetStub().SplitCompositeKey(responseRange.Key)
+ if err != nil {
+ return err
+ }
+
+ if len(compositeKeyParts) > 1 {
+ returnedAssetID := compositeKeyParts[1]
+ asset, err := t.ReadAsset(ctx, returnedAssetID)
+ if err != nil {
+ return err
+ }
+ asset.Owner = newOwner
+ assetBytes, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+ err = ctx.GetStub().PutState(returnedAssetID, assetBytes)
+ if err != nil {
+ return fmt.Errorf("transfer failed for asset %s: %v", returnedAssetID, err)
+ }
+ }
+ }
+
+ return nil
+}
+
+// QueryAssetsByOwner queries for assets based on the owners name.
+// This is an example of a parameterized query where the query logic is baked into the chaincode,
+// and accepting a single query parameter (owner).
+// Only available on state databases that support rich query (e.g. CouchDB)
+// Example: Parameterized rich query
+func (t *SimpleChaincode) QueryAssetsByOwner(ctx contractapi.TransactionContextInterface, owner string) ([]*Asset, error) {
+ queryString := fmt.Sprintf(`{"selector":{"docType":"asset","owner":"%s"}}`, owner)
+ return getQueryResultForQueryString(ctx, queryString)
+}
+
+// QueryAssets uses a query string to perform a query for assets.
+// Query string matching state database syntax is passed in and executed as is.
+// Supports ad hoc queries that can be defined at runtime by the client.
+// If this is not desired, follow the QueryAssetsForOwner example for parameterized queries.
+// Only available on state databases that support rich query (e.g. CouchDB)
+// Example: Ad hoc rich query
+func (t *SimpleChaincode) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) {
+ return getQueryResultForQueryString(ctx, queryString)
+}
+
+// getQueryResultForQueryString executes the passed in query string.
+// The result set is built and returned as a byte array containing the JSON results.
+func getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) {
+ resultsIterator, err := ctx.GetStub().GetQueryResult(queryString)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ return constructQueryResponseFromIterator(resultsIterator)
+}
+
+// GetAssetsByRangeWithPagination performs a range query based on the start and end key,
+// page size and a bookmark.
+// The number of fetched records will be equal to or lesser than the page size.
+// Paginated range queries are only valid for read only transactions.
+// Example: Pagination with Range Query
+func (t *SimpleChaincode) GetAssetsByRangeWithPagination(ctx contractapi.TransactionContextInterface, startKey string, endKey string, pageSize int, bookmark string) ([]*Asset, error) {
+
+ resultsIterator, _, err := ctx.GetStub().GetStateByRangeWithPagination(startKey, endKey, int32(pageSize), bookmark)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ return constructQueryResponseFromIterator(resultsIterator)
+}
+
+// QueryAssetsWithPagination uses a query string, page size and a bookmark to perform a query
+// for assets. Query string matching state database syntax is passed in and executed as is.
+// The number of fetched records would be equal to or lesser than the specified page size.
+// Supports ad hoc queries that can be defined at runtime by the client.
+// If this is not desired, follow the QueryAssetsForOwner example for parameterized queries.
+// Only available on state databases that support rich query (e.g. CouchDB)
+// Paginated queries are only valid for read only transactions.
+// Example: Pagination with Ad hoc Rich Query
+func (t *SimpleChaincode) QueryAssetsWithPagination(ctx contractapi.TransactionContextInterface, queryString string, pageSize int, bookmark string) (*PaginatedQueryResult, error) {
+
+ return getQueryResultForQueryStringWithPagination(ctx, queryString, int32(pageSize), bookmark)
+}
+
+// getQueryResultForQueryStringWithPagination executes the passed in query string with
+// pagination info. The result set is built and returned as a byte array containing the JSON results.
+func getQueryResultForQueryStringWithPagination(ctx contractapi.TransactionContextInterface, queryString string, pageSize int32, bookmark string) (*PaginatedQueryResult, error) {
+
+ resultsIterator, responseMetadata, err := ctx.GetStub().GetQueryResultWithPagination(queryString, pageSize, bookmark)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ assets, err := constructQueryResponseFromIterator(resultsIterator)
+ if err != nil {
+ return nil, err
+ }
+
+ return &PaginatedQueryResult{
+ Records: assets,
+ FetchedRecordsCount: responseMetadata.FetchedRecordsCount,
+ Bookmark: responseMetadata.Bookmark,
+ }, nil
+}
+
+// GetAssetHistory returns the chain of custody for an asset since issuance.
+func (t *SimpleChaincode) GetAssetHistory(ctx contractapi.TransactionContextInterface, assetID string) ([]HistoryQueryResult, error) {
+ log.Printf("GetAssetHistory: ID %v", assetID)
+
+ resultsIterator, err := ctx.GetStub().GetHistoryForKey(assetID)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ var records []HistoryQueryResult
+ for resultsIterator.HasNext() {
+ response, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ var asset Asset
+ if len(response.Value) > 0 {
+ err = json.Unmarshal(response.Value, &asset)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ asset = Asset{
+ ID: assetID,
+ }
+ }
+
+ timestamp, err := ptypes.Timestamp(response.Timestamp)
+ if err != nil {
+ return nil, err
+ }
+
+ record := HistoryQueryResult{
+ TxId: response.TxId,
+ Timestamp: timestamp,
+ Record: &asset,
+ IsDelete: response.IsDelete,
+ }
+ records = append(records, record)
+ }
+
+ return records, nil
+}
+
+// AssetExists returns true when asset with given ID exists in the ledger.
+func (t *SimpleChaincode) AssetExists(ctx contractapi.TransactionContextInterface, assetID string) (bool, error) {
+ assetBytes, err := ctx.GetStub().GetState(assetID)
+ if err != nil {
+ return false, fmt.Errorf("failed to read asset %s from world state. %v", assetID, err)
+ }
+
+ return assetBytes != nil, nil
+}
+
+// InitLedger creates the initial set of assets in the ledger.
+func (t *SimpleChaincode) InitLedger(ctx contractapi.TransactionContextInterface) error {
+ assets := []Asset{
+ {DocType: "asset", ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
+ {DocType: "asset", ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
+ {DocType: "asset", ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
+ {DocType: "asset", ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
+ {DocType: "asset", ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
+ {DocType: "asset", ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
+ }
+
+ for _, asset := range assets {
+ err := t.CreateAsset(ctx, asset.ID, asset.Color, asset.Size, asset.Owner, asset.AppraisedValue)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func main() {
+ chaincode, err := contractapi.NewChaincode(&SimpleChaincode{})
+ if err != nil {
+ log.Panicf("Error creating asset chaincode: %v", err)
+ }
+
+ if err := chaincode.Start(); err != nil {
+ log.Panicf("Error starting asset chaincode: %v", err)
+ }
+}
diff --git a/asset-transfer-ledger-queries/chaincode-go/go.mod b/asset-transfer-ledger-queries/chaincode-go/go.mod
new file mode 100644
index 0000000..fc82521
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-go/go.mod
@@ -0,0 +1,9 @@
+module github.com/hyperledger/fabric-samples/asset-transfer-ledger-queries/chaincode-go
+
+go 1.14
+
+require (
+ github.com/golang/protobuf v1.3.2
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a
+ github.com/hyperledger/fabric-contract-api-go v1.1.0
+)
diff --git a/asset-transfer-ledger-queries/chaincode-go/go.sum b/asset-transfer-ledger-queries/chaincode-go/go.sum
new file mode 100644
index 0000000..1c4ebdf
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-go/go.sum
@@ -0,0 +1,146 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a h1:KoFw2HnRfW+EItMP0zvUUl1FGzDb/7O0ov7uXZffQok=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/asset-transfer-ledger-queries/chaincode-javascript/.eslintignore b/asset-transfer-ledger-queries/chaincode-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-ledger-queries/chaincode-javascript/.eslintrc.js b/asset-transfer-ledger-queries/chaincode-javascript/.eslintrc.js
new file mode 100644
index 0000000..072edaf
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-javascript/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+'use strict';
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: 'eslint:recommended',
+ rules: {
+ indent: ['error', 'tab'],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/asset-transfer-ledger-queries/chaincode-javascript/index.js b/asset-transfer-ledger-queries/chaincode-javascript/index.js
new file mode 100644
index 0000000..5c7b6e0
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-javascript/index.js
@@ -0,0 +1,13 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const CC = require('./lib/asset_transfer_ledger_chaincode.js');
+
+module.exports.CC = CC;
+module.exports.contracts = [ CC ];
diff --git a/asset-transfer-ledger-queries/chaincode-javascript/lib/META-INF/statedb/couchdb/indexes/indexOwner.json b/asset-transfer-ledger-queries/chaincode-javascript/lib/META-INF/statedb/couchdb/indexes/indexOwner.json
new file mode 100644
index 0000000..305f090
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-javascript/lib/META-INF/statedb/couchdb/indexes/indexOwner.json
@@ -0,0 +1 @@
+{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
diff --git a/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js b/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js
new file mode 100644
index 0000000..31174f4
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js
@@ -0,0 +1,402 @@
+/*
+ SPDX-License-Identifier: Apache-2.0
+*/
+
+// ====CHAINCODE EXECUTION SAMPLES (CLI) ==================
+
+// ==== Invoke assets ====
+// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset1","blue","35","Tom","100"]}'
+// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset2","red","50","Tom","150"]}'
+// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset3","blue","70","Tom","200"]}'
+// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAsset","asset2","jerry"]}'
+// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAssetsBasedOnColor","blue","jerry"]}'
+// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["DeleteAsset","asset1"]}'
+
+// ==== Query assets ====
+// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["ReadAsset","asset1"]}'
+// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["GetAssetsByRange","asset1","asset3"]}'
+// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["GetAssetHistory","asset1"]}'
+
+// Rich Query (Only supported if CouchDB is used as state database):
+// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssetsByOwner","Tom"]}' output issue
+// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"owner\":\"Tom\"}}"]}'
+
+// Rich Query with Pagination (Only supported if CouchDB is used as state database):
+// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssetsWithPagination","{\"selector\":{\"owner\":\"Tom\"}}","3",""]}'
+
+// INDEXES TO SUPPORT COUCHDB RICH QUERIES
+//
+// Indexes in CouchDB are required in order to make JSON queries efficient and are required for
+// any JSON query with a sort. Indexes may be packaged alongside
+// chaincode in a META-INF/statedb/couchdb/indexes directory. Each index must be defined in its own
+// text file with extension *.json with the index definition formatted in JSON following the
+// CouchDB index JSON syntax as documented at:
+// http://docs.couchdb.org/en/2.3.1/api/database/find.html#db-index
+//
+// This asset transfer ledger example chaincode demonstrates a packaged
+// index which you can find in META-INF/statedb/couchdb/indexes/indexOwner.json.
+//
+// If you have access to the your peer's CouchDB state database in a development environment,
+// you may want to iteratively test various indexes in support of your chaincode queries. You
+// can use the CouchDB Fauxton interface or a command line curl utility to create and update
+// indexes. Then once you finalize an index, include the index definition alongside your
+// chaincode in the META-INF/statedb/couchdb/indexes directory, for packaging and deployment
+// to managed environments.
+//
+// In the examples below you can find index definitions that support asset transfer ledger
+// chaincode queries, along with the syntax that you can use in development environments
+// to create the indexes in the CouchDB Fauxton interface or a curl command line utility.
+//
+
+// Index for docType, owner.
+//
+// Example curl command line to define index in the CouchDB channel_chaincode database
+// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"docType\",\"owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index
+//
+
+// Index for docType, owner, size (descending order).
+//
+// Example curl command line to define index in the CouchDB channel_chaincode database
+// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index
+
+// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
+// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
+
+// Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
+// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"Tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'
+
+'use strict';
+
+const {Contract} = require('fabric-contract-api');
+
+class Chaincode extends Contract {
+
+ // CreateAsset - create a new asset, store into chaincode state
+ async CreateAsset(ctx, assetID, color, size, owner, appraisedValue) {
+ const exists = await this.AssetExists(ctx, assetID);
+ if (exists) {
+ throw new Error(`The asset ${assetID} already exists`);
+ }
+
+ // ==== Create asset object and marshal to JSON ====
+ let asset = {
+ docType: 'asset',
+ assetID: assetID,
+ color: color,
+ size: size,
+ owner: owner,
+ appraisedValue: appraisedValue
+ };
+
+
+ // === Save asset to state ===
+ await ctx.stub.putState(assetID, Buffer.from(JSON.stringify(asset)));
+ let indexName = 'color~name';
+ let colorNameIndexKey = await ctx.stub.createCompositeKey(indexName, [asset.color, asset.assetID]);
+
+ // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble.
+ // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value
+ await ctx.stub.putState(colorNameIndexKey, Buffer.from('\u0000'));
+ }
+
+ // ReadAsset returns the asset stored in the world state with given id.
+ async ReadAsset(ctx, id) {
+ const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state
+ if (!assetJSON || assetJSON.length === 0) {
+ throw new Error(`Asset ${id} does not exist`);
+ }
+
+ return assetJSON.toString();
+ }
+
+ // delete - remove a asset key/value pair from state
+ async DeleteAsset(ctx, id) {
+ if (!id) {
+ throw new Error('Asset name must not be empty');
+ }
+
+ let exists = await this.AssetExists(ctx, id);
+ if (!exists) {
+ throw new Error(`Asset ${id} does not exist`);
+ }
+
+ // to maintain the color~name index, we need to read the asset first and get its color
+ let valAsbytes = await ctx.stub.getState(id); // get the asset from chaincode state
+ let jsonResp = {};
+ if (!valAsbytes) {
+ jsonResp.error = `Asset does not exist: ${id}`;
+ throw new Error(jsonResp);
+ }
+ let assetJSON;
+ try {
+ assetJSON = JSON.parse(valAsbytes.toString());
+ } catch (err) {
+ jsonResp = {};
+ jsonResp.error = `Failed to decode JSON of: ${id}`;
+ throw new Error(jsonResp);
+ }
+ await ctx.stub.deleteState(id); //remove the asset from chaincode state
+
+ // delete the index
+ let indexName = 'color~name';
+ let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.assetID]);
+ if (!colorNameIndexKey) {
+ throw new Error(' Failed to create the createCompositeKey');
+ }
+ // Delete index entry to state.
+ await ctx.stub.deleteState(colorNameIndexKey);
+ }
+
+ // TransferAsset transfers a asset by setting a new owner name on the asset
+ async TransferAsset(ctx, assetName, newOwner) {
+
+ let assetAsBytes = await ctx.stub.getState(assetName);
+ if (!assetAsBytes || !assetAsBytes.toString()) {
+ throw new Error(`Asset ${assetName} does not exist`);
+ }
+ let assetToTransfer = {};
+ try {
+ assetToTransfer = JSON.parse(assetAsBytes.toString()); //unmarshal
+ } catch (err) {
+ let jsonResp = {};
+ jsonResp.error = 'Failed to decode JSON of: ' + assetName;
+ throw new Error(jsonResp);
+ }
+ assetToTransfer.owner = newOwner; //change the owner
+
+ let assetJSONasBytes = Buffer.from(JSON.stringify(assetToTransfer));
+ await ctx.stub.putState(assetName, assetJSONasBytes); //rewrite the asset
+ }
+
+ // GetAssetsByRange performs a range query based on the start and end keys provided.
+ // Read-only function results are not typically submitted to ordering. If the read-only
+ // results are submitted to ordering, or if the query is used in an update transaction
+ // and submitted to ordering, then the committing peers will re-execute to guarantee that
+ // result sets are stable between endorsement time and commit time. The transaction is
+ // invalidated by the committing peers if the result set has changed between endorsement
+ // time and commit time.
+ // Therefore, range queries are a safe option for performing update transactions based on query results.
+ async GetAssetsByRange(ctx, startKey, endKey) {
+
+ let resultsIterator = await ctx.stub.getStateByRange(startKey, endKey);
+ let results = await this.GetAllResults(resultsIterator, false);
+
+ return JSON.stringify(results);
+ }
+
+ // TransferAssetBasedOnColor will transfer assets of a given color to a certain new owner.
+ // Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'.
+ // Committing peers will re-execute range queries to guarantee that result sets are stable
+ // between endorsement time and commit time. The transaction is invalidated by the
+ // committing peers if the result set has changed between endorsement time and commit time.
+ // Therefore, range queries are a safe option for performing update transactions based on query results.
+ // Example: GetStateByPartialCompositeKey/RangeQuery
+ async TransferAssetByColor(ctx, color, newOwner) {
+ // Query the color~name index by color
+ // This will execute a key range query on all keys starting with 'color'
+ let coloredAssetResultsIterator = await ctx.stub.getStateByPartialCompositeKey('color~name', [color]);
+
+ // Iterate through result set and for each asset found, transfer to newOwner
+ let responseRange = await coloredAssetResultsIterator.next();
+ while (!responseRange.done) {
+ if (!responseRange || !responseRange.value || !responseRange.value.key) {
+ return;
+ }
+
+ let objectType;
+ let attributes;
+ (
+ {objectType, attributes} = await ctx.stub.splitCompositeKey(responseRange.value.key)
+ );
+
+ console.log(objectType);
+ let returnedAssetName = attributes[1];
+
+ // Now call the transfer function for the found asset.
+ // Re-use the same function that is used to transfer individual assets
+ await this.TransferAsset(ctx, returnedAssetName, newOwner);
+ responseRange = await coloredAssetResultsIterator.next();
+ }
+ }
+
+ // QueryAssetsByOwner queries for assets based on a passed in owner.
+ // This is an example of a parameterized query where the query logic is baked into the chaincode,
+ // and accepting a single query parameter (owner).
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ // Example: Parameterized rich query
+ async QueryAssetsByOwner(ctx, owner) {
+ let queryString = {};
+ queryString.selector = {};
+ queryString.selector.docType = 'asset';
+ queryString.selector.owner = owner;
+ return await this.GetQueryResultForQueryString(ctx, JSON.stringify(queryString)); //shim.success(queryResults);
+ }
+
+ // Example: Ad hoc rich query
+ // QueryAssets uses a query string to perform a query for assets.
+ // Query string matching state database syntax is passed in and executed as is.
+ // Supports ad hoc queries that can be defined at runtime by the client.
+ // If this is not desired, follow the QueryAssetsForOwner example for parameterized queries.
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ async QueryAssets(ctx, queryString) {
+ return await this.GetQueryResultForQueryString(ctx, queryString);
+ }
+
+ // GetQueryResultForQueryString executes the passed in query string.
+ // Result set is built and returned as a byte array containing the JSON results.
+ async GetQueryResultForQueryString(ctx, queryString) {
+
+ let resultsIterator = await ctx.stub.getQueryResult(queryString);
+ let results = await this.GetAllResults(resultsIterator, false);
+
+ return JSON.stringify(results);
+ }
+
+ // Example: Pagination with Range Query
+ // GetAssetsByRangeWithPagination performs a range query based on the start & end key,
+ // page size and a bookmark.
+ // The number of fetched records will be equal to or lesser than the page size.
+ // Paginated range queries are only valid for read only transactions.
+ async GetAssetsByRangeWithPagination(ctx, startKey, endKey, pageSize, bookmark) {
+
+ const {iterator, metadata} = await ctx.stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark);
+ const results = await this.GetAllResults(iterator, false);
+
+ results.ResponseMetadata = {
+ RecordsCount: metadata.fetched_records_count,
+ Bookmark: metadata.bookmark,
+ };
+ return JSON.stringify(results);
+ }
+
+ // Example: Pagination with Ad hoc Rich Query
+ // QueryAssetsWithPagination uses a query string, page size and a bookmark to perform a query
+ // for assets. Query string matching state database syntax is passed in and executed as is.
+ // The number of fetched records would be equal to or lesser than the specified page size.
+ // Supports ad hoc queries that can be defined at runtime by the client.
+ // If this is not desired, follow the QueryAssetsForOwner example for parameterized queries.
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ // Paginated queries are only valid for read only transactions.
+ async QueryAssetsWithPagination(ctx, queryString, pageSize, bookmark) {
+
+ const {iterator, metadata} = await ctx.stub.getQueryResultWithPagination(queryString, pageSize, bookmark);
+ const results = await this.GetAllResults(iterator, false);
+
+ results.ResponseMetadata = {
+ RecordsCount: metadata.fetched_records_count,
+ Bookmark: metadata.bookmark,
+ };
+
+ return JSON.stringify(results);
+ }
+
+ // GetAssetHistory returns the chain of custody for an asset since issuance.
+ async GetAssetHistory(ctx, assetName) {
+
+ let resultsIterator = await ctx.stub.getHistoryForKey(assetName);
+ let results = await this.GetAllResults(resultsIterator, true);
+
+ return JSON.stringify(results);
+ }
+
+ // AssetExists returns true when asset with given ID exists in world state
+ async AssetExists(ctx, assetName) {
+ // ==== Check if asset already exists ====
+ let assetState = await ctx.stub.getState(assetName);
+ return assetState && assetState.length > 0;
+ }
+
+ async GetAllResults(iterator, isHistory) {
+ let allResults = [];
+ let res = await iterator.next();
+ while (!res.done) {
+ if (res.value && res.value.value.toString()) {
+ let jsonRes = {};
+ console.log(res.value.value.toString('utf8'));
+ if (isHistory && isHistory === true) {
+ jsonRes.TxId = res.value.tx_id;
+ jsonRes.Timestamp = res.value.timestamp;
+ try {
+ jsonRes.Value = JSON.parse(res.value.value.toString('utf8'));
+ } catch (err) {
+ console.log(err);
+ jsonRes.Value = res.value.value.toString('utf8');
+ }
+ } else {
+ jsonRes.Key = res.value.key;
+ try {
+ jsonRes.Record = JSON.parse(res.value.value.toString('utf8'));
+ } catch (err) {
+ console.log(err);
+ jsonRes.Record = res.value.value.toString('utf8');
+ }
+ }
+ allResults.push(jsonRes);
+ }
+ res = await iterator.next();
+ }
+ iterator.close();
+ return allResults;
+ }
+
+ // InitLedger creates sample assets in the ledger
+ async InitLedger(ctx) {
+ const assets = [
+ {
+ assetID: 'asset1',
+ color: 'blue',
+ size: 5,
+ owner: 'Tom',
+ appraisedValue: 100
+ },
+ {
+ assetID: 'asset2',
+ color: 'red',
+ size: 5,
+ owner: 'Brad',
+ appraisedValue: 100
+ },
+ {
+ assetID: 'asset3',
+ color: 'green',
+ size: 10,
+ owner: 'Jin Soo',
+ appraisedValue: 200
+ },
+ {
+ assetID: 'asset4',
+ color: 'yellow',
+ size: 10,
+ owner: 'Max',
+ appraisedValue: 200
+ },
+ {
+ assetID: 'asset5',
+ color: 'black',
+ size: 15,
+ owner: 'Adriana',
+ appraisedValue: 250
+ },
+ {
+ assetID: 'asset6',
+ color: 'white',
+ size: 15,
+ owner: 'Michel',
+ appraisedValue: 250
+ },
+ ];
+
+ for (const asset of assets) {
+ await this.CreateAsset(
+ ctx,
+ asset.assetID,
+ asset.color,
+ asset.size,
+ asset.owner,
+ asset.appraisedValue
+ );
+ }
+ }
+}
+
+module.exports = Chaincode;
diff --git a/asset-transfer-ledger-queries/chaincode-javascript/package.json b/asset-transfer-ledger-queries/chaincode-javascript/package.json
new file mode 100644
index 0000000..0427b5c
--- /dev/null
+++ b/asset-transfer-ledger-queries/chaincode-javascript/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "asset-transfer-ledger-queries",
+ "version": "1.0.0",
+ "description": "asset chaincode implemented in node.js",
+ "main": "index.js",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5.3.0"
+ },
+ "scripts": {
+ "start": "fabric-chaincode-node start"
+ },
+ "engine-strict": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ }
+}
diff --git a/asset-transfer-private-data/application-javascript/.eslintignore b/asset-transfer-private-data/application-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-private-data/application-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-private-data/application-javascript/.eslintrc.js b/asset-transfer-private-data/application-javascript/.eslintrc.js
new file mode 100644
index 0000000..8b83df7
--- /dev/null
+++ b/asset-transfer-private-data/application-javascript/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 4],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-tabs': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/asset-transfer-private-data/application-javascript/.gitignore b/asset-transfer-private-data/application-javascript/.gitignore
new file mode 100644
index 0000000..b7db091
--- /dev/null
+++ b/asset-transfer-private-data/application-javascript/.gitignore
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+package-lock.json
+
+wallet.org1
+wallet.org2
+!wallet.org1/.gitkeep
+!wallet.org2/.gitkeep
\ No newline at end of file
diff --git a/asset-transfer-private-data/application-javascript/app.js b/asset-transfer-private-data/application-javascript/app.js
new file mode 100644
index 0000000..77ac59f
--- /dev/null
+++ b/asset-transfer-private-data/application-javascript/app.js
@@ -0,0 +1,358 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const FabricCAServices = require('fabric-ca-client');
+const path = require('path');
+const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'private';
+
+const memberAssetCollectionName = 'assetCollection';
+const org1PrivateCollectionName = 'Org1MSPPrivateCollection';
+const org2PrivateCollectionName = 'Org2MSPPrivateCollection';
+const mspOrg1 = 'Org1MSP';
+const mspOrg2 = 'Org2MSP';
+const Org1UserId = 'appUser1';
+const Org2UserId = 'appUser2';
+
+const RED = '\x1b[31m\n';
+const RESET = '\x1b[0m';
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+function doFail(msgString) {
+ console.error(`${RED}\t${msgString}${RESET}`);
+ process.exit(1);
+}
+
+function verifyAssetData(org, resultBuffer, expectedId, color, size, ownerUserId, appraisedValue) {
+
+ let asset;
+ if (resultBuffer) {
+ asset = JSON.parse(resultBuffer.toString('utf8'));
+ } else {
+ doFail('Failed to read asset');
+ }
+ console.log(`*** verify asset data for: ${expectedId}`);
+ if (!asset) {
+ doFail('Received empty asset');
+ }
+ if (expectedId !== asset.assetID) {
+ doFail(`recieved asset ${asset.assetID} , but expected ${expectedId}`);
+ }
+ if (asset.color !== color) {
+ doFail(`asset ${asset.assetID} has color of ${asset.color}, expected value ${color}`);
+ }
+ if (asset.size !== size) {
+ doFail(`Failed size check - asset ${asset.assetID} has size of ${asset.size}, expected value ${size}`);
+ }
+
+ if (asset.owner.includes(ownerUserId)) {
+ console.log(`\tasset ${asset.assetID} owner: ${asset.owner}`);
+ } else {
+ doFail(`Failed owner check from ${org} - asset ${asset.assetID} owned by ${asset.owner}, expected userId ${ownerUserId}`);
+ }
+ if (appraisedValue) {
+ if (asset.appraisedValue !== appraisedValue) {
+ doFail(`Failed appraised value check from ${org} - asset ${asset.assetID} has appraised value of ${asset.appraisedValue}, expected value ${appraisedValue}`);
+ }
+ }
+}
+
+function verifyAssetPrivateDetails(resultBuffer, expectedId, appraisedValue) {
+ let assetPD;
+ if (resultBuffer) {
+ assetPD = JSON.parse(resultBuffer.toString('utf8'));
+ } else {
+ doFail('Failed to read asset private details');
+ }
+ console.log(`*** verify private details: ${expectedId}`);
+ if (!assetPD) {
+ doFail('Received empty data');
+ }
+ if (expectedId !== assetPD.assetID) {
+ doFail(`recieved ${assetPD.assetID} , but expected ${expectedId}`);
+ }
+
+ if (appraisedValue) {
+ if (assetPD.appraisedValue !== appraisedValue) {
+ doFail(`Failed appraised value check - asset ${assetPD.assetID} has appraised value of ${assetPD.appraisedValue}, expected value ${appraisedValue}`);
+ }
+ }
+}
+
+async function initContractFromOrg1Identity() {
+ console.log('\n--> Fabric client user & Gateway init: Using Org1 identity to Org1 Peer');
+ // build an in memory object with the network configuration (also known as a connection profile)
+ const ccpOrg1 = buildCCPOrg1();
+
+ // build an instance of the fabric ca services client based on
+ // the information in the network configuration
+ const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
+
+ // setup the wallet to cache the credentials of the application user, on the app server locally
+ const walletPathOrg1 = path.join(__dirname, 'wallet/org1');
+ const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
+
+ // in a real application this would be done on an administrative flow, and only once
+ // stores admin identity in local wallet, if needed
+ await enrollAdmin(caOrg1Client, walletOrg1, mspOrg1);
+ // register & enroll application user with CA, which is used as client identify to make chaincode calls
+ // and stores app user identity in local wallet
+ // In a real application this would be done only when a new user was required to be added
+ // and would be part of an administrative flow
+ await registerAndEnrollUser(caOrg1Client, walletOrg1, mspOrg1, Org1UserId, 'org1.department1');
+
+ try {
+ // Create a new gateway for connecting to Org's peer node.
+ const gatewayOrg1 = new Gateway();
+ //connect using Discovery enabled
+ await gatewayOrg1.connect(ccpOrg1,
+ { wallet: walletOrg1, identity: Org1UserId, discovery: { enabled: true, asLocalhost: true } });
+
+ return gatewayOrg1;
+ } catch (error) {
+ console.error(`Error in connecting to gateway: ${error}`);
+ process.exit(1);
+ }
+}
+
+async function initContractFromOrg2Identity() {
+ console.log('\n--> Fabric client user & Gateway init: Using Org2 identity to Org2 Peer');
+ const ccpOrg2 = buildCCPOrg2();
+ const caOrg2Client = buildCAClient(FabricCAServices, ccpOrg2, 'ca.org2.example.com');
+
+ const walletPathOrg2 = path.join(__dirname, 'wallet/org2');
+ const walletOrg2 = await buildWallet(Wallets, walletPathOrg2);
+
+ await enrollAdmin(caOrg2Client, walletOrg2, mspOrg2);
+ await registerAndEnrollUser(caOrg2Client, walletOrg2, mspOrg2, Org2UserId, 'org2.department1');
+
+ try {
+ // Create a new gateway for connecting to Org's peer node.
+ const gatewayOrg2 = new Gateway();
+ await gatewayOrg2.connect(ccpOrg2,
+ { wallet: walletOrg2, identity: Org2UserId, discovery: { enabled: true, asLocalhost: true } });
+
+ return gatewayOrg2;
+ } catch (error) {
+ console.error(`Error in connecting to gateway: ${error}`);
+ process.exit(1);
+ }
+}
+
+// Main workflow : usecase details at asset-transfer-private-data/chaincode-go/README.md
+// This app uses fabric-samples/test-network based setup and the companion chaincode
+// For this usecase illustration, we will use both Org1 & Org2 client identity from this same app
+// In real world the Org1 & Org2 identity will be used in different apps to achieve asset transfer.
+async function main() {
+ try {
+
+ /** ******* Fabric client init: Using Org1 identity to Org1 Peer ********** */
+ const gatewayOrg1 = await initContractFromOrg1Identity();
+ const networkOrg1 = await gatewayOrg1.getNetwork(myChannel);
+ const contractOrg1 = networkOrg1.getContract(myChaincodeName);
+ // Since this sample chaincode uses, Private Data Collection level endorsement policy, addDiscoveryInterest
+ // scopes the discovery service further to use the endorsement policies of collections, if any
+ contractOrg1.addDiscoveryInterest({ name: myChaincodeName, collectionNames: [memberAssetCollectionName, org1PrivateCollectionName] });
+
+ /** ~~~~~~~ Fabric client init: Using Org2 identity to Org2 Peer ~~~~~~~ */
+ const gatewayOrg2 = await initContractFromOrg2Identity();
+ const networkOrg2 = await gatewayOrg2.getNetwork(myChannel);
+ const contractOrg2 = networkOrg2.getContract(myChaincodeName);
+ contractOrg2.addDiscoveryInterest({ name: myChaincodeName, collectionNames: [memberAssetCollectionName, org2PrivateCollectionName] });
+ try {
+ // Sample transactions are listed below
+ // Add few sample Assets & transfers one of the asset from Org1 to Org2 as the new owner
+ let randomNumber = Math.floor(Math.random() * 1000) + 1;
+ // use a random key so that we can run multiple times
+ let assetID1 = `asset${randomNumber}`;
+ let assetID2 = `asset${randomNumber + 1}`;
+ const assetType = 'ValuableAsset';
+ let result;
+ let asset1Data = { objectType: assetType, assetID: assetID1, color: 'green', size: 20, appraisedValue: 100 };
+ let asset2Data = { objectType: assetType, assetID: assetID2, color: 'blue', size: 35, appraisedValue: 727 };
+
+ console.log('\n**************** As Org1 Client ****************');
+ console.log('Adding Assets to work with:\n--> Submit Transaction: CreateAsset ' + assetID1);
+ let statefulTxn = contractOrg1.createTransaction('CreateAsset');
+ //if you need to customize endorsement to specific set of Orgs, use setEndorsingOrganizations
+ //statefulTxn.setEndorsingOrganizations(mspOrg1);
+ let tmapData = Buffer.from(JSON.stringify(asset1Data));
+ statefulTxn.setTransient({
+ asset_properties: tmapData
+ });
+ result = await statefulTxn.submit();
+
+ //Add asset2
+ console.log('\n--> Submit Transaction: CreateAsset ' + assetID2);
+ statefulTxn = contractOrg1.createTransaction('CreateAsset');
+ tmapData = Buffer.from(JSON.stringify(asset2Data));
+ statefulTxn.setTransient({
+ asset_properties: tmapData
+ });
+ result = await statefulTxn.submit();
+
+
+ console.log('\n--> Evaluate Transaction: GetAssetByRange asset0-asset9');
+ // GetAssetByRange returns assets on the ledger with ID in the range of startKey (inclusive) and endKey (exclusive)
+ result = await contractOrg1.evaluateTransaction('GetAssetByRange', 'asset0', 'asset9');
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+ if (!result || result.length === 0) {
+ doFail('recieved empty query list for GetAssetByRange');
+ }
+ console.log('\n--> Evaluate Transaction: ReadAssetPrivateDetails from ' + org1PrivateCollectionName);
+ // ReadAssetPrivateDetails reads data from Org's private collection. Args: collectionName, assetID
+ result = await contractOrg1.evaluateTransaction('ReadAssetPrivateDetails', org1PrivateCollectionName, assetID1);
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+ verifyAssetPrivateDetails(result, assetID1, 100);
+
+ // Attempt Transfer the asset to Org2 , without Org2 adding AgreeToTransfer //
+ // Transaction should return an error: "failed transfer verification ..."
+ let buyerDetails = { assetID: assetID1, buyerMSP: mspOrg2 };
+ try {
+ console.log('\n--> Attempt Submit Transaction: TransferAsset ' + assetID1);
+ statefulTxn = contractOrg1.createTransaction('TransferAsset');
+ tmapData = Buffer.from(JSON.stringify(buyerDetails));
+ statefulTxn.setTransient({
+ asset_owner: tmapData
+ });
+ result = await statefulTxn.submit();
+ console.log('******** FAILED: above operation expected to return an error');
+ } catch (error) {
+ console.log(` Successfully caught the error: \n ${error}`);
+ }
+ console.log('\n~~~~~~~~~~~~~~~~ As Org2 Client ~~~~~~~~~~~~~~~~');
+ console.log('\n--> Evaluate Transaction: ReadAsset ' + assetID1);
+ result = await contractOrg2.evaluateTransaction('ReadAsset', assetID1);
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+ verifyAssetData(mspOrg2, result, assetID1, 'green', 20, Org1UserId);
+
+
+ // Org2 cannot ReadAssetPrivateDetails from Org1's private collection due to Collection policy
+ // Will fail: await contractOrg2.evaluateTransaction('ReadAssetPrivateDetails', org1PrivateCollectionName, assetID1);
+
+ // Buyer from Org2 agrees to buy the asset assetID1 //
+ // To purchase the asset, the buyer needs to agree to the same value as the asset owner
+ let dataForAgreement = { assetID: assetID1, appraisedValue: 100 };
+ console.log('\n--> Submit Transaction: AgreeToTransfer payload ' + JSON.stringify(dataForAgreement));
+ statefulTxn = contractOrg2.createTransaction('AgreeToTransfer');
+ tmapData = Buffer.from(JSON.stringify(dataForAgreement));
+ statefulTxn.setTransient({
+ asset_value: tmapData
+ });
+ result = await statefulTxn.submit();
+
+ //Buyer can withdraw the Agreement, using DeleteTranferAgreement
+ /*statefulTxn = contractOrg2.createTransaction('DeleteTranferAgreement');
+ statefulTxn.setEndorsingOrganizations(mspOrg2);
+ let dataForDeleteAgreement = { assetID: assetID1 };
+ tmapData = Buffer.from(JSON.stringify(dataForDeleteAgreement));
+ statefulTxn.setTransient({
+ agreement_delete: tmapData
+ });
+ result = await statefulTxn.submit();*/
+
+ console.log('\n**************** As Org1 Client ****************');
+ // All members can send txn ReadTransferAgreement, set by Org2 above
+ console.log('\n--> Evaluate Transaction: ReadTransferAgreement ' + assetID1);
+ result = await contractOrg1.evaluateTransaction('ReadTransferAgreement', assetID1);
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+
+ // Transfer the asset to Org2 //
+ // To transfer the asset, the owner needs to pass the MSP ID of new asset owner, and initiate the transfer
+ console.log('\n--> Submit Transaction: TransferAsset ' + assetID1);
+
+ statefulTxn = contractOrg1.createTransaction('TransferAsset');
+ tmapData = Buffer.from(JSON.stringify(buyerDetails));
+ statefulTxn.setTransient({
+ asset_owner: tmapData
+ });
+ result = await statefulTxn.submit();
+
+ //Again ReadAsset : results will show that the buyer identity now owns the asset:
+ console.log('\n--> Evaluate Transaction: ReadAsset ' + assetID1);
+ result = await contractOrg1.evaluateTransaction('ReadAsset', assetID1);
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+ verifyAssetData(mspOrg1, result, assetID1, 'green', 20, Org2UserId);
+
+ //Confirm that transfer removed the private details from the Org1 collection:
+ console.log('\n--> Evaluate Transaction: ReadAssetPrivateDetails');
+ // ReadAssetPrivateDetails reads data from Org's private collection: Should return empty
+ result = await contractOrg1.evaluateTransaction('ReadAssetPrivateDetails', org1PrivateCollectionName, assetID1);
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+ if (result && result.length > 0) {
+ doFail('Expected empty data from ReadAssetPrivateDetails');
+ }
+ console.log('\n--> Evaluate Transaction: ReadAsset ' + assetID2);
+ result = await contractOrg1.evaluateTransaction('ReadAsset', assetID2);
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+ verifyAssetData(mspOrg1, result, assetID2, 'blue', 35, Org1UserId);
+
+ console.log('\n********* Demo deleting asset **************');
+ let dataForDelete = { assetID: assetID2 };
+ try {
+ //Non-owner Org2 should not be able to DeleteAsset. Expect an error from DeleteAsset
+ console.log('--> Attempt Transaction: as Org2 DeleteAsset ' + assetID2);
+ statefulTxn = contractOrg2.createTransaction('DeleteAsset');
+ tmapData = Buffer.from(JSON.stringify(dataForDelete));
+ statefulTxn.setTransient({
+ asset_delete: tmapData
+ });
+ result = await statefulTxn.submit();
+ console.log('******** FAILED : expected to return an error');
+ } catch (error) {
+ console.log(` Successfully caught the error: \n ${error}`);
+ }
+ // Delete Asset2 as Org1
+ console.log('--> Submit Transaction: as Org1 DeleteAsset ' + assetID2);
+ statefulTxn = contractOrg1.createTransaction('DeleteAsset');
+ tmapData = Buffer.from(JSON.stringify(dataForDelete));
+ statefulTxn.setTransient({
+ asset_delete: tmapData
+ });
+ result = await statefulTxn.submit();
+
+ console.log('\n--> Evaluate Transaction: ReadAsset ' + assetID2);
+ result = await contractOrg1.evaluateTransaction('ReadAsset', assetID2);
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+ if (result && result.length > 0) {
+ doFail('Expected empty read, after asset is deleted');
+ }
+
+ console.log('\n~~~~~~~~~~~~~~~~ As Org2 Client ~~~~~~~~~~~~~~~~');
+ // Org2 can ReadAssetPrivateDetails: Org2 is owner, and private details exist in new owner's Collection
+ console.log('\n--> Evaluate Transaction as Org2: ReadAssetPrivateDetails ' + assetID1 + ' from ' + org2PrivateCollectionName);
+ result = await contractOrg2.evaluateTransaction('ReadAssetPrivateDetails', org2PrivateCollectionName, assetID1);
+ console.log(`<-- result: ${prettyJSONString(result.toString())}`);
+ verifyAssetPrivateDetails(result, assetID1, 100);
+ } finally {
+ // Disconnect from the gateway peer when all work for this client identity is complete
+ gatewayOrg1.disconnect();
+ gatewayOrg2.disconnect();
+ }
+ } catch (error) {
+ console.error(`Error in transaction: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/asset-transfer-private-data/application-javascript/package.json b/asset-transfer-private-data/application-javascript/package.json
new file mode 100644
index 0000000..86d8b24
--- /dev/null
+++ b/asset-transfer-private-data/application-javascript/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "asset-transfer-private-data",
+ "version": "1.0.0",
+ "description": "asset-transfer-private-data application implemented in JavaScript",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "test": "nyc mocha --recursive"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.4",
+ "fabric-network": "^2.2.4"
+ },
+ "devDependencies": {
+ "chai": "^4.2.0",
+ "eslint": "^5.9.0",
+ "mocha": "^5.2.0",
+ "nyc": "^14.1.1",
+ "sinon": "^7.1.1",
+ "sinon-chai": "^3.3.0"
+ },
+ "nyc": {
+ "exclude": [
+ "coverage/**",
+ "test/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/asset-transfer-private-data/chaincode-go/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json b/asset-transfer-private-data/chaincode-go/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json
new file mode 100644
index 0000000..e2d1d08
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json
@@ -0,0 +1,12 @@
+{
+ "index": {
+ "fields": [
+ "objectType",
+ "owner"
+ ]
+ },
+ "ddoc": "indexOwnerDoc",
+ "name": "indexOwner",
+ "type": "json"
+}
+
diff --git a/asset-transfer-private-data/chaincode-go/README.md b/asset-transfer-private-data/chaincode-go/README.md
new file mode 100644
index 0000000..f87a95c
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/README.md
@@ -0,0 +1 @@
+[Using Private Data tutorial](https://hyperledger-fabric.readthedocs.io/en/latest/private_data_tutorial.html)
diff --git a/asset-transfer-private-data/chaincode-go/chaincode/asset_queries.go b/asset-transfer-private-data/chaincode-go/chaincode/asset_queries.go
new file mode 100644
index 0000000..8672e31
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/chaincode/asset_queries.go
@@ -0,0 +1,190 @@
+/*
+Copyright IBM Corp. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package chaincode
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// ReadAsset reads the information from collection
+func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, assetID string) (*Asset, error) {
+
+ log.Printf("ReadAsset: collection %v, ID %v", assetCollection, assetID)
+ assetJSON, err := ctx.GetStub().GetPrivateData(assetCollection, assetID) //get the asset from chaincode state
+ if err != nil {
+ return nil, fmt.Errorf("failed to read asset: %v", err)
+ }
+
+ //No Asset found, return empty response
+ if assetJSON == nil {
+ log.Printf("%v does not exist in collection %v", assetID, assetCollection)
+ return nil, nil
+ }
+
+ var asset *Asset
+ err = json.Unmarshal(assetJSON, &asset)
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ return asset, nil
+
+}
+
+// ReadAssetPrivateDetails reads the asset private details in organization specific collection
+func (s *SmartContract) ReadAssetPrivateDetails(ctx contractapi.TransactionContextInterface, collection string, assetID string) (*AssetPrivateDetails, error) {
+ log.Printf("ReadAssetPrivateDetails: collection %v, ID %v", collection, assetID)
+ assetDetailsJSON, err := ctx.GetStub().GetPrivateData(collection, assetID) // Get the asset from chaincode state
+ if err != nil {
+ return nil, fmt.Errorf("failed to read asset details: %v", err)
+ }
+ if assetDetailsJSON == nil {
+ log.Printf("AssetPrivateDetails for %v does not exist in collection %v", assetID, collection)
+ return nil, nil
+ }
+
+ var assetDetails *AssetPrivateDetails
+ err = json.Unmarshal(assetDetailsJSON, &assetDetails)
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ return assetDetails, nil
+}
+
+// ReadTransferAgreement gets the buyer's identity from the transfer agreement from collection
+func (s *SmartContract) ReadTransferAgreement(ctx contractapi.TransactionContextInterface, assetID string) (*TransferAgreement, error) {
+ log.Printf("ReadTransferAgreement: collection %v, ID %v", assetCollection, assetID)
+ // composite key for TransferAgreement of this asset
+ transferAgreeKey, err := ctx.GetStub().CreateCompositeKey(transferAgreementObjectType, []string{assetID})
+ if err != nil {
+ return nil, fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ buyerIdentity, err := ctx.GetStub().GetPrivateData(assetCollection, transferAgreeKey) // Get the identity from collection
+ if err != nil {
+ return nil, fmt.Errorf("failed to read TransferAgreement: %v", err)
+ }
+ if buyerIdentity == nil {
+ log.Printf("TransferAgreement for %v does not exist", assetID)
+ return nil, nil
+ }
+ agreement := &TransferAgreement{
+ ID: assetID,
+ BuyerID: string(buyerIdentity),
+ }
+ return agreement, nil
+}
+
+// GetAssetByRange performs a range query based on the start and end keys provided. Range
+// queries can be used to read data from private data collections, but can not be used in
+// a transaction that also writes to private data.
+func (s *SmartContract) GetAssetByRange(ctx contractapi.TransactionContextInterface, startKey string, endKey string) ([]*Asset, error) {
+
+ resultsIterator, err := ctx.GetStub().GetPrivateDataByRange(assetCollection, startKey, endKey)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ results := []*Asset{}
+
+ for resultsIterator.HasNext() {
+ response, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ var asset *Asset
+ err = json.Unmarshal(response.Value, &asset)
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ results = append(results, asset)
+ }
+
+ return results, nil
+
+}
+
+// =======Rich queries =========================================================================
+// Two examples of rich queries are provided below (parameterized query and ad hoc query).
+// Rich queries pass a query string to the state database.
+// Rich queries are only supported by state database implementations
+// that support rich query (e.g. CouchDB).
+// The query string is in the syntax of the underlying state database.
+// With rich queries there is no guarantee that the result set hasn't changed between
+// endorsement time and commit time, aka 'phantom reads'.
+// Therefore, rich queries should not be used in update transactions, unless the
+// application handles the possibility of result set changes between endorsement and commit time.
+// Rich queries can be used for point-in-time queries against a peer.
+// ============================================================================================
+
+// ===== Example: Parameterized rich query =================================================
+
+// QueryAssetByOwner queries for assets based on assetType, owner.
+// This is an example of a parameterized query where the query logic is baked into the chaincode,
+// and accepting a single query parameter (owner).
+// Only available on state databases that support rich query (e.g. CouchDB)
+// =========================================================================================
+func (s *SmartContract) QueryAssetByOwner(ctx contractapi.TransactionContextInterface, assetType string, owner string) ([]*Asset, error) {
+
+ queryString := fmt.Sprintf("{\"selector\":{\"objectType\":\"%v\",\"owner\":\"%v\"}}", assetType, owner)
+
+ queryResults, err := s.getQueryResultForQueryString(ctx, queryString)
+ if err != nil {
+ return nil, err
+ }
+ return queryResults, nil
+}
+
+// QueryAssets uses a query string to perform a query for assets.
+// Query string matching state database syntax is passed in and executed as is.
+// Supports ad hoc queries that can be defined at runtime by the client.
+// If this is not desired, follow the QueryAssetByOwner example for parameterized queries.
+// Only available on state databases that support rich query (e.g. CouchDB)
+func (s *SmartContract) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) {
+
+ queryResults, err := s.getQueryResultForQueryString(ctx, queryString)
+ if err != nil {
+ return nil, err
+ }
+ return queryResults, nil
+}
+
+// getQueryResultForQueryString executes the passed in query string.
+func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) {
+
+ resultsIterator, err := ctx.GetStub().GetPrivateDataQueryResult(assetCollection, queryString)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ results := []*Asset{}
+
+ for resultsIterator.HasNext() {
+ response, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+ var asset *Asset
+
+ err = json.Unmarshal(response.Value, &asset)
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ results = append(results, asset)
+ }
+ return results, nil
+}
diff --git a/asset-transfer-private-data/chaincode-go/chaincode/asset_queries_test.go b/asset-transfer-private-data/chaincode-go/chaincode/asset_queries_test.go
new file mode 100644
index 0000000..3663c87
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/chaincode/asset_queries_test.go
@@ -0,0 +1,185 @@
+/*
+Copyright IBM Corp. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+package chaincode_test
+
+import (
+ "encoding/json"
+ "fmt"
+ "testing"
+
+ "github.com/hyperledger/fabric-protos-go/ledger/queryresult"
+
+ "github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode"
+ "github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode/mocks"
+ "github.com/stretchr/testify/require"
+)
+
+/*
+For details on generating the mocks, see comments in the file asset_transfer_test.go
+*/
+func TestReadAsset(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+
+ assetBytes, err := assetTransferCC.ReadAsset(transactionContext, "id1")
+ require.NoError(t, err)
+ require.Nil(t, assetBytes)
+
+ chaincodeStub.GetPrivateDataReturns(nil, fmt.Errorf("unable to retrieve asset"))
+ assetBytes, err = assetTransferCC.ReadAsset(transactionContext, "id1")
+ require.EqualError(t, err, "failed to read asset: unable to retrieve asset")
+
+ testAsset := &chaincode.Asset{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ Owner: myOrg1Clientid,
+ }
+ setReturnPrivateDataInStub(t, chaincodeStub, testAsset)
+ assetRead, err := assetTransferCC.ReadAsset(transactionContext, "id1")
+ require.NoError(t, err)
+ require.Equal(t, testAsset, assetRead)
+}
+
+func TestReadAssetPrivateDetails(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+
+ assetBytes, err := assetTransferCC.ReadAssetPrivateDetails(transactionContext, myOrg1PrivCollection, "id1")
+ require.NoError(t, err)
+ require.Nil(t, assetBytes)
+
+ //read from the collection with no access
+ chaincodeStub.GetPrivateDataReturns(nil, fmt.Errorf("collection not found"))
+ assetBytes, err = assetTransferCC.ReadAssetPrivateDetails(transactionContext, myOrg2PrivCollection, "id1")
+ require.EqualError(t, err, "failed to read asset details: collection not found")
+
+ returnPrivData := &chaincode.AssetPrivateDetails{
+ ID: "id1",
+ AppraisedValue: 5,
+ }
+ setReturnAssetPrivateDetailsInStub(t, chaincodeStub, returnPrivData)
+ assetRead, err := assetTransferCC.ReadAssetPrivateDetails(transactionContext, myOrg1PrivCollection, "id1")
+ require.NoError(t, err)
+ require.Equal(t, returnPrivData, assetRead)
+}
+
+func TestReadTransferAgreement(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+
+ //TransferAgreement does not exist
+ assetBytes, err := assetTransferCC.ReadTransferAgreement(transactionContext, "id1")
+ require.NoError(t, err)
+ require.Nil(t, assetBytes)
+
+ chaincodeStub.GetPrivateDataReturns([]byte(myOrg2Clientid), nil)
+ expectedData := &chaincode.TransferAgreement{
+ ID: "id1",
+ BuyerID: myOrg2Clientid,
+ }
+ dataRead, err := assetTransferCC.ReadTransferAgreement(transactionContext, "id1")
+ require.NoError(t, err)
+ require.Equal(t, expectedData, dataRead)
+}
+
+func TestQueryAssetByOwner(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+
+ asset := &chaincode.Asset{Type: "valuableasset", ID: "asset1", Owner: "user1"}
+ asset1Bytes, err := json.Marshal(asset)
+ require.NoError(t, err)
+
+ iterator := &mocks.StateQueryIterator{}
+ iterator.HasNextReturnsOnCall(0, true)
+ iterator.HasNextReturnsOnCall(1, false)
+ iterator.NextReturns(&queryresult.KV{Value: asset1Bytes}, nil)
+ chaincodeStub.GetPrivateDataQueryResultReturns(iterator, nil)
+
+ assetTransferCC := &chaincode.SmartContract{}
+ assets, err := assetTransferCC.QueryAssetByOwner(transactionContext, "valuableasset", "user1")
+ require.NoError(t, err)
+ require.Equal(t, []*chaincode.Asset{asset}, assets)
+
+ iterator.HasNextReturns(true)
+ iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item"))
+ assets, err = assetTransferCC.QueryAssetByOwner(transactionContext, "valuableasset", "user1")
+ require.EqualError(t, err, "failed retrieving next item")
+ require.Nil(t, assets)
+
+}
+
+func TestQueryAssets(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ //Iterator with no records
+ iterator := &mocks.StateQueryIterator{}
+ iterator.HasNextReturns(false)
+ chaincodeStub.GetPrivateDataQueryResultReturns(iterator, nil)
+
+ assetTransferCC := &chaincode.SmartContract{}
+ assets, err := assetTransferCC.QueryAssets(transactionContext, "querystr")
+ require.NoError(t, err)
+ require.Equal(t, []*chaincode.Asset{}, assets)
+
+ iterator = &mocks.StateQueryIterator{}
+ chaincodeStub.GetPrivateDataQueryResultReturns(iterator, nil)
+ iterator.HasNextReturns(true)
+ iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item"))
+ assets, err = assetTransferCC.QueryAssets(transactionContext, "querystr")
+ require.EqualError(t, err, "failed retrieving next item")
+ require.Nil(t, assets)
+
+ asset := &chaincode.Asset{Type: "valuableasset", ID: "asset1", Owner: "user1"}
+ asset1Bytes, err := json.Marshal(asset)
+ require.NoError(t, err)
+
+ iterator = &mocks.StateQueryIterator{}
+ chaincodeStub.GetPrivateDataQueryResultReturns(iterator, nil)
+ iterator.HasNextReturnsOnCall(0, true)
+ iterator.HasNextReturnsOnCall(1, false)
+ iterator.NextReturns(&queryresult.KV{Value: asset1Bytes}, nil)
+
+ assets, err = assetTransferCC.QueryAssets(transactionContext, "querystr")
+ require.NoError(t, err)
+ require.Equal(t, []*chaincode.Asset{asset}, assets)
+}
+
+func TestGetAssetByRange(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ //Iterator with no records
+ iterator := &mocks.StateQueryIterator{}
+ iterator.HasNextReturns(false)
+ chaincodeStub.GetPrivateDataByRangeReturns(iterator, nil)
+
+ assetTransferCC := &chaincode.SmartContract{}
+ assets, err := assetTransferCC.GetAssetByRange(transactionContext, "st", "end")
+ require.NoError(t, err)
+ require.Equal(t, []*chaincode.Asset{}, assets)
+
+ iterator = &mocks.StateQueryIterator{}
+ chaincodeStub.GetPrivateDataByRangeReturns(iterator, nil)
+ iterator.HasNextReturns(true)
+ iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item"))
+ assets, err = assetTransferCC.GetAssetByRange(transactionContext, "st", "end")
+ require.EqualError(t, err, "failed retrieving next item")
+ require.Nil(t, assets)
+
+ asset := &chaincode.Asset{Type: "valuableasset", ID: "asset1", Owner: "user1"}
+ asset1Bytes, err := json.Marshal(asset)
+ require.NoError(t, err)
+
+ iterator = &mocks.StateQueryIterator{}
+ chaincodeStub.GetPrivateDataByRangeReturns(iterator, nil)
+ iterator.HasNextReturnsOnCall(0, true)
+ iterator.HasNextReturnsOnCall(1, false)
+ iterator.NextReturns(&queryresult.KV{Value: asset1Bytes}, nil)
+
+ assets, err = assetTransferCC.GetAssetByRange(transactionContext, "st", "end")
+ require.NoError(t, err)
+ require.Equal(t, []*chaincode.Asset{asset}, assets)
+
+}
diff --git a/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go b/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go
new file mode 100644
index 0000000..59eae2a
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go
@@ -0,0 +1,590 @@
+/*
+Copyright IBM Corp. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package chaincode
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "log"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+const assetCollection = "assetCollection"
+const transferAgreementObjectType = "transferAgreement"
+
+// SmartContract of this fabric sample
+type SmartContract struct {
+ contractapi.Contract
+}
+
+// Asset describes main asset details that are visible to all organizations
+type Asset struct {
+ Type string `json:"objectType"` //Type is used to distinguish the various types of objects in state database
+ ID string `json:"assetID"`
+ Color string `json:"color"`
+ Size int `json:"size"`
+ Owner string `json:"owner"`
+}
+
+// AssetPrivateDetails describes details that are private to owners
+type AssetPrivateDetails struct {
+ ID string `json:"assetID"`
+ AppraisedValue int `json:"appraisedValue"`
+}
+
+// TransferAgreement describes the buyer agreement returned by ReadTransferAgreement
+type TransferAgreement struct {
+ ID string `json:"assetID"`
+ BuyerID string `json:"buyerID"`
+}
+
+// CreateAsset creates a new asset by placing the main asset details in the assetCollection
+// that can be read by both organizations. The appraisal value is stored in the owners org specific collection.
+func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface) error {
+
+ // Get new asset from transient map
+ transientMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("error getting transient: %v", err)
+ }
+
+ // Asset properties are private, therefore they get passed in transient field, instead of func args
+ transientAssetJSON, ok := transientMap["asset_properties"]
+ if !ok {
+ //log error to stdout
+ return fmt.Errorf("asset not found in the transient map input")
+ }
+
+ type assetTransientInput struct {
+ Type string `json:"objectType"` //Type is used to distinguish the various types of objects in state database
+ ID string `json:"assetID"`
+ Color string `json:"color"`
+ Size int `json:"size"`
+ AppraisedValue int `json:"appraisedValue"`
+ }
+
+ var assetInput assetTransientInput
+ err = json.Unmarshal(transientAssetJSON, &assetInput)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ if len(assetInput.Type) == 0 {
+ return fmt.Errorf("objectType field must be a non-empty string")
+ }
+ if len(assetInput.ID) == 0 {
+ return fmt.Errorf("assetID field must be a non-empty string")
+ }
+ if len(assetInput.Color) == 0 {
+ return fmt.Errorf("color field must be a non-empty string")
+ }
+ if assetInput.Size <= 0 {
+ return fmt.Errorf("size field must be a positive integer")
+ }
+ if assetInput.AppraisedValue <= 0 {
+ return fmt.Errorf("appraisedValue field must be a positive integer")
+ }
+
+ // Check if asset already exists
+ assetAsBytes, err := ctx.GetStub().GetPrivateData(assetCollection, assetInput.ID)
+ if err != nil {
+ return fmt.Errorf("failed to get asset: %v", err)
+ } else if assetAsBytes != nil {
+ fmt.Println("Asset already exists: " + assetInput.ID)
+ return fmt.Errorf("this asset already exists: " + assetInput.ID)
+ }
+
+ // Get ID of submitting client identity
+ clientID, err := submittingClientIdentity(ctx)
+ if err != nil {
+ return err
+ }
+
+ // Verify that the client is submitting request to peer in their organization
+ // This is to ensure that a client from another org doesn't attempt to read or
+ // write private data from this peer.
+ err = verifyClientOrgMatchesPeerOrg(ctx)
+ if err != nil {
+ return fmt.Errorf("CreateAsset cannot be performed: Error %v", err)
+ }
+
+ // Make submitting client the owner
+ asset := Asset{
+ Type: assetInput.Type,
+ ID: assetInput.ID,
+ Color: assetInput.Color,
+ Size: assetInput.Size,
+ Owner: clientID,
+ }
+ assetJSONasBytes, err := json.Marshal(asset)
+ if err != nil {
+ return fmt.Errorf("failed to marshal asset into JSON: %v", err)
+ }
+
+ // Save asset to private data collection
+ // Typical logger, logs to stdout/file in the fabric managed docker container, running this chaincode
+ // Look for container name like dev-peer0.org1.example.com-{chaincodename_version}-xyz
+ log.Printf("CreateAsset Put: collection %v, ID %v, owner %v", assetCollection, assetInput.ID, clientID)
+
+ err = ctx.GetStub().PutPrivateData(assetCollection, assetInput.ID, assetJSONasBytes)
+ if err != nil {
+ return fmt.Errorf("failed to put asset into private data collecton: %v", err)
+ }
+
+ // Save asset details to collection visible to owning organization
+ assetPrivateDetails := AssetPrivateDetails{
+ ID: assetInput.ID,
+ AppraisedValue: assetInput.AppraisedValue,
+ }
+
+ assetPrivateDetailsAsBytes, err := json.Marshal(assetPrivateDetails) // marshal asset details to JSON
+ if err != nil {
+ return fmt.Errorf("failed to marshal into JSON: %v", err)
+ }
+
+ // Get collection name for this organization.
+ orgCollection, err := getCollectionName(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to infer private collection name for the org: %v", err)
+ }
+
+ // Put asset appraised value into owners org specific private data collection
+ log.Printf("Put: collection %v, ID %v", orgCollection, assetInput.ID)
+ err = ctx.GetStub().PutPrivateData(orgCollection, assetInput.ID, assetPrivateDetailsAsBytes)
+ if err != nil {
+ return fmt.Errorf("failed to put asset private details: %v", err)
+ }
+ return nil
+}
+
+// AgreeToTransfer is used by the potential buyer of the asset to agree to the
+// asset value. The agreed to appraisal value is stored in the buying orgs
+// org specifc collection, while the the buyer client ID is stored in the asset collection
+// using a composite key
+func (s *SmartContract) AgreeToTransfer(ctx contractapi.TransactionContextInterface) error {
+
+ // Get ID of submitting client identity
+ clientID, err := submittingClientIdentity(ctx)
+ if err != nil {
+ return err
+ }
+
+ // Value is private, therefore it gets passed in transient field
+ transientMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("error getting transient: %v", err)
+ }
+
+ // Persist the JSON bytes as-is so that there is no risk of nondeterministic marshaling.
+ valueJSONasBytes, ok := transientMap["asset_value"]
+ if !ok {
+ return fmt.Errorf("asset_value key not found in the transient map")
+ }
+
+ // Unmarshal the tranisent map to get the asset ID.
+ var valueJSON AssetPrivateDetails
+ err = json.Unmarshal(valueJSONasBytes, &valueJSON)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ // Do some error checking since we get the chance
+ if len(valueJSON.ID) == 0 {
+ return fmt.Errorf("assetID field must be a non-empty string")
+ }
+ if valueJSON.AppraisedValue <= 0 {
+ return fmt.Errorf("appraisedValue field must be a positive integer")
+ }
+
+ // Read asset from the private data collection
+ asset, err := s.ReadAsset(ctx, valueJSON.ID)
+ if err != nil {
+ return fmt.Errorf("error reading asset: %v", err)
+ }
+ if asset == nil {
+ return fmt.Errorf("%v does not exist", valueJSON.ID)
+ }
+ // Verify that the client is submitting request to peer in their organization
+ err = verifyClientOrgMatchesPeerOrg(ctx)
+ if err != nil {
+ return fmt.Errorf("AgreeToTransfer cannot be performed: Error %v", err)
+ }
+
+ // Get collection name for this organization. Needs to be read by a member of the organization.
+ orgCollection, err := getCollectionName(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to infer private collection name for the org: %v", err)
+ }
+
+ log.Printf("AgreeToTransfer Put: collection %v, ID %v", orgCollection, valueJSON.ID)
+ // Put agreed value in the org specifc private data collection
+ err = ctx.GetStub().PutPrivateData(orgCollection, valueJSON.ID, valueJSONasBytes)
+ if err != nil {
+ return fmt.Errorf("failed to put asset bid: %v", err)
+ }
+
+ // Create agreeement that indicates which identity has agreed to purchase
+ // In a more realistic transfer scenario, a transfer agreement would be secured to ensure that it cannot
+ // be overwritten by another channel member
+ transferAgreeKey, err := ctx.GetStub().CreateCompositeKey(transferAgreementObjectType, []string{valueJSON.ID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ log.Printf("AgreeToTransfer Put: collection %v, ID %v, Key %v", assetCollection, valueJSON.ID, transferAgreeKey)
+ err = ctx.GetStub().PutPrivateData(assetCollection, transferAgreeKey, []byte(clientID))
+ if err != nil {
+ return fmt.Errorf("failed to put asset bid: %v", err)
+ }
+
+ return nil
+}
+
+// TransferAsset transfers the asset to the new owner by setting a new owner ID
+func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface) error {
+
+ transientMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("error getting transient %v", err)
+ }
+
+ // Asset properties are private, therefore they get passed in transient field
+ transientTransferJSON, ok := transientMap["asset_owner"]
+ if !ok {
+ return fmt.Errorf("asset owner not found in the transient map")
+ }
+
+ type assetTransferTransientInput struct {
+ ID string `json:"assetID"`
+ BuyerMSP string `json:"buyerMSP"`
+ }
+
+ var assetTransferInput assetTransferTransientInput
+ err = json.Unmarshal(transientTransferJSON, &assetTransferInput)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ if len(assetTransferInput.ID) == 0 {
+ return fmt.Errorf("assetID field must be a non-empty string")
+ }
+ if len(assetTransferInput.BuyerMSP) == 0 {
+ return fmt.Errorf("buyerMSP field must be a non-empty string")
+ }
+ log.Printf("TransferAsset: verify asset exists ID %v", assetTransferInput.ID)
+ // Read asset from the private data collection
+ asset, err := s.ReadAsset(ctx, assetTransferInput.ID)
+ if err != nil {
+ return fmt.Errorf("error reading asset: %v", err)
+ }
+ if asset == nil {
+ return fmt.Errorf("%v does not exist", assetTransferInput.ID)
+ }
+ // Verify that the client is submitting request to peer in their organization
+ err = verifyClientOrgMatchesPeerOrg(ctx)
+ if err != nil {
+ return fmt.Errorf("TransferAsset cannot be performed: Error %v", err)
+ }
+
+ // Verify transfer details and transfer owner
+ err = s.verifyAgreement(ctx, assetTransferInput.ID, asset.Owner, assetTransferInput.BuyerMSP)
+ if err != nil {
+ return fmt.Errorf("failed transfer verification: %v", err)
+ }
+
+ transferAgreement, err := s.ReadTransferAgreement(ctx, assetTransferInput.ID)
+ if err != nil {
+ return fmt.Errorf("failed ReadTransferAgreement to find buyerID: %v", err)
+ }
+ if transferAgreement.BuyerID == "" {
+ return fmt.Errorf("BuyerID not found in TransferAgreement for %v", assetTransferInput.ID)
+ }
+
+ // Transfer asset in private data collection to new owner
+ asset.Owner = transferAgreement.BuyerID
+
+ assetJSONasBytes, err := json.Marshal(asset)
+ if err != nil {
+ return fmt.Errorf("failed marshalling asset %v: %v", assetTransferInput.ID, err)
+ }
+
+ log.Printf("TransferAsset Put: collection %v, ID %v", assetCollection, assetTransferInput.ID)
+ err = ctx.GetStub().PutPrivateData(assetCollection, assetTransferInput.ID, assetJSONasBytes) //rewrite the asset
+ if err != nil {
+ return err
+ }
+
+ // Get collection name for this organization
+ ownersCollection, err := getCollectionName(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to infer private collection name for the org: %v", err)
+ }
+
+ // Delete the asset appraised value from this organization's private data collection
+ err = ctx.GetStub().DelPrivateData(ownersCollection, assetTransferInput.ID)
+ if err != nil {
+ return err
+ }
+
+ // Delete the transfer agreement from the asset collection
+ transferAgreeKey, err := ctx.GetStub().CreateCompositeKey(transferAgreementObjectType, []string{assetTransferInput.ID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ err = ctx.GetStub().DelPrivateData(assetCollection, transferAgreeKey)
+ if err != nil {
+ return err
+ }
+
+ return nil
+
+}
+
+// verifyAgreement is an internal helper function used by TransferAsset to verify
+// that the transfer is being initiated by the owner and that the buyer has agreed
+// to the same appraisal value as the owner
+func (s *SmartContract) verifyAgreement(ctx contractapi.TransactionContextInterface, assetID string, owner string, buyerMSP string) error {
+
+ // Check 1: verify that the transfer is being initiatied by the owner
+
+ // Get ID of submitting client identity
+ clientID, err := submittingClientIdentity(ctx)
+ if err != nil {
+ return err
+ }
+
+ if clientID != owner {
+ return fmt.Errorf("error: submitting client identity does not own asset")
+ }
+
+ // Check 2: verify that the buyer has agreed to the appraised value
+
+ // Get collection names
+ collectionOwner, err := getCollectionName(ctx) // get owner collection from caller identity
+ if err != nil {
+ return fmt.Errorf("failed to infer private collection name for the org: %v", err)
+ }
+
+ collectionBuyer := buyerMSP + "PrivateCollection" // get buyers collection
+
+ // Get hash of owners agreed to value
+ ownerAppraisedValueHash, err := ctx.GetStub().GetPrivateDataHash(collectionOwner, assetID)
+ if err != nil {
+ return fmt.Errorf("failed to get hash of appraised value from owners collection %v: %v", collectionOwner, err)
+ }
+ if ownerAppraisedValueHash == nil {
+ return fmt.Errorf("hash of appraised value for %v does not exist in collection %v", assetID, collectionOwner)
+ }
+
+ // Get hash of buyers agreed to value
+ buyerAppraisedValueHash, err := ctx.GetStub().GetPrivateDataHash(collectionBuyer, assetID)
+ if err != nil {
+ return fmt.Errorf("failed to get hash of appraised value from buyer collection %v: %v", collectionBuyer, err)
+ }
+ if buyerAppraisedValueHash == nil {
+ return fmt.Errorf("hash of appraised value for %v does not exist in collection %v. AgreeToTransfer must be called by the buyer first", assetID, collectionBuyer)
+ }
+
+ // Verify that the two hashes match
+ if !bytes.Equal(ownerAppraisedValueHash, buyerAppraisedValueHash) {
+ return fmt.Errorf("hash for appraised value for owner %x does not value for seller %x", ownerAppraisedValueHash, buyerAppraisedValueHash)
+ }
+
+ return nil
+}
+
+// DeleteAsset can be used by the owner of the asset to delete the asset
+func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface) error {
+
+ transientMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("Error getting transient: %v", err)
+ }
+
+ // Asset properties are private, therefore they get passed in transient field
+ transientDeleteJSON, ok := transientMap["asset_delete"]
+ if !ok {
+ return fmt.Errorf("asset to delete not found in the transient map")
+ }
+
+ type assetDelete struct {
+ ID string `json:"assetID"`
+ }
+
+ var assetDeleteInput assetDelete
+ err = json.Unmarshal(transientDeleteJSON, &assetDeleteInput)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ if len(assetDeleteInput.ID) == 0 {
+ return fmt.Errorf("assetID field must be a non-empty string")
+ }
+
+ // Verify that the client is submitting request to peer in their organization
+ err = verifyClientOrgMatchesPeerOrg(ctx)
+ if err != nil {
+ return fmt.Errorf("DeleteAsset cannot be performed: Error %v", err)
+ }
+
+ log.Printf("Deleting Asset: %v", assetDeleteInput.ID)
+ valAsbytes, err := ctx.GetStub().GetPrivateData(assetCollection, assetDeleteInput.ID) //get the asset from chaincode state
+ if err != nil {
+ return fmt.Errorf("failed to read asset: %v", err)
+ }
+ if valAsbytes == nil {
+ return fmt.Errorf("asset not found: %v", assetDeleteInput.ID)
+ }
+
+ ownerCollection, err := getCollectionName(ctx) // Get owners collection
+ if err != nil {
+ return fmt.Errorf("failed to infer private collection name for the org: %v", err)
+ }
+
+ //check the asset is in the caller org's private collection
+ valAsbytes, err = ctx.GetStub().GetPrivateData(ownerCollection, assetDeleteInput.ID)
+ if err != nil {
+ return fmt.Errorf("failed to read asset from owner's Collection: %v", err)
+ }
+ if valAsbytes == nil {
+ return fmt.Errorf("asset not found in owner's private Collection %v: %v", ownerCollection, assetDeleteInput.ID)
+ }
+
+ // delete the asset from state
+ err = ctx.GetStub().DelPrivateData(assetCollection, assetDeleteInput.ID)
+ if err != nil {
+ return fmt.Errorf("failed to delete state: %v", err)
+ }
+
+ // Finally, delete private details of asset
+ err = ctx.GetStub().DelPrivateData(ownerCollection, assetDeleteInput.ID)
+ if err != nil {
+ return err
+ }
+
+ return nil
+
+}
+
+// DeleteTranferAgreement can be used by the buyer to withdraw a proposal from
+// the asset collection and from his own collection.
+func (s *SmartContract) DeleteTranferAgreement(ctx contractapi.TransactionContextInterface) error {
+
+ transientMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("error getting transient: %v", err)
+ }
+
+ // Asset properties are private, therefore they get passed in transient field
+ transientDeleteJSON, ok := transientMap["agreement_delete"]
+ if !ok {
+ return fmt.Errorf("asset to delete not found in the transient map")
+ }
+
+ type assetDelete struct {
+ ID string `json:"assetID"`
+ }
+
+ var assetDeleteInput assetDelete
+ err = json.Unmarshal(transientDeleteJSON, &assetDeleteInput)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ if len(assetDeleteInput.ID) == 0 {
+ return fmt.Errorf("transient input ID field must be a non-empty string")
+ }
+
+ // Verify that the client is submitting request to peer in their organization
+ err = verifyClientOrgMatchesPeerOrg(ctx)
+ if err != nil {
+ return fmt.Errorf("DeleteTranferAgreement cannot be performed: Error %v", err)
+ }
+ // Delete private details of agreement
+ orgCollection, err := getCollectionName(ctx) // Get proposers collection.
+ if err != nil {
+ return fmt.Errorf("failed to infer private collection name for the org: %v", err)
+ }
+ tranferAgreeKey, err := ctx.GetStub().CreateCompositeKey(transferAgreementObjectType, []string{assetDeleteInput.
+ ID}) // Create composite key
+ if err != nil {
+ return fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ valAsbytes, err := ctx.GetStub().GetPrivateData(assetCollection, tranferAgreeKey) //get the transfer_agreement
+ if err != nil {
+ return fmt.Errorf("failed to read transfer_agreement: %v", err)
+ }
+ if valAsbytes == nil {
+ return fmt.Errorf("asset's transfer_agreement does not exist: %v", assetDeleteInput.ID)
+ }
+
+ log.Printf("Deleting TranferAgreement: %v", assetDeleteInput.ID)
+ err = ctx.GetStub().DelPrivateData(orgCollection, assetDeleteInput.ID) // Delete the asset
+ if err != nil {
+ return err
+ }
+
+ // Delete transfer agreement record
+ err = ctx.GetStub().DelPrivateData(assetCollection, tranferAgreeKey) // remove agreement from state
+ if err != nil {
+ return err
+ }
+
+ return nil
+
+}
+
+// getCollectionName is an internal helper function to get collection of submitting client identity.
+func getCollectionName(ctx contractapi.TransactionContextInterface) (string, error) {
+
+ // Get the MSP ID of submitting client identity
+ clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
+ if err != nil {
+ return "", fmt.Errorf("failed to get verified MSPID: %v", err)
+ }
+
+ // Create the collection name
+ orgCollection := clientMSPID + "PrivateCollection"
+
+ return orgCollection, nil
+}
+
+// verifyClientOrgMatchesPeerOrg is an internal function used verify client org id and matches peer org id.
+func verifyClientOrgMatchesPeerOrg(ctx contractapi.TransactionContextInterface) error {
+ clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
+ if err != nil {
+ return fmt.Errorf("failed getting the client's MSPID: %v", err)
+ }
+ peerMSPID, err := shim.GetMSPID()
+ if err != nil {
+ return fmt.Errorf("failed getting the peer's MSPID: %v", err)
+ }
+
+ if clientMSPID != peerMSPID {
+ return fmt.Errorf("client from org %v is not authorized to read or write private data from an org %v peer", clientMSPID, peerMSPID)
+ }
+
+ return nil
+}
+
+func submittingClientIdentity(ctx contractapi.TransactionContextInterface) (string, error) {
+ b64ID, err := ctx.GetClientIdentity().GetID()
+ if err != nil {
+ return "", fmt.Errorf("Failed to read clientID: %v", err)
+ }
+ decodeID, err := base64.StdEncoding.DecodeString(b64ID)
+ if err != nil {
+ return "", fmt.Errorf("failed to base64 decode clientID: %v", err)
+ }
+ return string(decodeID), nil
+}
diff --git a/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer_test.go b/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer_test.go
new file mode 100644
index 0000000..c6ec5ac
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer_test.go
@@ -0,0 +1,444 @@
+/*
+Copyright IBM Corp. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+package chaincode_test
+
+import (
+ "encoding/json"
+ "os"
+ "testing"
+
+ "github.com/hyperledger/fabric-chaincode-go/pkg/cid"
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+
+ "github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode"
+ "github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode/mocks"
+ "github.com/stretchr/testify/require"
+)
+
+/*
+These unit tests use mocks to simulate chaincode-api & fabric interactions
+The mocks are generated using counterfeiter directives in the comments (starting with "go:generate counterfeiter")
+All files in mocks/* are generated by running following, in the directory with your directive:
+ `go generate`
+*/
+
+//go:generate counterfeiter -o mocks/transaction.go -fake-name TransactionContext . transactionContext
+type transactionContext interface {
+ contractapi.TransactionContextInterface
+}
+
+//go:generate counterfeiter -o mocks/chaincodestub.go -fake-name ChaincodeStub . chaincodeStub
+type chaincodeStub interface {
+ shim.ChaincodeStubInterface
+}
+
+//go:generate counterfeiter -o mocks/statequeryiterator.go -fake-name StateQueryIterator . stateQueryIterator
+type stateQueryIterator interface {
+ shim.StateQueryIteratorInterface
+}
+
+//go:generate counterfeiter -o mocks/clientIdentity.go -fake-name ClientIdentity . clientIdentity
+type clientIdentity interface {
+ cid.ClientIdentity
+}
+
+const assetCollectionName = "assetCollection"
+const transferAgreementObjectType = "transferAgreement"
+const myOrg1Msp = "Org1Testmsp"
+const myOrg1Clientid = "myOrg1Userid"
+const myOrg1PrivCollection = "Org1TestmspPrivateCollection"
+const myOrg2Msp = "Org2Testmsp"
+const myOrg2Clientid = "myOrg2Userid"
+const myOrg2PrivCollection = "Org2TestmspPrivateCollection"
+
+type assetTransientInput struct {
+ Type string `json:"objectType"`
+ ID string `json:"assetID"`
+ Color string `json:"color"`
+ Size int `json:"size"`
+ AppraisedValue int `json:"appraisedValue"`
+}
+
+type assetTransferTransientInput struct {
+ ID string `json:"assetID"`
+ BuyerMSP string `json:"buyerMSP"`
+}
+
+func TestCreateAssetBadInput(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+
+ // No transient map
+ err := assetTransferCC.CreateAsset(transactionContext)
+ require.EqualError(t, err, "asset not found in the transient map input")
+
+ // transient map with incomplete asset data
+ assetPropMap := map[string][]byte{
+ "asset_properties": []byte("ill formatted property"),
+ }
+ chaincodeStub.GetTransientReturns(assetPropMap, nil)
+ err = assetTransferCC.CreateAsset(transactionContext)
+ require.Error(t, err, "Expected error: transient map with incomplete asset data")
+ require.Contains(t, err.Error(), "failed to unmarshal JSON")
+
+ testAsset := &assetTransientInput{
+ Type: "testfulasset",
+ }
+ setReturnAssetPropsInTransientMap(t, chaincodeStub, testAsset)
+ err = assetTransferCC.CreateAsset(transactionContext)
+ require.EqualError(t, err, "assetID field must be a non-empty string")
+
+ testAsset = &assetTransientInput{
+ ID: "id1",
+ Color: "gray",
+ }
+ setReturnAssetPropsInTransientMap(t, chaincodeStub, testAsset)
+ err = assetTransferCC.CreateAsset(transactionContext)
+ require.EqualError(t, err, "objectType field must be a non-empty string")
+
+ // case when asset exists, GetPrivateData returns a valid data from ledger
+ testAsset = &assetTransientInput{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ AppraisedValue: 500,
+ }
+ setReturnAssetPropsInTransientMap(t, chaincodeStub, testAsset)
+ chaincodeStub.GetPrivateDataReturns([]byte{}, nil)
+ err = assetTransferCC.CreateAsset(transactionContext)
+ require.EqualError(t, err, "this asset already exists: id1")
+}
+
+func TestCreateAssetSuccessful(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+ testAsset := &assetTransientInput{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ AppraisedValue: 500,
+ }
+ setReturnAssetPropsInTransientMap(t, chaincodeStub, testAsset)
+ err := assetTransferCC.CreateAsset(transactionContext)
+ require.NoError(t, err)
+ //Validate PutPrivateData calls
+ calledCollection, calledId, _ := chaincodeStub.PutPrivateDataArgsForCall(0)
+ require.Equal(t, assetCollectionName, calledCollection)
+ require.Equal(t, "id1", calledId)
+
+ expectedPrivateDetails := &chaincode.AssetPrivateDetails{
+ ID: "id1",
+ AppraisedValue: 500,
+ }
+ assetBytes, err := json.Marshal(expectedPrivateDetails)
+ calledCollection, calledId, calledAssetBytes := chaincodeStub.PutPrivateDataArgsForCall(1)
+ require.Equal(t, myOrg1PrivCollection, calledCollection)
+ require.Equal(t, "id1", calledId)
+ require.Equal(t, assetBytes, calledAssetBytes)
+}
+
+func TestAgreeToTransferBadInput(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+
+ assetPrivDetail := &chaincode.AssetPrivateDetails{
+ ID: "id1",
+ //no AppraisedValue
+ }
+ setReturnAssetPrivateDetailsInTransientMap(t, chaincodeStub, assetPrivDetail)
+ origAsset := chaincode.Asset{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ Owner: myOrg1Clientid,
+ }
+ setReturnPrivateDataInStub(t, chaincodeStub, &origAsset)
+
+ err := assetTransferCC.AgreeToTransfer(transactionContext)
+ require.EqualError(t, err, "appraisedValue field must be a positive integer")
+
+ assetPrivDetail = &chaincode.AssetPrivateDetails{
+ //no ID
+ AppraisedValue: 500,
+ }
+ setReturnAssetPrivateDetailsInTransientMap(t, chaincodeStub, assetPrivDetail)
+ err = assetTransferCC.AgreeToTransfer(transactionContext)
+ require.EqualError(t, err, "assetID field must be a non-empty string")
+
+ assetPrivDetail = &chaincode.AssetPrivateDetails{
+ ID: "id1",
+ AppraisedValue: 500,
+ }
+ setReturnAssetPrivateDetailsInTransientMap(t, chaincodeStub, assetPrivDetail)
+ //asset does not exist
+ setReturnPrivateDataInStub(t, chaincodeStub, nil)
+ err = assetTransferCC.AgreeToTransfer(transactionContext)
+ require.EqualError(t, err, "id1 does not exist")
+}
+
+func TestAgreeToTransferSuccessful(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+ assetPrivDetail := &chaincode.AssetPrivateDetails{
+ ID: "id1",
+ AppraisedValue: 500,
+ }
+ setReturnAssetPrivateDetailsInTransientMap(t, chaincodeStub, assetPrivDetail)
+ origAsset := chaincode.Asset{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ Owner: myOrg1Clientid,
+ }
+ setReturnPrivateDataInStub(t, chaincodeStub, &origAsset)
+ chaincodeStub.CreateCompositeKeyReturns(transferAgreementObjectType+"id1", nil)
+ err := assetTransferCC.AgreeToTransfer(transactionContext)
+ require.NoError(t, err)
+
+ expectedDataBytes, err := json.Marshal(assetPrivDetail)
+ calledCollection, calledId, calledWithDataBytes := chaincodeStub.PutPrivateDataArgsForCall(0)
+ require.Equal(t, myOrg1PrivCollection, calledCollection)
+ require.Equal(t, "id1", calledId)
+ require.Equal(t, expectedDataBytes, calledWithDataBytes)
+
+ calledCollection, calledId, calledWithDataBytes = chaincodeStub.PutPrivateDataArgsForCall(1)
+ require.Equal(t, assetCollectionName, calledCollection)
+ require.Equal(t, transferAgreementObjectType+"id1", calledId)
+ require.Equal(t, []byte(myOrg1Clientid), calledWithDataBytes)
+}
+func TestTransferAssetBadInput(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+
+ assetNewOwner := &assetTransferTransientInput{
+ ID: "id1",
+ BuyerMSP: "",
+ }
+ setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
+ setReturnPrivateDataInStub(t, chaincodeStub, &chaincode.Asset{})
+ err := assetTransferCC.TransferAsset(transactionContext)
+ require.EqualError(t, err, "buyerMSP field must be a non-empty string")
+
+ assetNewOwner = &assetTransferTransientInput{
+ ID: "id1",
+ BuyerMSP: myOrg2Msp,
+ }
+ setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
+ //asset does not exist
+ setReturnPrivateDataInStub(t, chaincodeStub, nil)
+ err = assetTransferCC.TransferAsset(transactionContext)
+ require.EqualError(t, err, "id1 does not exist")
+}
+
+func TestTransferAssetSuccessful(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+ assetNewOwner := &assetTransferTransientInput{
+ ID: "id1",
+ BuyerMSP: myOrg2Msp,
+ }
+ setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
+ origAsset := chaincode.Asset{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ Owner: myOrg1Clientid,
+ }
+ setReturnPrivateDataInStub(t, chaincodeStub, &origAsset)
+ //to ensure we pass data hash verification
+ chaincodeStub.GetPrivateDataHashReturns([]byte("datahash"), nil)
+ //to ensure that ReadTransferAgreement call returns org2 client ID
+ chaincodeStub.GetPrivateDataReturnsOnCall(1, []byte(myOrg2Clientid), nil)
+ chaincodeStub.CreateCompositeKeyReturns(transferAgreementObjectType+"id1", nil)
+
+ err := assetTransferCC.TransferAsset(transactionContext)
+ require.NoError(t, err)
+ //Validate PutPrivateData calls
+ expectedNewAsset := origAsset
+ expectedNewAsset.Owner = myOrg2Clientid
+ expectedNewAssetBytes, err := json.Marshal(expectedNewAsset)
+ require.NoError(t, err)
+ calledCollection, calledId, calledWithAssetBytes := chaincodeStub.PutPrivateDataArgsForCall(0)
+ require.Equal(t, assetCollectionName, calledCollection)
+ require.Equal(t, "id1", calledId)
+ require.Equal(t, expectedNewAssetBytes, calledWithAssetBytes)
+ calledCollection, calledId = chaincodeStub.DelPrivateDataArgsForCall(0)
+ require.Equal(t, myOrg1PrivCollection, calledCollection)
+ require.Equal(t, "id1", calledId)
+
+ calledCollection, calledId = chaincodeStub.DelPrivateDataArgsForCall(1)
+ require.Equal(t, assetCollectionName, calledCollection)
+ require.Equal(t, transferAgreementObjectType+"id1", calledId)
+
+}
+
+func TestTransferAssetByNonOwner(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+ assetNewOwner := &assetTransferTransientInput{
+ ID: "id1",
+ BuyerMSP: myOrg1Msp,
+ }
+ setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
+ //Try to transfer asset owned by Org2
+ org2Asset := chaincode.Asset{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ Owner: myOrg2Clientid,
+ }
+ setReturnPrivateDataInStub(t, chaincodeStub, &org2Asset)
+ err := assetTransferCC.TransferAsset(transactionContext)
+ require.EqualError(t, err, "failed transfer verification: error: submitting client identity does not own asset")
+}
+
+func TestTransferAssetWithoutAnAgreement(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+ assetNewOwner := &assetTransferTransientInput{
+ ID: "id1",
+ BuyerMSP: myOrg1Msp,
+ }
+ setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
+ orgAsset := chaincode.Asset{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ Owner: myOrg1Clientid,
+ }
+ setReturnPrivateDataInStub(t, chaincodeStub, &orgAsset)
+ //to ensure we pass data hash verification
+ chaincodeStub.GetPrivateDataHashReturns([]byte("datahash"), nil)
+ chaincodeStub.CreateCompositeKeyReturns(transferAgreementObjectType+"id1", nil)
+ //ReadTransferAgreement call returns no buyer client ID
+ chaincodeStub.GetPrivateDataReturnsOnCall(1, []byte{}, nil)
+
+ err := assetTransferCC.TransferAsset(transactionContext)
+ require.EqualError(t, err, "BuyerID not found in TransferAgreement for id1")
+}
+
+func TestTransferAssetNonMatchingAppraisalValue(t *testing.T) {
+ transactionContext, chaincodeStub := prepMocksAsOrg1()
+ assetTransferCC := chaincode.SmartContract{}
+ assetNewOwner := &assetTransferTransientInput{
+ ID: "id1",
+ BuyerMSP: myOrg2Msp,
+ }
+ setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
+
+ orgAsset := chaincode.Asset{
+ ID: "id1",
+ Type: "testfulasset",
+ Color: "gray",
+ Size: 7,
+ Owner: myOrg1Clientid,
+ }
+ setReturnPrivateDataInStub(t, chaincodeStub, &orgAsset)
+ chaincodeStub.CreateCompositeKeyReturns(transferAgreementObjectType+"id1", nil)
+ //data hash different in each collection
+ chaincodeStub.GetPrivateDataHashReturnsOnCall(0, []byte("datahash1"), nil)
+ chaincodeStub.GetPrivateDataHashReturnsOnCall(1, []byte("datahash2"), nil)
+
+ err := assetTransferCC.TransferAsset(transactionContext)
+ require.Error(t, err, "Expected failed hash verification")
+ require.Contains(t, err.Error(), "failed transfer verification: hash for appraised value")
+}
+
+func prepMocksAsOrg1() (*mocks.TransactionContext, *mocks.ChaincodeStub) {
+ return prepMocks(myOrg1Msp, myOrg1Clientid)
+}
+func prepMocksAsOrg2() (*mocks.TransactionContext, *mocks.ChaincodeStub) {
+ return prepMocks(myOrg2Msp, myOrg2Clientid)
+}
+func prepMocks(orgMSP, clientId string) (*mocks.TransactionContext, *mocks.ChaincodeStub) {
+ chaincodeStub := &mocks.ChaincodeStub{}
+ transactionContext := &mocks.TransactionContext{}
+ transactionContext.GetStubReturns(chaincodeStub)
+
+ clientIdentity := &mocks.ClientIdentity{}
+ clientIdentity.GetMSPIDReturns(orgMSP, nil)
+ clientIdentity.GetIDReturns(clientId, nil)
+ //set matching msp ID using peer shim env variable
+ os.Setenv("CORE_PEER_LOCALMSPID", orgMSP)
+ transactionContext.GetClientIdentityReturns(clientIdentity)
+ return transactionContext, chaincodeStub
+}
+
+func setReturnAssetPrivateDetailsInTransientMap(t *testing.T, chaincodeStub *mocks.ChaincodeStub, assetPrivDetail *chaincode.AssetPrivateDetails) []byte {
+ assetOwnerBytes := []byte{}
+ if assetPrivDetail != nil {
+ var err error
+ assetOwnerBytes, err = json.Marshal(assetPrivDetail)
+ require.NoError(t, err)
+ }
+ assetPropMap := map[string][]byte{
+ "asset_value": assetOwnerBytes,
+ }
+ chaincodeStub.GetTransientReturns(assetPropMap, nil)
+ return assetOwnerBytes
+}
+
+func setReturnAssetOwnerInTransientMap(t *testing.T, chaincodeStub *mocks.ChaincodeStub, assetOwner *assetTransferTransientInput) []byte {
+ assetOwnerBytes := []byte{}
+ if assetOwner != nil {
+ var err error
+ assetOwnerBytes, err = json.Marshal(assetOwner)
+ require.NoError(t, err)
+ }
+ assetPropMap := map[string][]byte{
+ "asset_owner": assetOwnerBytes,
+ }
+ chaincodeStub.GetTransientReturns(assetPropMap, nil)
+ return assetOwnerBytes
+}
+
+func setReturnAssetPropsInTransientMap(t *testing.T, chaincodeStub *mocks.ChaincodeStub, testAsset *assetTransientInput) []byte {
+ assetBytes := []byte{}
+ if testAsset != nil {
+ var err error
+ assetBytes, err = json.Marshal(testAsset)
+ require.NoError(t, err)
+ }
+ assetPropMap := map[string][]byte{
+ "asset_properties": assetBytes,
+ }
+ chaincodeStub.GetTransientReturns(assetPropMap, nil)
+ return assetBytes
+}
+
+func setReturnPrivateDataInStub(t *testing.T, chaincodeStub *mocks.ChaincodeStub, testAsset *chaincode.Asset) []byte {
+ if testAsset == nil {
+ chaincodeStub.GetPrivateDataReturns(nil, nil)
+ return nil
+ } else {
+ var err error
+ assetBytes, err := json.Marshal(testAsset)
+ require.NoError(t, err)
+ chaincodeStub.GetPrivateDataReturns(assetBytes, nil)
+ return assetBytes
+ }
+}
+
+func setReturnAssetPrivateDetailsInStub(t *testing.T, chaincodeStub *mocks.ChaincodeStub, testAsset *chaincode.AssetPrivateDetails) []byte {
+ if testAsset == nil {
+ chaincodeStub.GetPrivateDataReturns(nil, nil)
+ return nil
+ } else {
+ var err error
+ assetBytes, err := json.Marshal(testAsset)
+ require.NoError(t, err)
+ chaincodeStub.GetPrivateDataReturns(assetBytes, nil)
+ return assetBytes
+ }
+}
diff --git a/asset-transfer-private-data/chaincode-go/chaincode/mocks/chaincodestub.go b/asset-transfer-private-data/chaincode-go/chaincode/mocks/chaincodestub.go
new file mode 100644
index 0000000..eea9056
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/chaincode/mocks/chaincodestub.go
@@ -0,0 +1,2878 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package mocks
+
+import (
+ "sync"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-protos-go/peer"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+type ChaincodeStub struct {
+ CreateCompositeKeyStub func(string, []string) (string, error)
+ createCompositeKeyMutex sync.RWMutex
+ createCompositeKeyArgsForCall []struct {
+ arg1 string
+ arg2 []string
+ }
+ createCompositeKeyReturns struct {
+ result1 string
+ result2 error
+ }
+ createCompositeKeyReturnsOnCall map[int]struct {
+ result1 string
+ result2 error
+ }
+ DelPrivateDataStub func(string, string) error
+ delPrivateDataMutex sync.RWMutex
+ delPrivateDataArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ delPrivateDataReturns struct {
+ result1 error
+ }
+ delPrivateDataReturnsOnCall map[int]struct {
+ result1 error
+ }
+ DelStateStub func(string) error
+ delStateMutex sync.RWMutex
+ delStateArgsForCall []struct {
+ arg1 string
+ }
+ delStateReturns struct {
+ result1 error
+ }
+ delStateReturnsOnCall map[int]struct {
+ result1 error
+ }
+ GetArgsStub func() [][]byte
+ getArgsMutex sync.RWMutex
+ getArgsArgsForCall []struct {
+ }
+ getArgsReturns struct {
+ result1 [][]byte
+ }
+ getArgsReturnsOnCall map[int]struct {
+ result1 [][]byte
+ }
+ GetArgsSliceStub func() ([]byte, error)
+ getArgsSliceMutex sync.RWMutex
+ getArgsSliceArgsForCall []struct {
+ }
+ getArgsSliceReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getArgsSliceReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetBindingStub func() ([]byte, error)
+ getBindingMutex sync.RWMutex
+ getBindingArgsForCall []struct {
+ }
+ getBindingReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getBindingReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetChannelIDStub func() string
+ getChannelIDMutex sync.RWMutex
+ getChannelIDArgsForCall []struct {
+ }
+ getChannelIDReturns struct {
+ result1 string
+ }
+ getChannelIDReturnsOnCall map[int]struct {
+ result1 string
+ }
+ GetCreatorStub func() ([]byte, error)
+ getCreatorMutex sync.RWMutex
+ getCreatorArgsForCall []struct {
+ }
+ getCreatorReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getCreatorReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetDecorationsStub func() map[string][]byte
+ getDecorationsMutex sync.RWMutex
+ getDecorationsArgsForCall []struct {
+ }
+ getDecorationsReturns struct {
+ result1 map[string][]byte
+ }
+ getDecorationsReturnsOnCall map[int]struct {
+ result1 map[string][]byte
+ }
+ GetFunctionAndParametersStub func() (string, []string)
+ getFunctionAndParametersMutex sync.RWMutex
+ getFunctionAndParametersArgsForCall []struct {
+ }
+ getFunctionAndParametersReturns struct {
+ result1 string
+ result2 []string
+ }
+ getFunctionAndParametersReturnsOnCall map[int]struct {
+ result1 string
+ result2 []string
+ }
+ GetHistoryForKeyStub func(string) (shim.HistoryQueryIteratorInterface, error)
+ getHistoryForKeyMutex sync.RWMutex
+ getHistoryForKeyArgsForCall []struct {
+ arg1 string
+ }
+ getHistoryForKeyReturns struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ }
+ getHistoryForKeyReturnsOnCall map[int]struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ }
+ GetPrivateDataStub func(string, string) ([]byte, error)
+ getPrivateDataMutex sync.RWMutex
+ getPrivateDataArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getPrivateDataReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getPrivateDataReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetPrivateDataByPartialCompositeKeyStub func(string, string, []string) (shim.StateQueryIteratorInterface, error)
+ getPrivateDataByPartialCompositeKeyMutex sync.RWMutex
+ getPrivateDataByPartialCompositeKeyArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 []string
+ }
+ getPrivateDataByPartialCompositeKeyReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getPrivateDataByPartialCompositeKeyReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetPrivateDataByRangeStub func(string, string, string) (shim.StateQueryIteratorInterface, error)
+ getPrivateDataByRangeMutex sync.RWMutex
+ getPrivateDataByRangeArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 string
+ }
+ getPrivateDataByRangeReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getPrivateDataByRangeReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetPrivateDataHashStub func(string, string) ([]byte, error)
+ getPrivateDataHashMutex sync.RWMutex
+ getPrivateDataHashArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getPrivateDataHashReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getPrivateDataHashReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetPrivateDataQueryResultStub func(string, string) (shim.StateQueryIteratorInterface, error)
+ getPrivateDataQueryResultMutex sync.RWMutex
+ getPrivateDataQueryResultArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getPrivateDataQueryResultReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getPrivateDataQueryResultReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetPrivateDataValidationParameterStub func(string, string) ([]byte, error)
+ getPrivateDataValidationParameterMutex sync.RWMutex
+ getPrivateDataValidationParameterArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getPrivateDataValidationParameterReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getPrivateDataValidationParameterReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetQueryResultStub func(string) (shim.StateQueryIteratorInterface, error)
+ getQueryResultMutex sync.RWMutex
+ getQueryResultArgsForCall []struct {
+ arg1 string
+ }
+ getQueryResultReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getQueryResultReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetQueryResultWithPaginationStub func(string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)
+ getQueryResultWithPaginationMutex sync.RWMutex
+ getQueryResultWithPaginationArgsForCall []struct {
+ arg1 string
+ arg2 int32
+ arg3 string
+ }
+ getQueryResultWithPaginationReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ getQueryResultWithPaginationReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ GetSignedProposalStub func() (*peer.SignedProposal, error)
+ getSignedProposalMutex sync.RWMutex
+ getSignedProposalArgsForCall []struct {
+ }
+ getSignedProposalReturns struct {
+ result1 *peer.SignedProposal
+ result2 error
+ }
+ getSignedProposalReturnsOnCall map[int]struct {
+ result1 *peer.SignedProposal
+ result2 error
+ }
+ GetStateStub func(string) ([]byte, error)
+ getStateMutex sync.RWMutex
+ getStateArgsForCall []struct {
+ arg1 string
+ }
+ getStateReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getStateReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetStateByPartialCompositeKeyStub func(string, []string) (shim.StateQueryIteratorInterface, error)
+ getStateByPartialCompositeKeyMutex sync.RWMutex
+ getStateByPartialCompositeKeyArgsForCall []struct {
+ arg1 string
+ arg2 []string
+ }
+ getStateByPartialCompositeKeyReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getStateByPartialCompositeKeyReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetStateByPartialCompositeKeyWithPaginationStub func(string, []string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)
+ getStateByPartialCompositeKeyWithPaginationMutex sync.RWMutex
+ getStateByPartialCompositeKeyWithPaginationArgsForCall []struct {
+ arg1 string
+ arg2 []string
+ arg3 int32
+ arg4 string
+ }
+ getStateByPartialCompositeKeyWithPaginationReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ getStateByPartialCompositeKeyWithPaginationReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ GetStateByRangeStub func(string, string) (shim.StateQueryIteratorInterface, error)
+ getStateByRangeMutex sync.RWMutex
+ getStateByRangeArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ getStateByRangeReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ getStateByRangeReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }
+ GetStateByRangeWithPaginationStub func(string, string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)
+ getStateByRangeWithPaginationMutex sync.RWMutex
+ getStateByRangeWithPaginationArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 int32
+ arg4 string
+ }
+ getStateByRangeWithPaginationReturns struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ getStateByRangeWithPaginationReturnsOnCall map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }
+ GetStateValidationParameterStub func(string) ([]byte, error)
+ getStateValidationParameterMutex sync.RWMutex
+ getStateValidationParameterArgsForCall []struct {
+ arg1 string
+ }
+ getStateValidationParameterReturns struct {
+ result1 []byte
+ result2 error
+ }
+ getStateValidationParameterReturnsOnCall map[int]struct {
+ result1 []byte
+ result2 error
+ }
+ GetStringArgsStub func() []string
+ getStringArgsMutex sync.RWMutex
+ getStringArgsArgsForCall []struct {
+ }
+ getStringArgsReturns struct {
+ result1 []string
+ }
+ getStringArgsReturnsOnCall map[int]struct {
+ result1 []string
+ }
+ GetTransientStub func() (map[string][]byte, error)
+ getTransientMutex sync.RWMutex
+ getTransientArgsForCall []struct {
+ }
+ getTransientReturns struct {
+ result1 map[string][]byte
+ result2 error
+ }
+ getTransientReturnsOnCall map[int]struct {
+ result1 map[string][]byte
+ result2 error
+ }
+ GetTxIDStub func() string
+ getTxIDMutex sync.RWMutex
+ getTxIDArgsForCall []struct {
+ }
+ getTxIDReturns struct {
+ result1 string
+ }
+ getTxIDReturnsOnCall map[int]struct {
+ result1 string
+ }
+ GetTxTimestampStub func() (*timestamppb.Timestamp, error)
+ getTxTimestampMutex sync.RWMutex
+ getTxTimestampArgsForCall []struct {
+ }
+ getTxTimestampReturns struct {
+ result1 *timestamppb.Timestamp
+ result2 error
+ }
+ getTxTimestampReturnsOnCall map[int]struct {
+ result1 *timestamppb.Timestamp
+ result2 error
+ }
+ InvokeChaincodeStub func(string, [][]byte, string) peer.Response
+ invokeChaincodeMutex sync.RWMutex
+ invokeChaincodeArgsForCall []struct {
+ arg1 string
+ arg2 [][]byte
+ arg3 string
+ }
+ invokeChaincodeReturns struct {
+ result1 peer.Response
+ }
+ invokeChaincodeReturnsOnCall map[int]struct {
+ result1 peer.Response
+ }
+ PutPrivateDataStub func(string, string, []byte) error
+ putPrivateDataMutex sync.RWMutex
+ putPrivateDataArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 []byte
+ }
+ putPrivateDataReturns struct {
+ result1 error
+ }
+ putPrivateDataReturnsOnCall map[int]struct {
+ result1 error
+ }
+ PutStateStub func(string, []byte) error
+ putStateMutex sync.RWMutex
+ putStateArgsForCall []struct {
+ arg1 string
+ arg2 []byte
+ }
+ putStateReturns struct {
+ result1 error
+ }
+ putStateReturnsOnCall map[int]struct {
+ result1 error
+ }
+ SetEventStub func(string, []byte) error
+ setEventMutex sync.RWMutex
+ setEventArgsForCall []struct {
+ arg1 string
+ arg2 []byte
+ }
+ setEventReturns struct {
+ result1 error
+ }
+ setEventReturnsOnCall map[int]struct {
+ result1 error
+ }
+ SetPrivateDataValidationParameterStub func(string, string, []byte) error
+ setPrivateDataValidationParameterMutex sync.RWMutex
+ setPrivateDataValidationParameterArgsForCall []struct {
+ arg1 string
+ arg2 string
+ arg3 []byte
+ }
+ setPrivateDataValidationParameterReturns struct {
+ result1 error
+ }
+ setPrivateDataValidationParameterReturnsOnCall map[int]struct {
+ result1 error
+ }
+ SetStateValidationParameterStub func(string, []byte) error
+ setStateValidationParameterMutex sync.RWMutex
+ setStateValidationParameterArgsForCall []struct {
+ arg1 string
+ arg2 []byte
+ }
+ setStateValidationParameterReturns struct {
+ result1 error
+ }
+ setStateValidationParameterReturnsOnCall map[int]struct {
+ result1 error
+ }
+ SplitCompositeKeyStub func(string) (string, []string, error)
+ splitCompositeKeyMutex sync.RWMutex
+ splitCompositeKeyArgsForCall []struct {
+ arg1 string
+ }
+ splitCompositeKeyReturns struct {
+ result1 string
+ result2 []string
+ result3 error
+ }
+ splitCompositeKeyReturnsOnCall map[int]struct {
+ result1 string
+ result2 []string
+ result3 error
+ }
+ invocations map[string][][]interface{}
+ invocationsMutex sync.RWMutex
+}
+
+func (fake *ChaincodeStub) CreateCompositeKey(arg1 string, arg2 []string) (string, error) {
+ var arg2Copy []string
+ if arg2 != nil {
+ arg2Copy = make([]string, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.createCompositeKeyMutex.Lock()
+ ret, specificReturn := fake.createCompositeKeyReturnsOnCall[len(fake.createCompositeKeyArgsForCall)]
+ fake.createCompositeKeyArgsForCall = append(fake.createCompositeKeyArgsForCall, struct {
+ arg1 string
+ arg2 []string
+ }{arg1, arg2Copy})
+ fake.recordInvocation("CreateCompositeKey", []interface{}{arg1, arg2Copy})
+ fake.createCompositeKeyMutex.Unlock()
+ if fake.CreateCompositeKeyStub != nil {
+ return fake.CreateCompositeKeyStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.createCompositeKeyReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyCallCount() int {
+ fake.createCompositeKeyMutex.RLock()
+ defer fake.createCompositeKeyMutex.RUnlock()
+ return len(fake.createCompositeKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyCalls(stub func(string, []string) (string, error)) {
+ fake.createCompositeKeyMutex.Lock()
+ defer fake.createCompositeKeyMutex.Unlock()
+ fake.CreateCompositeKeyStub = stub
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyArgsForCall(i int) (string, []string) {
+ fake.createCompositeKeyMutex.RLock()
+ defer fake.createCompositeKeyMutex.RUnlock()
+ argsForCall := fake.createCompositeKeyArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyReturns(result1 string, result2 error) {
+ fake.createCompositeKeyMutex.Lock()
+ defer fake.createCompositeKeyMutex.Unlock()
+ fake.CreateCompositeKeyStub = nil
+ fake.createCompositeKeyReturns = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) CreateCompositeKeyReturnsOnCall(i int, result1 string, result2 error) {
+ fake.createCompositeKeyMutex.Lock()
+ defer fake.createCompositeKeyMutex.Unlock()
+ fake.CreateCompositeKeyStub = nil
+ if fake.createCompositeKeyReturnsOnCall == nil {
+ fake.createCompositeKeyReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 error
+ })
+ }
+ fake.createCompositeKeyReturnsOnCall[i] = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) DelPrivateData(arg1 string, arg2 string) error {
+ fake.delPrivateDataMutex.Lock()
+ ret, specificReturn := fake.delPrivateDataReturnsOnCall[len(fake.delPrivateDataArgsForCall)]
+ fake.delPrivateDataArgsForCall = append(fake.delPrivateDataArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("DelPrivateData", []interface{}{arg1, arg2})
+ fake.delPrivateDataMutex.Unlock()
+ if fake.DelPrivateDataStub != nil {
+ return fake.DelPrivateDataStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.delPrivateDataReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) DelPrivateDataCallCount() int {
+ fake.delPrivateDataMutex.RLock()
+ defer fake.delPrivateDataMutex.RUnlock()
+ return len(fake.delPrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) DelPrivateDataCalls(stub func(string, string) error) {
+ fake.delPrivateDataMutex.Lock()
+ defer fake.delPrivateDataMutex.Unlock()
+ fake.DelPrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) DelPrivateDataArgsForCall(i int) (string, string) {
+ fake.delPrivateDataMutex.RLock()
+ defer fake.delPrivateDataMutex.RUnlock()
+ argsForCall := fake.delPrivateDataArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) DelPrivateDataReturns(result1 error) {
+ fake.delPrivateDataMutex.Lock()
+ defer fake.delPrivateDataMutex.Unlock()
+ fake.DelPrivateDataStub = nil
+ fake.delPrivateDataReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) DelPrivateDataReturnsOnCall(i int, result1 error) {
+ fake.delPrivateDataMutex.Lock()
+ defer fake.delPrivateDataMutex.Unlock()
+ fake.DelPrivateDataStub = nil
+ if fake.delPrivateDataReturnsOnCall == nil {
+ fake.delPrivateDataReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.delPrivateDataReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) DelState(arg1 string) error {
+ fake.delStateMutex.Lock()
+ ret, specificReturn := fake.delStateReturnsOnCall[len(fake.delStateArgsForCall)]
+ fake.delStateArgsForCall = append(fake.delStateArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("DelState", []interface{}{arg1})
+ fake.delStateMutex.Unlock()
+ if fake.DelStateStub != nil {
+ return fake.DelStateStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.delStateReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) DelStateCallCount() int {
+ fake.delStateMutex.RLock()
+ defer fake.delStateMutex.RUnlock()
+ return len(fake.delStateArgsForCall)
+}
+
+func (fake *ChaincodeStub) DelStateCalls(stub func(string) error) {
+ fake.delStateMutex.Lock()
+ defer fake.delStateMutex.Unlock()
+ fake.DelStateStub = stub
+}
+
+func (fake *ChaincodeStub) DelStateArgsForCall(i int) string {
+ fake.delStateMutex.RLock()
+ defer fake.delStateMutex.RUnlock()
+ argsForCall := fake.delStateArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) DelStateReturns(result1 error) {
+ fake.delStateMutex.Lock()
+ defer fake.delStateMutex.Unlock()
+ fake.DelStateStub = nil
+ fake.delStateReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) DelStateReturnsOnCall(i int, result1 error) {
+ fake.delStateMutex.Lock()
+ defer fake.delStateMutex.Unlock()
+ fake.DelStateStub = nil
+ if fake.delStateReturnsOnCall == nil {
+ fake.delStateReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.delStateReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetArgs() [][]byte {
+ fake.getArgsMutex.Lock()
+ ret, specificReturn := fake.getArgsReturnsOnCall[len(fake.getArgsArgsForCall)]
+ fake.getArgsArgsForCall = append(fake.getArgsArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetArgs", []interface{}{})
+ fake.getArgsMutex.Unlock()
+ if fake.GetArgsStub != nil {
+ return fake.GetArgsStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getArgsReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetArgsCallCount() int {
+ fake.getArgsMutex.RLock()
+ defer fake.getArgsMutex.RUnlock()
+ return len(fake.getArgsArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetArgsCalls(stub func() [][]byte) {
+ fake.getArgsMutex.Lock()
+ defer fake.getArgsMutex.Unlock()
+ fake.GetArgsStub = stub
+}
+
+func (fake *ChaincodeStub) GetArgsReturns(result1 [][]byte) {
+ fake.getArgsMutex.Lock()
+ defer fake.getArgsMutex.Unlock()
+ fake.GetArgsStub = nil
+ fake.getArgsReturns = struct {
+ result1 [][]byte
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetArgsReturnsOnCall(i int, result1 [][]byte) {
+ fake.getArgsMutex.Lock()
+ defer fake.getArgsMutex.Unlock()
+ fake.GetArgsStub = nil
+ if fake.getArgsReturnsOnCall == nil {
+ fake.getArgsReturnsOnCall = make(map[int]struct {
+ result1 [][]byte
+ })
+ }
+ fake.getArgsReturnsOnCall[i] = struct {
+ result1 [][]byte
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetArgsSlice() ([]byte, error) {
+ fake.getArgsSliceMutex.Lock()
+ ret, specificReturn := fake.getArgsSliceReturnsOnCall[len(fake.getArgsSliceArgsForCall)]
+ fake.getArgsSliceArgsForCall = append(fake.getArgsSliceArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetArgsSlice", []interface{}{})
+ fake.getArgsSliceMutex.Unlock()
+ if fake.GetArgsSliceStub != nil {
+ return fake.GetArgsSliceStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getArgsSliceReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetArgsSliceCallCount() int {
+ fake.getArgsSliceMutex.RLock()
+ defer fake.getArgsSliceMutex.RUnlock()
+ return len(fake.getArgsSliceArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetArgsSliceCalls(stub func() ([]byte, error)) {
+ fake.getArgsSliceMutex.Lock()
+ defer fake.getArgsSliceMutex.Unlock()
+ fake.GetArgsSliceStub = stub
+}
+
+func (fake *ChaincodeStub) GetArgsSliceReturns(result1 []byte, result2 error) {
+ fake.getArgsSliceMutex.Lock()
+ defer fake.getArgsSliceMutex.Unlock()
+ fake.GetArgsSliceStub = nil
+ fake.getArgsSliceReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetArgsSliceReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getArgsSliceMutex.Lock()
+ defer fake.getArgsSliceMutex.Unlock()
+ fake.GetArgsSliceStub = nil
+ if fake.getArgsSliceReturnsOnCall == nil {
+ fake.getArgsSliceReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getArgsSliceReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetBinding() ([]byte, error) {
+ fake.getBindingMutex.Lock()
+ ret, specificReturn := fake.getBindingReturnsOnCall[len(fake.getBindingArgsForCall)]
+ fake.getBindingArgsForCall = append(fake.getBindingArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetBinding", []interface{}{})
+ fake.getBindingMutex.Unlock()
+ if fake.GetBindingStub != nil {
+ return fake.GetBindingStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getBindingReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetBindingCallCount() int {
+ fake.getBindingMutex.RLock()
+ defer fake.getBindingMutex.RUnlock()
+ return len(fake.getBindingArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetBindingCalls(stub func() ([]byte, error)) {
+ fake.getBindingMutex.Lock()
+ defer fake.getBindingMutex.Unlock()
+ fake.GetBindingStub = stub
+}
+
+func (fake *ChaincodeStub) GetBindingReturns(result1 []byte, result2 error) {
+ fake.getBindingMutex.Lock()
+ defer fake.getBindingMutex.Unlock()
+ fake.GetBindingStub = nil
+ fake.getBindingReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetBindingReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getBindingMutex.Lock()
+ defer fake.getBindingMutex.Unlock()
+ fake.GetBindingStub = nil
+ if fake.getBindingReturnsOnCall == nil {
+ fake.getBindingReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getBindingReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetChannelID() string {
+ fake.getChannelIDMutex.Lock()
+ ret, specificReturn := fake.getChannelIDReturnsOnCall[len(fake.getChannelIDArgsForCall)]
+ fake.getChannelIDArgsForCall = append(fake.getChannelIDArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetChannelID", []interface{}{})
+ fake.getChannelIDMutex.Unlock()
+ if fake.GetChannelIDStub != nil {
+ return fake.GetChannelIDStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getChannelIDReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetChannelIDCallCount() int {
+ fake.getChannelIDMutex.RLock()
+ defer fake.getChannelIDMutex.RUnlock()
+ return len(fake.getChannelIDArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetChannelIDCalls(stub func() string) {
+ fake.getChannelIDMutex.Lock()
+ defer fake.getChannelIDMutex.Unlock()
+ fake.GetChannelIDStub = stub
+}
+
+func (fake *ChaincodeStub) GetChannelIDReturns(result1 string) {
+ fake.getChannelIDMutex.Lock()
+ defer fake.getChannelIDMutex.Unlock()
+ fake.GetChannelIDStub = nil
+ fake.getChannelIDReturns = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetChannelIDReturnsOnCall(i int, result1 string) {
+ fake.getChannelIDMutex.Lock()
+ defer fake.getChannelIDMutex.Unlock()
+ fake.GetChannelIDStub = nil
+ if fake.getChannelIDReturnsOnCall == nil {
+ fake.getChannelIDReturnsOnCall = make(map[int]struct {
+ result1 string
+ })
+ }
+ fake.getChannelIDReturnsOnCall[i] = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetCreator() ([]byte, error) {
+ fake.getCreatorMutex.Lock()
+ ret, specificReturn := fake.getCreatorReturnsOnCall[len(fake.getCreatorArgsForCall)]
+ fake.getCreatorArgsForCall = append(fake.getCreatorArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetCreator", []interface{}{})
+ fake.getCreatorMutex.Unlock()
+ if fake.GetCreatorStub != nil {
+ return fake.GetCreatorStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getCreatorReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetCreatorCallCount() int {
+ fake.getCreatorMutex.RLock()
+ defer fake.getCreatorMutex.RUnlock()
+ return len(fake.getCreatorArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetCreatorCalls(stub func() ([]byte, error)) {
+ fake.getCreatorMutex.Lock()
+ defer fake.getCreatorMutex.Unlock()
+ fake.GetCreatorStub = stub
+}
+
+func (fake *ChaincodeStub) GetCreatorReturns(result1 []byte, result2 error) {
+ fake.getCreatorMutex.Lock()
+ defer fake.getCreatorMutex.Unlock()
+ fake.GetCreatorStub = nil
+ fake.getCreatorReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetCreatorReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getCreatorMutex.Lock()
+ defer fake.getCreatorMutex.Unlock()
+ fake.GetCreatorStub = nil
+ if fake.getCreatorReturnsOnCall == nil {
+ fake.getCreatorReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getCreatorReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetDecorations() map[string][]byte {
+ fake.getDecorationsMutex.Lock()
+ ret, specificReturn := fake.getDecorationsReturnsOnCall[len(fake.getDecorationsArgsForCall)]
+ fake.getDecorationsArgsForCall = append(fake.getDecorationsArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetDecorations", []interface{}{})
+ fake.getDecorationsMutex.Unlock()
+ if fake.GetDecorationsStub != nil {
+ return fake.GetDecorationsStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getDecorationsReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetDecorationsCallCount() int {
+ fake.getDecorationsMutex.RLock()
+ defer fake.getDecorationsMutex.RUnlock()
+ return len(fake.getDecorationsArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetDecorationsCalls(stub func() map[string][]byte) {
+ fake.getDecorationsMutex.Lock()
+ defer fake.getDecorationsMutex.Unlock()
+ fake.GetDecorationsStub = stub
+}
+
+func (fake *ChaincodeStub) GetDecorationsReturns(result1 map[string][]byte) {
+ fake.getDecorationsMutex.Lock()
+ defer fake.getDecorationsMutex.Unlock()
+ fake.GetDecorationsStub = nil
+ fake.getDecorationsReturns = struct {
+ result1 map[string][]byte
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetDecorationsReturnsOnCall(i int, result1 map[string][]byte) {
+ fake.getDecorationsMutex.Lock()
+ defer fake.getDecorationsMutex.Unlock()
+ fake.GetDecorationsStub = nil
+ if fake.getDecorationsReturnsOnCall == nil {
+ fake.getDecorationsReturnsOnCall = make(map[int]struct {
+ result1 map[string][]byte
+ })
+ }
+ fake.getDecorationsReturnsOnCall[i] = struct {
+ result1 map[string][]byte
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParameters() (string, []string) {
+ fake.getFunctionAndParametersMutex.Lock()
+ ret, specificReturn := fake.getFunctionAndParametersReturnsOnCall[len(fake.getFunctionAndParametersArgsForCall)]
+ fake.getFunctionAndParametersArgsForCall = append(fake.getFunctionAndParametersArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetFunctionAndParameters", []interface{}{})
+ fake.getFunctionAndParametersMutex.Unlock()
+ if fake.GetFunctionAndParametersStub != nil {
+ return fake.GetFunctionAndParametersStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getFunctionAndParametersReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParametersCallCount() int {
+ fake.getFunctionAndParametersMutex.RLock()
+ defer fake.getFunctionAndParametersMutex.RUnlock()
+ return len(fake.getFunctionAndParametersArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParametersCalls(stub func() (string, []string)) {
+ fake.getFunctionAndParametersMutex.Lock()
+ defer fake.getFunctionAndParametersMutex.Unlock()
+ fake.GetFunctionAndParametersStub = stub
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParametersReturns(result1 string, result2 []string) {
+ fake.getFunctionAndParametersMutex.Lock()
+ defer fake.getFunctionAndParametersMutex.Unlock()
+ fake.GetFunctionAndParametersStub = nil
+ fake.getFunctionAndParametersReturns = struct {
+ result1 string
+ result2 []string
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetFunctionAndParametersReturnsOnCall(i int, result1 string, result2 []string) {
+ fake.getFunctionAndParametersMutex.Lock()
+ defer fake.getFunctionAndParametersMutex.Unlock()
+ fake.GetFunctionAndParametersStub = nil
+ if fake.getFunctionAndParametersReturnsOnCall == nil {
+ fake.getFunctionAndParametersReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 []string
+ })
+ }
+ fake.getFunctionAndParametersReturnsOnCall[i] = struct {
+ result1 string
+ result2 []string
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetHistoryForKey(arg1 string) (shim.HistoryQueryIteratorInterface, error) {
+ fake.getHistoryForKeyMutex.Lock()
+ ret, specificReturn := fake.getHistoryForKeyReturnsOnCall[len(fake.getHistoryForKeyArgsForCall)]
+ fake.getHistoryForKeyArgsForCall = append(fake.getHistoryForKeyArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetHistoryForKey", []interface{}{arg1})
+ fake.getHistoryForKeyMutex.Unlock()
+ if fake.GetHistoryForKeyStub != nil {
+ return fake.GetHistoryForKeyStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getHistoryForKeyReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyCallCount() int {
+ fake.getHistoryForKeyMutex.RLock()
+ defer fake.getHistoryForKeyMutex.RUnlock()
+ return len(fake.getHistoryForKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyCalls(stub func(string) (shim.HistoryQueryIteratorInterface, error)) {
+ fake.getHistoryForKeyMutex.Lock()
+ defer fake.getHistoryForKeyMutex.Unlock()
+ fake.GetHistoryForKeyStub = stub
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyArgsForCall(i int) string {
+ fake.getHistoryForKeyMutex.RLock()
+ defer fake.getHistoryForKeyMutex.RUnlock()
+ argsForCall := fake.getHistoryForKeyArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyReturns(result1 shim.HistoryQueryIteratorInterface, result2 error) {
+ fake.getHistoryForKeyMutex.Lock()
+ defer fake.getHistoryForKeyMutex.Unlock()
+ fake.GetHistoryForKeyStub = nil
+ fake.getHistoryForKeyReturns = struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetHistoryForKeyReturnsOnCall(i int, result1 shim.HistoryQueryIteratorInterface, result2 error) {
+ fake.getHistoryForKeyMutex.Lock()
+ defer fake.getHistoryForKeyMutex.Unlock()
+ fake.GetHistoryForKeyStub = nil
+ if fake.getHistoryForKeyReturnsOnCall == nil {
+ fake.getHistoryForKeyReturnsOnCall = make(map[int]struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getHistoryForKeyReturnsOnCall[i] = struct {
+ result1 shim.HistoryQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateData(arg1 string, arg2 string) ([]byte, error) {
+ fake.getPrivateDataMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataReturnsOnCall[len(fake.getPrivateDataArgsForCall)]
+ fake.getPrivateDataArgsForCall = append(fake.getPrivateDataArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetPrivateData", []interface{}{arg1, arg2})
+ fake.getPrivateDataMutex.Unlock()
+ if fake.GetPrivateDataStub != nil {
+ return fake.GetPrivateDataStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataCallCount() int {
+ fake.getPrivateDataMutex.RLock()
+ defer fake.getPrivateDataMutex.RUnlock()
+ return len(fake.getPrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataCalls(stub func(string, string) ([]byte, error)) {
+ fake.getPrivateDataMutex.Lock()
+ defer fake.getPrivateDataMutex.Unlock()
+ fake.GetPrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataArgsForCall(i int) (string, string) {
+ fake.getPrivateDataMutex.RLock()
+ defer fake.getPrivateDataMutex.RUnlock()
+ argsForCall := fake.getPrivateDataArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataReturns(result1 []byte, result2 error) {
+ fake.getPrivateDataMutex.Lock()
+ defer fake.getPrivateDataMutex.Unlock()
+ fake.GetPrivateDataStub = nil
+ fake.getPrivateDataReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getPrivateDataMutex.Lock()
+ defer fake.getPrivateDataMutex.Unlock()
+ fake.GetPrivateDataStub = nil
+ if fake.getPrivateDataReturnsOnCall == nil {
+ fake.getPrivateDataReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getPrivateDataReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKey(arg1 string, arg2 string, arg3 []string) (shim.StateQueryIteratorInterface, error) {
+ var arg3Copy []string
+ if arg3 != nil {
+ arg3Copy = make([]string, len(arg3))
+ copy(arg3Copy, arg3)
+ }
+ fake.getPrivateDataByPartialCompositeKeyMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataByPartialCompositeKeyReturnsOnCall[len(fake.getPrivateDataByPartialCompositeKeyArgsForCall)]
+ fake.getPrivateDataByPartialCompositeKeyArgsForCall = append(fake.getPrivateDataByPartialCompositeKeyArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 []string
+ }{arg1, arg2, arg3Copy})
+ fake.recordInvocation("GetPrivateDataByPartialCompositeKey", []interface{}{arg1, arg2, arg3Copy})
+ fake.getPrivateDataByPartialCompositeKeyMutex.Unlock()
+ if fake.GetPrivateDataByPartialCompositeKeyStub != nil {
+ return fake.GetPrivateDataByPartialCompositeKeyStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataByPartialCompositeKeyReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyCallCount() int {
+ fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.RUnlock()
+ return len(fake.getPrivateDataByPartialCompositeKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyCalls(stub func(string, string, []string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getPrivateDataByPartialCompositeKeyMutex.Lock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.Unlock()
+ fake.GetPrivateDataByPartialCompositeKeyStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyArgsForCall(i int) (string, string, []string) {
+ fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.RUnlock()
+ argsForCall := fake.getPrivateDataByPartialCompositeKeyArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataByPartialCompositeKeyMutex.Lock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.Unlock()
+ fake.GetPrivateDataByPartialCompositeKeyStub = nil
+ fake.getPrivateDataByPartialCompositeKeyReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByPartialCompositeKeyReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataByPartialCompositeKeyMutex.Lock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.Unlock()
+ fake.GetPrivateDataByPartialCompositeKeyStub = nil
+ if fake.getPrivateDataByPartialCompositeKeyReturnsOnCall == nil {
+ fake.getPrivateDataByPartialCompositeKeyReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getPrivateDataByPartialCompositeKeyReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRange(arg1 string, arg2 string, arg3 string) (shim.StateQueryIteratorInterface, error) {
+ fake.getPrivateDataByRangeMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataByRangeReturnsOnCall[len(fake.getPrivateDataByRangeArgsForCall)]
+ fake.getPrivateDataByRangeArgsForCall = append(fake.getPrivateDataByRangeArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 string
+ }{arg1, arg2, arg3})
+ fake.recordInvocation("GetPrivateDataByRange", []interface{}{arg1, arg2, arg3})
+ fake.getPrivateDataByRangeMutex.Unlock()
+ if fake.GetPrivateDataByRangeStub != nil {
+ return fake.GetPrivateDataByRangeStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataByRangeReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeCallCount() int {
+ fake.getPrivateDataByRangeMutex.RLock()
+ defer fake.getPrivateDataByRangeMutex.RUnlock()
+ return len(fake.getPrivateDataByRangeArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeCalls(stub func(string, string, string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getPrivateDataByRangeMutex.Lock()
+ defer fake.getPrivateDataByRangeMutex.Unlock()
+ fake.GetPrivateDataByRangeStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeArgsForCall(i int) (string, string, string) {
+ fake.getPrivateDataByRangeMutex.RLock()
+ defer fake.getPrivateDataByRangeMutex.RUnlock()
+ argsForCall := fake.getPrivateDataByRangeArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataByRangeMutex.Lock()
+ defer fake.getPrivateDataByRangeMutex.Unlock()
+ fake.GetPrivateDataByRangeStub = nil
+ fake.getPrivateDataByRangeReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataByRangeReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataByRangeMutex.Lock()
+ defer fake.getPrivateDataByRangeMutex.Unlock()
+ fake.GetPrivateDataByRangeStub = nil
+ if fake.getPrivateDataByRangeReturnsOnCall == nil {
+ fake.getPrivateDataByRangeReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getPrivateDataByRangeReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHash(arg1 string, arg2 string) ([]byte, error) {
+ fake.getPrivateDataHashMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataHashReturnsOnCall[len(fake.getPrivateDataHashArgsForCall)]
+ fake.getPrivateDataHashArgsForCall = append(fake.getPrivateDataHashArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetPrivateDataHash", []interface{}{arg1, arg2})
+ fake.getPrivateDataHashMutex.Unlock()
+ if fake.GetPrivateDataHashStub != nil {
+ return fake.GetPrivateDataHashStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataHashReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashCallCount() int {
+ fake.getPrivateDataHashMutex.RLock()
+ defer fake.getPrivateDataHashMutex.RUnlock()
+ return len(fake.getPrivateDataHashArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashCalls(stub func(string, string) ([]byte, error)) {
+ fake.getPrivateDataHashMutex.Lock()
+ defer fake.getPrivateDataHashMutex.Unlock()
+ fake.GetPrivateDataHashStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashArgsForCall(i int) (string, string) {
+ fake.getPrivateDataHashMutex.RLock()
+ defer fake.getPrivateDataHashMutex.RUnlock()
+ argsForCall := fake.getPrivateDataHashArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashReturns(result1 []byte, result2 error) {
+ fake.getPrivateDataHashMutex.Lock()
+ defer fake.getPrivateDataHashMutex.Unlock()
+ fake.GetPrivateDataHashStub = nil
+ fake.getPrivateDataHashReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataHashReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getPrivateDataHashMutex.Lock()
+ defer fake.getPrivateDataHashMutex.Unlock()
+ fake.GetPrivateDataHashStub = nil
+ if fake.getPrivateDataHashReturnsOnCall == nil {
+ fake.getPrivateDataHashReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getPrivateDataHashReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResult(arg1 string, arg2 string) (shim.StateQueryIteratorInterface, error) {
+ fake.getPrivateDataQueryResultMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataQueryResultReturnsOnCall[len(fake.getPrivateDataQueryResultArgsForCall)]
+ fake.getPrivateDataQueryResultArgsForCall = append(fake.getPrivateDataQueryResultArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetPrivateDataQueryResult", []interface{}{arg1, arg2})
+ fake.getPrivateDataQueryResultMutex.Unlock()
+ if fake.GetPrivateDataQueryResultStub != nil {
+ return fake.GetPrivateDataQueryResultStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataQueryResultReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultCallCount() int {
+ fake.getPrivateDataQueryResultMutex.RLock()
+ defer fake.getPrivateDataQueryResultMutex.RUnlock()
+ return len(fake.getPrivateDataQueryResultArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultCalls(stub func(string, string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getPrivateDataQueryResultMutex.Lock()
+ defer fake.getPrivateDataQueryResultMutex.Unlock()
+ fake.GetPrivateDataQueryResultStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultArgsForCall(i int) (string, string) {
+ fake.getPrivateDataQueryResultMutex.RLock()
+ defer fake.getPrivateDataQueryResultMutex.RUnlock()
+ argsForCall := fake.getPrivateDataQueryResultArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataQueryResultMutex.Lock()
+ defer fake.getPrivateDataQueryResultMutex.Unlock()
+ fake.GetPrivateDataQueryResultStub = nil
+ fake.getPrivateDataQueryResultReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataQueryResultReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getPrivateDataQueryResultMutex.Lock()
+ defer fake.getPrivateDataQueryResultMutex.Unlock()
+ fake.GetPrivateDataQueryResultStub = nil
+ if fake.getPrivateDataQueryResultReturnsOnCall == nil {
+ fake.getPrivateDataQueryResultReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getPrivateDataQueryResultReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameter(arg1 string, arg2 string) ([]byte, error) {
+ fake.getPrivateDataValidationParameterMutex.Lock()
+ ret, specificReturn := fake.getPrivateDataValidationParameterReturnsOnCall[len(fake.getPrivateDataValidationParameterArgsForCall)]
+ fake.getPrivateDataValidationParameterArgsForCall = append(fake.getPrivateDataValidationParameterArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetPrivateDataValidationParameter", []interface{}{arg1, arg2})
+ fake.getPrivateDataValidationParameterMutex.Unlock()
+ if fake.GetPrivateDataValidationParameterStub != nil {
+ return fake.GetPrivateDataValidationParameterStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getPrivateDataValidationParameterReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterCallCount() int {
+ fake.getPrivateDataValidationParameterMutex.RLock()
+ defer fake.getPrivateDataValidationParameterMutex.RUnlock()
+ return len(fake.getPrivateDataValidationParameterArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterCalls(stub func(string, string) ([]byte, error)) {
+ fake.getPrivateDataValidationParameterMutex.Lock()
+ defer fake.getPrivateDataValidationParameterMutex.Unlock()
+ fake.GetPrivateDataValidationParameterStub = stub
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterArgsForCall(i int) (string, string) {
+ fake.getPrivateDataValidationParameterMutex.RLock()
+ defer fake.getPrivateDataValidationParameterMutex.RUnlock()
+ argsForCall := fake.getPrivateDataValidationParameterArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterReturns(result1 []byte, result2 error) {
+ fake.getPrivateDataValidationParameterMutex.Lock()
+ defer fake.getPrivateDataValidationParameterMutex.Unlock()
+ fake.GetPrivateDataValidationParameterStub = nil
+ fake.getPrivateDataValidationParameterReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetPrivateDataValidationParameterReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getPrivateDataValidationParameterMutex.Lock()
+ defer fake.getPrivateDataValidationParameterMutex.Unlock()
+ fake.GetPrivateDataValidationParameterStub = nil
+ if fake.getPrivateDataValidationParameterReturnsOnCall == nil {
+ fake.getPrivateDataValidationParameterReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getPrivateDataValidationParameterReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetQueryResult(arg1 string) (shim.StateQueryIteratorInterface, error) {
+ fake.getQueryResultMutex.Lock()
+ ret, specificReturn := fake.getQueryResultReturnsOnCall[len(fake.getQueryResultArgsForCall)]
+ fake.getQueryResultArgsForCall = append(fake.getQueryResultArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetQueryResult", []interface{}{arg1})
+ fake.getQueryResultMutex.Unlock()
+ if fake.GetQueryResultStub != nil {
+ return fake.GetQueryResultStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getQueryResultReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetQueryResultCallCount() int {
+ fake.getQueryResultMutex.RLock()
+ defer fake.getQueryResultMutex.RUnlock()
+ return len(fake.getQueryResultArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetQueryResultCalls(stub func(string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getQueryResultMutex.Lock()
+ defer fake.getQueryResultMutex.Unlock()
+ fake.GetQueryResultStub = stub
+}
+
+func (fake *ChaincodeStub) GetQueryResultArgsForCall(i int) string {
+ fake.getQueryResultMutex.RLock()
+ defer fake.getQueryResultMutex.RUnlock()
+ argsForCall := fake.getQueryResultArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetQueryResultReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getQueryResultMutex.Lock()
+ defer fake.getQueryResultMutex.Unlock()
+ fake.GetQueryResultStub = nil
+ fake.getQueryResultReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetQueryResultReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getQueryResultMutex.Lock()
+ defer fake.getQueryResultMutex.Unlock()
+ fake.GetQueryResultStub = nil
+ if fake.getQueryResultReturnsOnCall == nil {
+ fake.getQueryResultReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getQueryResultReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPagination(arg1 string, arg2 int32, arg3 string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) {
+ fake.getQueryResultWithPaginationMutex.Lock()
+ ret, specificReturn := fake.getQueryResultWithPaginationReturnsOnCall[len(fake.getQueryResultWithPaginationArgsForCall)]
+ fake.getQueryResultWithPaginationArgsForCall = append(fake.getQueryResultWithPaginationArgsForCall, struct {
+ arg1 string
+ arg2 int32
+ arg3 string
+ }{arg1, arg2, arg3})
+ fake.recordInvocation("GetQueryResultWithPagination", []interface{}{arg1, arg2, arg3})
+ fake.getQueryResultWithPaginationMutex.Unlock()
+ if fake.GetQueryResultWithPaginationStub != nil {
+ return fake.GetQueryResultWithPaginationStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.getQueryResultWithPaginationReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationCallCount() int {
+ fake.getQueryResultWithPaginationMutex.RLock()
+ defer fake.getQueryResultWithPaginationMutex.RUnlock()
+ return len(fake.getQueryResultWithPaginationArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationCalls(stub func(string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) {
+ fake.getQueryResultWithPaginationMutex.Lock()
+ defer fake.getQueryResultWithPaginationMutex.Unlock()
+ fake.GetQueryResultWithPaginationStub = stub
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationArgsForCall(i int) (string, int32, string) {
+ fake.getQueryResultWithPaginationMutex.RLock()
+ defer fake.getQueryResultWithPaginationMutex.RUnlock()
+ argsForCall := fake.getQueryResultWithPaginationArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationReturns(result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getQueryResultWithPaginationMutex.Lock()
+ defer fake.getQueryResultWithPaginationMutex.Unlock()
+ fake.GetQueryResultWithPaginationStub = nil
+ fake.getQueryResultWithPaginationReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetQueryResultWithPaginationReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getQueryResultWithPaginationMutex.Lock()
+ defer fake.getQueryResultWithPaginationMutex.Unlock()
+ fake.GetQueryResultWithPaginationStub = nil
+ if fake.getQueryResultWithPaginationReturnsOnCall == nil {
+ fake.getQueryResultWithPaginationReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ })
+ }
+ fake.getQueryResultWithPaginationReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetSignedProposal() (*peer.SignedProposal, error) {
+ fake.getSignedProposalMutex.Lock()
+ ret, specificReturn := fake.getSignedProposalReturnsOnCall[len(fake.getSignedProposalArgsForCall)]
+ fake.getSignedProposalArgsForCall = append(fake.getSignedProposalArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetSignedProposal", []interface{}{})
+ fake.getSignedProposalMutex.Unlock()
+ if fake.GetSignedProposalStub != nil {
+ return fake.GetSignedProposalStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getSignedProposalReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetSignedProposalCallCount() int {
+ fake.getSignedProposalMutex.RLock()
+ defer fake.getSignedProposalMutex.RUnlock()
+ return len(fake.getSignedProposalArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetSignedProposalCalls(stub func() (*peer.SignedProposal, error)) {
+ fake.getSignedProposalMutex.Lock()
+ defer fake.getSignedProposalMutex.Unlock()
+ fake.GetSignedProposalStub = stub
+}
+
+func (fake *ChaincodeStub) GetSignedProposalReturns(result1 *peer.SignedProposal, result2 error) {
+ fake.getSignedProposalMutex.Lock()
+ defer fake.getSignedProposalMutex.Unlock()
+ fake.GetSignedProposalStub = nil
+ fake.getSignedProposalReturns = struct {
+ result1 *peer.SignedProposal
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetSignedProposalReturnsOnCall(i int, result1 *peer.SignedProposal, result2 error) {
+ fake.getSignedProposalMutex.Lock()
+ defer fake.getSignedProposalMutex.Unlock()
+ fake.GetSignedProposalStub = nil
+ if fake.getSignedProposalReturnsOnCall == nil {
+ fake.getSignedProposalReturnsOnCall = make(map[int]struct {
+ result1 *peer.SignedProposal
+ result2 error
+ })
+ }
+ fake.getSignedProposalReturnsOnCall[i] = struct {
+ result1 *peer.SignedProposal
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetState(arg1 string) ([]byte, error) {
+ fake.getStateMutex.Lock()
+ ret, specificReturn := fake.getStateReturnsOnCall[len(fake.getStateArgsForCall)]
+ fake.getStateArgsForCall = append(fake.getStateArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetState", []interface{}{arg1})
+ fake.getStateMutex.Unlock()
+ if fake.GetStateStub != nil {
+ return fake.GetStateStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getStateReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetStateCallCount() int {
+ fake.getStateMutex.RLock()
+ defer fake.getStateMutex.RUnlock()
+ return len(fake.getStateArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateCalls(stub func(string) ([]byte, error)) {
+ fake.getStateMutex.Lock()
+ defer fake.getStateMutex.Unlock()
+ fake.GetStateStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateArgsForCall(i int) string {
+ fake.getStateMutex.RLock()
+ defer fake.getStateMutex.RUnlock()
+ argsForCall := fake.getStateArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetStateReturns(result1 []byte, result2 error) {
+ fake.getStateMutex.Lock()
+ defer fake.getStateMutex.Unlock()
+ fake.GetStateStub = nil
+ fake.getStateReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getStateMutex.Lock()
+ defer fake.getStateMutex.Unlock()
+ fake.GetStateStub = nil
+ if fake.getStateReturnsOnCall == nil {
+ fake.getStateReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getStateReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKey(arg1 string, arg2 []string) (shim.StateQueryIteratorInterface, error) {
+ var arg2Copy []string
+ if arg2 != nil {
+ arg2Copy = make([]string, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.getStateByPartialCompositeKeyMutex.Lock()
+ ret, specificReturn := fake.getStateByPartialCompositeKeyReturnsOnCall[len(fake.getStateByPartialCompositeKeyArgsForCall)]
+ fake.getStateByPartialCompositeKeyArgsForCall = append(fake.getStateByPartialCompositeKeyArgsForCall, struct {
+ arg1 string
+ arg2 []string
+ }{arg1, arg2Copy})
+ fake.recordInvocation("GetStateByPartialCompositeKey", []interface{}{arg1, arg2Copy})
+ fake.getStateByPartialCompositeKeyMutex.Unlock()
+ if fake.GetStateByPartialCompositeKeyStub != nil {
+ return fake.GetStateByPartialCompositeKeyStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getStateByPartialCompositeKeyReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyCallCount() int {
+ fake.getStateByPartialCompositeKeyMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyMutex.RUnlock()
+ return len(fake.getStateByPartialCompositeKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyCalls(stub func(string, []string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getStateByPartialCompositeKeyMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyArgsForCall(i int) (string, []string) {
+ fake.getStateByPartialCompositeKeyMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyMutex.RUnlock()
+ argsForCall := fake.getStateByPartialCompositeKeyArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getStateByPartialCompositeKeyMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyStub = nil
+ fake.getStateByPartialCompositeKeyReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getStateByPartialCompositeKeyMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyStub = nil
+ if fake.getStateByPartialCompositeKeyReturnsOnCall == nil {
+ fake.getStateByPartialCompositeKeyReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getStateByPartialCompositeKeyReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPagination(arg1 string, arg2 []string, arg3 int32, arg4 string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) {
+ var arg2Copy []string
+ if arg2 != nil {
+ arg2Copy = make([]string, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Lock()
+ ret, specificReturn := fake.getStateByPartialCompositeKeyWithPaginationReturnsOnCall[len(fake.getStateByPartialCompositeKeyWithPaginationArgsForCall)]
+ fake.getStateByPartialCompositeKeyWithPaginationArgsForCall = append(fake.getStateByPartialCompositeKeyWithPaginationArgsForCall, struct {
+ arg1 string
+ arg2 []string
+ arg3 int32
+ arg4 string
+ }{arg1, arg2Copy, arg3, arg4})
+ fake.recordInvocation("GetStateByPartialCompositeKeyWithPagination", []interface{}{arg1, arg2Copy, arg3, arg4})
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Unlock()
+ if fake.GetStateByPartialCompositeKeyWithPaginationStub != nil {
+ return fake.GetStateByPartialCompositeKeyWithPaginationStub(arg1, arg2, arg3, arg4)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.getStateByPartialCompositeKeyWithPaginationReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationCallCount() int {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.RUnlock()
+ return len(fake.getStateByPartialCompositeKeyWithPaginationArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationCalls(stub func(string, []string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyWithPaginationStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationArgsForCall(i int) (string, []string, int32, string) {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.RUnlock()
+ argsForCall := fake.getStateByPartialCompositeKeyWithPaginationArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationReturns(result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyWithPaginationStub = nil
+ fake.getStateByPartialCompositeKeyWithPaginationReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetStateByPartialCompositeKeyWithPaginationReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.Lock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.Unlock()
+ fake.GetStateByPartialCompositeKeyWithPaginationStub = nil
+ if fake.getStateByPartialCompositeKeyWithPaginationReturnsOnCall == nil {
+ fake.getStateByPartialCompositeKeyWithPaginationReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ })
+ }
+ fake.getStateByPartialCompositeKeyWithPaginationReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetStateByRange(arg1 string, arg2 string) (shim.StateQueryIteratorInterface, error) {
+ fake.getStateByRangeMutex.Lock()
+ ret, specificReturn := fake.getStateByRangeReturnsOnCall[len(fake.getStateByRangeArgsForCall)]
+ fake.getStateByRangeArgsForCall = append(fake.getStateByRangeArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("GetStateByRange", []interface{}{arg1, arg2})
+ fake.getStateByRangeMutex.Unlock()
+ if fake.GetStateByRangeStub != nil {
+ return fake.GetStateByRangeStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getStateByRangeReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetStateByRangeCallCount() int {
+ fake.getStateByRangeMutex.RLock()
+ defer fake.getStateByRangeMutex.RUnlock()
+ return len(fake.getStateByRangeArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateByRangeCalls(stub func(string, string) (shim.StateQueryIteratorInterface, error)) {
+ fake.getStateByRangeMutex.Lock()
+ defer fake.getStateByRangeMutex.Unlock()
+ fake.GetStateByRangeStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateByRangeArgsForCall(i int) (string, string) {
+ fake.getStateByRangeMutex.RLock()
+ defer fake.getStateByRangeMutex.RUnlock()
+ argsForCall := fake.getStateByRangeArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) GetStateByRangeReturns(result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getStateByRangeMutex.Lock()
+ defer fake.getStateByRangeMutex.Unlock()
+ fake.GetStateByRangeStub = nil
+ fake.getStateByRangeReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByRangeReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 error) {
+ fake.getStateByRangeMutex.Lock()
+ defer fake.getStateByRangeMutex.Unlock()
+ fake.GetStateByRangeStub = nil
+ if fake.getStateByRangeReturnsOnCall == nil {
+ fake.getStateByRangeReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ })
+ }
+ fake.getStateByRangeReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPagination(arg1 string, arg2 string, arg3 int32, arg4 string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) {
+ fake.getStateByRangeWithPaginationMutex.Lock()
+ ret, specificReturn := fake.getStateByRangeWithPaginationReturnsOnCall[len(fake.getStateByRangeWithPaginationArgsForCall)]
+ fake.getStateByRangeWithPaginationArgsForCall = append(fake.getStateByRangeWithPaginationArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 int32
+ arg4 string
+ }{arg1, arg2, arg3, arg4})
+ fake.recordInvocation("GetStateByRangeWithPagination", []interface{}{arg1, arg2, arg3, arg4})
+ fake.getStateByRangeWithPaginationMutex.Unlock()
+ if fake.GetStateByRangeWithPaginationStub != nil {
+ return fake.GetStateByRangeWithPaginationStub(arg1, arg2, arg3, arg4)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.getStateByRangeWithPaginationReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationCallCount() int {
+ fake.getStateByRangeWithPaginationMutex.RLock()
+ defer fake.getStateByRangeWithPaginationMutex.RUnlock()
+ return len(fake.getStateByRangeWithPaginationArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationCalls(stub func(string, string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) {
+ fake.getStateByRangeWithPaginationMutex.Lock()
+ defer fake.getStateByRangeWithPaginationMutex.Unlock()
+ fake.GetStateByRangeWithPaginationStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationArgsForCall(i int) (string, string, int32, string) {
+ fake.getStateByRangeWithPaginationMutex.RLock()
+ defer fake.getStateByRangeWithPaginationMutex.RUnlock()
+ argsForCall := fake.getStateByRangeWithPaginationArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationReturns(result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getStateByRangeWithPaginationMutex.Lock()
+ defer fake.getStateByRangeWithPaginationMutex.Unlock()
+ fake.GetStateByRangeWithPaginationStub = nil
+ fake.getStateByRangeWithPaginationReturns = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetStateByRangeWithPaginationReturnsOnCall(i int, result1 shim.StateQueryIteratorInterface, result2 *peer.QueryResponseMetadata, result3 error) {
+ fake.getStateByRangeWithPaginationMutex.Lock()
+ defer fake.getStateByRangeWithPaginationMutex.Unlock()
+ fake.GetStateByRangeWithPaginationStub = nil
+ if fake.getStateByRangeWithPaginationReturnsOnCall == nil {
+ fake.getStateByRangeWithPaginationReturnsOnCall = make(map[int]struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ })
+ }
+ fake.getStateByRangeWithPaginationReturnsOnCall[i] = struct {
+ result1 shim.StateQueryIteratorInterface
+ result2 *peer.QueryResponseMetadata
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameter(arg1 string) ([]byte, error) {
+ fake.getStateValidationParameterMutex.Lock()
+ ret, specificReturn := fake.getStateValidationParameterReturnsOnCall[len(fake.getStateValidationParameterArgsForCall)]
+ fake.getStateValidationParameterArgsForCall = append(fake.getStateValidationParameterArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetStateValidationParameter", []interface{}{arg1})
+ fake.getStateValidationParameterMutex.Unlock()
+ if fake.GetStateValidationParameterStub != nil {
+ return fake.GetStateValidationParameterStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getStateValidationParameterReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterCallCount() int {
+ fake.getStateValidationParameterMutex.RLock()
+ defer fake.getStateValidationParameterMutex.RUnlock()
+ return len(fake.getStateValidationParameterArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterCalls(stub func(string) ([]byte, error)) {
+ fake.getStateValidationParameterMutex.Lock()
+ defer fake.getStateValidationParameterMutex.Unlock()
+ fake.GetStateValidationParameterStub = stub
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterArgsForCall(i int) string {
+ fake.getStateValidationParameterMutex.RLock()
+ defer fake.getStateValidationParameterMutex.RUnlock()
+ argsForCall := fake.getStateValidationParameterArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterReturns(result1 []byte, result2 error) {
+ fake.getStateValidationParameterMutex.Lock()
+ defer fake.getStateValidationParameterMutex.Unlock()
+ fake.GetStateValidationParameterStub = nil
+ fake.getStateValidationParameterReturns = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStateValidationParameterReturnsOnCall(i int, result1 []byte, result2 error) {
+ fake.getStateValidationParameterMutex.Lock()
+ defer fake.getStateValidationParameterMutex.Unlock()
+ fake.GetStateValidationParameterStub = nil
+ if fake.getStateValidationParameterReturnsOnCall == nil {
+ fake.getStateValidationParameterReturnsOnCall = make(map[int]struct {
+ result1 []byte
+ result2 error
+ })
+ }
+ fake.getStateValidationParameterReturnsOnCall[i] = struct {
+ result1 []byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetStringArgs() []string {
+ fake.getStringArgsMutex.Lock()
+ ret, specificReturn := fake.getStringArgsReturnsOnCall[len(fake.getStringArgsArgsForCall)]
+ fake.getStringArgsArgsForCall = append(fake.getStringArgsArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetStringArgs", []interface{}{})
+ fake.getStringArgsMutex.Unlock()
+ if fake.GetStringArgsStub != nil {
+ return fake.GetStringArgsStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getStringArgsReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetStringArgsCallCount() int {
+ fake.getStringArgsMutex.RLock()
+ defer fake.getStringArgsMutex.RUnlock()
+ return len(fake.getStringArgsArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetStringArgsCalls(stub func() []string) {
+ fake.getStringArgsMutex.Lock()
+ defer fake.getStringArgsMutex.Unlock()
+ fake.GetStringArgsStub = stub
+}
+
+func (fake *ChaincodeStub) GetStringArgsReturns(result1 []string) {
+ fake.getStringArgsMutex.Lock()
+ defer fake.getStringArgsMutex.Unlock()
+ fake.GetStringArgsStub = nil
+ fake.getStringArgsReturns = struct {
+ result1 []string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetStringArgsReturnsOnCall(i int, result1 []string) {
+ fake.getStringArgsMutex.Lock()
+ defer fake.getStringArgsMutex.Unlock()
+ fake.GetStringArgsStub = nil
+ if fake.getStringArgsReturnsOnCall == nil {
+ fake.getStringArgsReturnsOnCall = make(map[int]struct {
+ result1 []string
+ })
+ }
+ fake.getStringArgsReturnsOnCall[i] = struct {
+ result1 []string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetTransient() (map[string][]byte, error) {
+ fake.getTransientMutex.Lock()
+ ret, specificReturn := fake.getTransientReturnsOnCall[len(fake.getTransientArgsForCall)]
+ fake.getTransientArgsForCall = append(fake.getTransientArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetTransient", []interface{}{})
+ fake.getTransientMutex.Unlock()
+ if fake.GetTransientStub != nil {
+ return fake.GetTransientStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getTransientReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetTransientCallCount() int {
+ fake.getTransientMutex.RLock()
+ defer fake.getTransientMutex.RUnlock()
+ return len(fake.getTransientArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetTransientCalls(stub func() (map[string][]byte, error)) {
+ fake.getTransientMutex.Lock()
+ defer fake.getTransientMutex.Unlock()
+ fake.GetTransientStub = stub
+}
+
+func (fake *ChaincodeStub) GetTransientReturns(result1 map[string][]byte, result2 error) {
+ fake.getTransientMutex.Lock()
+ defer fake.getTransientMutex.Unlock()
+ fake.GetTransientStub = nil
+ fake.getTransientReturns = struct {
+ result1 map[string][]byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetTransientReturnsOnCall(i int, result1 map[string][]byte, result2 error) {
+ fake.getTransientMutex.Lock()
+ defer fake.getTransientMutex.Unlock()
+ fake.GetTransientStub = nil
+ if fake.getTransientReturnsOnCall == nil {
+ fake.getTransientReturnsOnCall = make(map[int]struct {
+ result1 map[string][]byte
+ result2 error
+ })
+ }
+ fake.getTransientReturnsOnCall[i] = struct {
+ result1 map[string][]byte
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetTxID() string {
+ fake.getTxIDMutex.Lock()
+ ret, specificReturn := fake.getTxIDReturnsOnCall[len(fake.getTxIDArgsForCall)]
+ fake.getTxIDArgsForCall = append(fake.getTxIDArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetTxID", []interface{}{})
+ fake.getTxIDMutex.Unlock()
+ if fake.GetTxIDStub != nil {
+ return fake.GetTxIDStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getTxIDReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) GetTxIDCallCount() int {
+ fake.getTxIDMutex.RLock()
+ defer fake.getTxIDMutex.RUnlock()
+ return len(fake.getTxIDArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetTxIDCalls(stub func() string) {
+ fake.getTxIDMutex.Lock()
+ defer fake.getTxIDMutex.Unlock()
+ fake.GetTxIDStub = stub
+}
+
+func (fake *ChaincodeStub) GetTxIDReturns(result1 string) {
+ fake.getTxIDMutex.Lock()
+ defer fake.getTxIDMutex.Unlock()
+ fake.GetTxIDStub = nil
+ fake.getTxIDReturns = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetTxIDReturnsOnCall(i int, result1 string) {
+ fake.getTxIDMutex.Lock()
+ defer fake.getTxIDMutex.Unlock()
+ fake.GetTxIDStub = nil
+ if fake.getTxIDReturnsOnCall == nil {
+ fake.getTxIDReturnsOnCall = make(map[int]struct {
+ result1 string
+ })
+ }
+ fake.getTxIDReturnsOnCall[i] = struct {
+ result1 string
+ }{result1}
+}
+
+func (fake *ChaincodeStub) GetTxTimestamp() (*timestamppb.Timestamp, error) {
+ fake.getTxTimestampMutex.Lock()
+ ret, specificReturn := fake.getTxTimestampReturnsOnCall[len(fake.getTxTimestampArgsForCall)]
+ fake.getTxTimestampArgsForCall = append(fake.getTxTimestampArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetTxTimestamp", []interface{}{})
+ fake.getTxTimestampMutex.Unlock()
+ if fake.GetTxTimestampStub != nil {
+ return fake.GetTxTimestampStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getTxTimestampReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ChaincodeStub) GetTxTimestampCallCount() int {
+ fake.getTxTimestampMutex.RLock()
+ defer fake.getTxTimestampMutex.RUnlock()
+ return len(fake.getTxTimestampArgsForCall)
+}
+
+func (fake *ChaincodeStub) GetTxTimestampCalls(stub func() (*timestamppb.Timestamp, error)) {
+ fake.getTxTimestampMutex.Lock()
+ defer fake.getTxTimestampMutex.Unlock()
+ fake.GetTxTimestampStub = stub
+}
+
+func (fake *ChaincodeStub) GetTxTimestampReturns(result1 *timestamppb.Timestamp, result2 error) {
+ fake.getTxTimestampMutex.Lock()
+ defer fake.getTxTimestampMutex.Unlock()
+ fake.GetTxTimestampStub = nil
+ fake.getTxTimestampReturns = struct {
+ result1 *timestamppb.Timestamp
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) GetTxTimestampReturnsOnCall(i int, result1 *timestamppb.Timestamp, result2 error) {
+ fake.getTxTimestampMutex.Lock()
+ defer fake.getTxTimestampMutex.Unlock()
+ fake.GetTxTimestampStub = nil
+ if fake.getTxTimestampReturnsOnCall == nil {
+ fake.getTxTimestampReturnsOnCall = make(map[int]struct {
+ result1 *timestamppb.Timestamp
+ result2 error
+ })
+ }
+ fake.getTxTimestampReturnsOnCall[i] = struct {
+ result1 *timestamppb.Timestamp
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ChaincodeStub) InvokeChaincode(arg1 string, arg2 [][]byte, arg3 string) peer.Response {
+ var arg2Copy [][]byte
+ if arg2 != nil {
+ arg2Copy = make([][]byte, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.invokeChaincodeMutex.Lock()
+ ret, specificReturn := fake.invokeChaincodeReturnsOnCall[len(fake.invokeChaincodeArgsForCall)]
+ fake.invokeChaincodeArgsForCall = append(fake.invokeChaincodeArgsForCall, struct {
+ arg1 string
+ arg2 [][]byte
+ arg3 string
+ }{arg1, arg2Copy, arg3})
+ fake.recordInvocation("InvokeChaincode", []interface{}{arg1, arg2Copy, arg3})
+ fake.invokeChaincodeMutex.Unlock()
+ if fake.InvokeChaincodeStub != nil {
+ return fake.InvokeChaincodeStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.invokeChaincodeReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeCallCount() int {
+ fake.invokeChaincodeMutex.RLock()
+ defer fake.invokeChaincodeMutex.RUnlock()
+ return len(fake.invokeChaincodeArgsForCall)
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeCalls(stub func(string, [][]byte, string) peer.Response) {
+ fake.invokeChaincodeMutex.Lock()
+ defer fake.invokeChaincodeMutex.Unlock()
+ fake.InvokeChaincodeStub = stub
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeArgsForCall(i int) (string, [][]byte, string) {
+ fake.invokeChaincodeMutex.RLock()
+ defer fake.invokeChaincodeMutex.RUnlock()
+ argsForCall := fake.invokeChaincodeArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeReturns(result1 peer.Response) {
+ fake.invokeChaincodeMutex.Lock()
+ defer fake.invokeChaincodeMutex.Unlock()
+ fake.InvokeChaincodeStub = nil
+ fake.invokeChaincodeReturns = struct {
+ result1 peer.Response
+ }{result1}
+}
+
+func (fake *ChaincodeStub) InvokeChaincodeReturnsOnCall(i int, result1 peer.Response) {
+ fake.invokeChaincodeMutex.Lock()
+ defer fake.invokeChaincodeMutex.Unlock()
+ fake.InvokeChaincodeStub = nil
+ if fake.invokeChaincodeReturnsOnCall == nil {
+ fake.invokeChaincodeReturnsOnCall = make(map[int]struct {
+ result1 peer.Response
+ })
+ }
+ fake.invokeChaincodeReturnsOnCall[i] = struct {
+ result1 peer.Response
+ }{result1}
+}
+
+func (fake *ChaincodeStub) PutPrivateData(arg1 string, arg2 string, arg3 []byte) error {
+ var arg3Copy []byte
+ if arg3 != nil {
+ arg3Copy = make([]byte, len(arg3))
+ copy(arg3Copy, arg3)
+ }
+ fake.putPrivateDataMutex.Lock()
+ ret, specificReturn := fake.putPrivateDataReturnsOnCall[len(fake.putPrivateDataArgsForCall)]
+ fake.putPrivateDataArgsForCall = append(fake.putPrivateDataArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 []byte
+ }{arg1, arg2, arg3Copy})
+ fake.recordInvocation("PutPrivateData", []interface{}{arg1, arg2, arg3Copy})
+ fake.putPrivateDataMutex.Unlock()
+ if fake.PutPrivateDataStub != nil {
+ return fake.PutPrivateDataStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.putPrivateDataReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) PutPrivateDataCallCount() int {
+ fake.putPrivateDataMutex.RLock()
+ defer fake.putPrivateDataMutex.RUnlock()
+ return len(fake.putPrivateDataArgsForCall)
+}
+
+func (fake *ChaincodeStub) PutPrivateDataCalls(stub func(string, string, []byte) error) {
+ fake.putPrivateDataMutex.Lock()
+ defer fake.putPrivateDataMutex.Unlock()
+ fake.PutPrivateDataStub = stub
+}
+
+func (fake *ChaincodeStub) PutPrivateDataArgsForCall(i int) (string, string, []byte) {
+ fake.putPrivateDataMutex.RLock()
+ defer fake.putPrivateDataMutex.RUnlock()
+ argsForCall := fake.putPrivateDataArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) PutPrivateDataReturns(result1 error) {
+ fake.putPrivateDataMutex.Lock()
+ defer fake.putPrivateDataMutex.Unlock()
+ fake.PutPrivateDataStub = nil
+ fake.putPrivateDataReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) PutPrivateDataReturnsOnCall(i int, result1 error) {
+ fake.putPrivateDataMutex.Lock()
+ defer fake.putPrivateDataMutex.Unlock()
+ fake.PutPrivateDataStub = nil
+ if fake.putPrivateDataReturnsOnCall == nil {
+ fake.putPrivateDataReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.putPrivateDataReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) PutState(arg1 string, arg2 []byte) error {
+ var arg2Copy []byte
+ if arg2 != nil {
+ arg2Copy = make([]byte, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.putStateMutex.Lock()
+ ret, specificReturn := fake.putStateReturnsOnCall[len(fake.putStateArgsForCall)]
+ fake.putStateArgsForCall = append(fake.putStateArgsForCall, struct {
+ arg1 string
+ arg2 []byte
+ }{arg1, arg2Copy})
+ fake.recordInvocation("PutState", []interface{}{arg1, arg2Copy})
+ fake.putStateMutex.Unlock()
+ if fake.PutStateStub != nil {
+ return fake.PutStateStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.putStateReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) PutStateCallCount() int {
+ fake.putStateMutex.RLock()
+ defer fake.putStateMutex.RUnlock()
+ return len(fake.putStateArgsForCall)
+}
+
+func (fake *ChaincodeStub) PutStateCalls(stub func(string, []byte) error) {
+ fake.putStateMutex.Lock()
+ defer fake.putStateMutex.Unlock()
+ fake.PutStateStub = stub
+}
+
+func (fake *ChaincodeStub) PutStateArgsForCall(i int) (string, []byte) {
+ fake.putStateMutex.RLock()
+ defer fake.putStateMutex.RUnlock()
+ argsForCall := fake.putStateArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) PutStateReturns(result1 error) {
+ fake.putStateMutex.Lock()
+ defer fake.putStateMutex.Unlock()
+ fake.PutStateStub = nil
+ fake.putStateReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) PutStateReturnsOnCall(i int, result1 error) {
+ fake.putStateMutex.Lock()
+ defer fake.putStateMutex.Unlock()
+ fake.PutStateStub = nil
+ if fake.putStateReturnsOnCall == nil {
+ fake.putStateReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.putStateReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetEvent(arg1 string, arg2 []byte) error {
+ var arg2Copy []byte
+ if arg2 != nil {
+ arg2Copy = make([]byte, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.setEventMutex.Lock()
+ ret, specificReturn := fake.setEventReturnsOnCall[len(fake.setEventArgsForCall)]
+ fake.setEventArgsForCall = append(fake.setEventArgsForCall, struct {
+ arg1 string
+ arg2 []byte
+ }{arg1, arg2Copy})
+ fake.recordInvocation("SetEvent", []interface{}{arg1, arg2Copy})
+ fake.setEventMutex.Unlock()
+ if fake.SetEventStub != nil {
+ return fake.SetEventStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.setEventReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) SetEventCallCount() int {
+ fake.setEventMutex.RLock()
+ defer fake.setEventMutex.RUnlock()
+ return len(fake.setEventArgsForCall)
+}
+
+func (fake *ChaincodeStub) SetEventCalls(stub func(string, []byte) error) {
+ fake.setEventMutex.Lock()
+ defer fake.setEventMutex.Unlock()
+ fake.SetEventStub = stub
+}
+
+func (fake *ChaincodeStub) SetEventArgsForCall(i int) (string, []byte) {
+ fake.setEventMutex.RLock()
+ defer fake.setEventMutex.RUnlock()
+ argsForCall := fake.setEventArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) SetEventReturns(result1 error) {
+ fake.setEventMutex.Lock()
+ defer fake.setEventMutex.Unlock()
+ fake.SetEventStub = nil
+ fake.setEventReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetEventReturnsOnCall(i int, result1 error) {
+ fake.setEventMutex.Lock()
+ defer fake.setEventMutex.Unlock()
+ fake.SetEventStub = nil
+ if fake.setEventReturnsOnCall == nil {
+ fake.setEventReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.setEventReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameter(arg1 string, arg2 string, arg3 []byte) error {
+ var arg3Copy []byte
+ if arg3 != nil {
+ arg3Copy = make([]byte, len(arg3))
+ copy(arg3Copy, arg3)
+ }
+ fake.setPrivateDataValidationParameterMutex.Lock()
+ ret, specificReturn := fake.setPrivateDataValidationParameterReturnsOnCall[len(fake.setPrivateDataValidationParameterArgsForCall)]
+ fake.setPrivateDataValidationParameterArgsForCall = append(fake.setPrivateDataValidationParameterArgsForCall, struct {
+ arg1 string
+ arg2 string
+ arg3 []byte
+ }{arg1, arg2, arg3Copy})
+ fake.recordInvocation("SetPrivateDataValidationParameter", []interface{}{arg1, arg2, arg3Copy})
+ fake.setPrivateDataValidationParameterMutex.Unlock()
+ if fake.SetPrivateDataValidationParameterStub != nil {
+ return fake.SetPrivateDataValidationParameterStub(arg1, arg2, arg3)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.setPrivateDataValidationParameterReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterCallCount() int {
+ fake.setPrivateDataValidationParameterMutex.RLock()
+ defer fake.setPrivateDataValidationParameterMutex.RUnlock()
+ return len(fake.setPrivateDataValidationParameterArgsForCall)
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterCalls(stub func(string, string, []byte) error) {
+ fake.setPrivateDataValidationParameterMutex.Lock()
+ defer fake.setPrivateDataValidationParameterMutex.Unlock()
+ fake.SetPrivateDataValidationParameterStub = stub
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterArgsForCall(i int) (string, string, []byte) {
+ fake.setPrivateDataValidationParameterMutex.RLock()
+ defer fake.setPrivateDataValidationParameterMutex.RUnlock()
+ argsForCall := fake.setPrivateDataValidationParameterArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterReturns(result1 error) {
+ fake.setPrivateDataValidationParameterMutex.Lock()
+ defer fake.setPrivateDataValidationParameterMutex.Unlock()
+ fake.SetPrivateDataValidationParameterStub = nil
+ fake.setPrivateDataValidationParameterReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetPrivateDataValidationParameterReturnsOnCall(i int, result1 error) {
+ fake.setPrivateDataValidationParameterMutex.Lock()
+ defer fake.setPrivateDataValidationParameterMutex.Unlock()
+ fake.SetPrivateDataValidationParameterStub = nil
+ if fake.setPrivateDataValidationParameterReturnsOnCall == nil {
+ fake.setPrivateDataValidationParameterReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.setPrivateDataValidationParameterReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameter(arg1 string, arg2 []byte) error {
+ var arg2Copy []byte
+ if arg2 != nil {
+ arg2Copy = make([]byte, len(arg2))
+ copy(arg2Copy, arg2)
+ }
+ fake.setStateValidationParameterMutex.Lock()
+ ret, specificReturn := fake.setStateValidationParameterReturnsOnCall[len(fake.setStateValidationParameterArgsForCall)]
+ fake.setStateValidationParameterArgsForCall = append(fake.setStateValidationParameterArgsForCall, struct {
+ arg1 string
+ arg2 []byte
+ }{arg1, arg2Copy})
+ fake.recordInvocation("SetStateValidationParameter", []interface{}{arg1, arg2Copy})
+ fake.setStateValidationParameterMutex.Unlock()
+ if fake.SetStateValidationParameterStub != nil {
+ return fake.SetStateValidationParameterStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.setStateValidationParameterReturns
+ return fakeReturns.result1
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterCallCount() int {
+ fake.setStateValidationParameterMutex.RLock()
+ defer fake.setStateValidationParameterMutex.RUnlock()
+ return len(fake.setStateValidationParameterArgsForCall)
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterCalls(stub func(string, []byte) error) {
+ fake.setStateValidationParameterMutex.Lock()
+ defer fake.setStateValidationParameterMutex.Unlock()
+ fake.SetStateValidationParameterStub = stub
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterArgsForCall(i int) (string, []byte) {
+ fake.setStateValidationParameterMutex.RLock()
+ defer fake.setStateValidationParameterMutex.RUnlock()
+ argsForCall := fake.setStateValidationParameterArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterReturns(result1 error) {
+ fake.setStateValidationParameterMutex.Lock()
+ defer fake.setStateValidationParameterMutex.Unlock()
+ fake.SetStateValidationParameterStub = nil
+ fake.setStateValidationParameterReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SetStateValidationParameterReturnsOnCall(i int, result1 error) {
+ fake.setStateValidationParameterMutex.Lock()
+ defer fake.setStateValidationParameterMutex.Unlock()
+ fake.SetStateValidationParameterStub = nil
+ if fake.setStateValidationParameterReturnsOnCall == nil {
+ fake.setStateValidationParameterReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.setStateValidationParameterReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ChaincodeStub) SplitCompositeKey(arg1 string) (string, []string, error) {
+ fake.splitCompositeKeyMutex.Lock()
+ ret, specificReturn := fake.splitCompositeKeyReturnsOnCall[len(fake.splitCompositeKeyArgsForCall)]
+ fake.splitCompositeKeyArgsForCall = append(fake.splitCompositeKeyArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("SplitCompositeKey", []interface{}{arg1})
+ fake.splitCompositeKeyMutex.Unlock()
+ if fake.SplitCompositeKeyStub != nil {
+ return fake.SplitCompositeKeyStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.splitCompositeKeyReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyCallCount() int {
+ fake.splitCompositeKeyMutex.RLock()
+ defer fake.splitCompositeKeyMutex.RUnlock()
+ return len(fake.splitCompositeKeyArgsForCall)
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyCalls(stub func(string) (string, []string, error)) {
+ fake.splitCompositeKeyMutex.Lock()
+ defer fake.splitCompositeKeyMutex.Unlock()
+ fake.SplitCompositeKeyStub = stub
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyArgsForCall(i int) string {
+ fake.splitCompositeKeyMutex.RLock()
+ defer fake.splitCompositeKeyMutex.RUnlock()
+ argsForCall := fake.splitCompositeKeyArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyReturns(result1 string, result2 []string, result3 error) {
+ fake.splitCompositeKeyMutex.Lock()
+ defer fake.splitCompositeKeyMutex.Unlock()
+ fake.SplitCompositeKeyStub = nil
+ fake.splitCompositeKeyReturns = struct {
+ result1 string
+ result2 []string
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) SplitCompositeKeyReturnsOnCall(i int, result1 string, result2 []string, result3 error) {
+ fake.splitCompositeKeyMutex.Lock()
+ defer fake.splitCompositeKeyMutex.Unlock()
+ fake.SplitCompositeKeyStub = nil
+ if fake.splitCompositeKeyReturnsOnCall == nil {
+ fake.splitCompositeKeyReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 []string
+ result3 error
+ })
+ }
+ fake.splitCompositeKeyReturnsOnCall[i] = struct {
+ result1 string
+ result2 []string
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ChaincodeStub) Invocations() map[string][][]interface{} {
+ fake.invocationsMutex.RLock()
+ defer fake.invocationsMutex.RUnlock()
+ fake.createCompositeKeyMutex.RLock()
+ defer fake.createCompositeKeyMutex.RUnlock()
+ fake.delPrivateDataMutex.RLock()
+ defer fake.delPrivateDataMutex.RUnlock()
+ fake.delStateMutex.RLock()
+ defer fake.delStateMutex.RUnlock()
+ fake.getArgsMutex.RLock()
+ defer fake.getArgsMutex.RUnlock()
+ fake.getArgsSliceMutex.RLock()
+ defer fake.getArgsSliceMutex.RUnlock()
+ fake.getBindingMutex.RLock()
+ defer fake.getBindingMutex.RUnlock()
+ fake.getChannelIDMutex.RLock()
+ defer fake.getChannelIDMutex.RUnlock()
+ fake.getCreatorMutex.RLock()
+ defer fake.getCreatorMutex.RUnlock()
+ fake.getDecorationsMutex.RLock()
+ defer fake.getDecorationsMutex.RUnlock()
+ fake.getFunctionAndParametersMutex.RLock()
+ defer fake.getFunctionAndParametersMutex.RUnlock()
+ fake.getHistoryForKeyMutex.RLock()
+ defer fake.getHistoryForKeyMutex.RUnlock()
+ fake.getPrivateDataMutex.RLock()
+ defer fake.getPrivateDataMutex.RUnlock()
+ fake.getPrivateDataByPartialCompositeKeyMutex.RLock()
+ defer fake.getPrivateDataByPartialCompositeKeyMutex.RUnlock()
+ fake.getPrivateDataByRangeMutex.RLock()
+ defer fake.getPrivateDataByRangeMutex.RUnlock()
+ fake.getPrivateDataHashMutex.RLock()
+ defer fake.getPrivateDataHashMutex.RUnlock()
+ fake.getPrivateDataQueryResultMutex.RLock()
+ defer fake.getPrivateDataQueryResultMutex.RUnlock()
+ fake.getPrivateDataValidationParameterMutex.RLock()
+ defer fake.getPrivateDataValidationParameterMutex.RUnlock()
+ fake.getQueryResultMutex.RLock()
+ defer fake.getQueryResultMutex.RUnlock()
+ fake.getQueryResultWithPaginationMutex.RLock()
+ defer fake.getQueryResultWithPaginationMutex.RUnlock()
+ fake.getSignedProposalMutex.RLock()
+ defer fake.getSignedProposalMutex.RUnlock()
+ fake.getStateMutex.RLock()
+ defer fake.getStateMutex.RUnlock()
+ fake.getStateByPartialCompositeKeyMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyMutex.RUnlock()
+ fake.getStateByPartialCompositeKeyWithPaginationMutex.RLock()
+ defer fake.getStateByPartialCompositeKeyWithPaginationMutex.RUnlock()
+ fake.getStateByRangeMutex.RLock()
+ defer fake.getStateByRangeMutex.RUnlock()
+ fake.getStateByRangeWithPaginationMutex.RLock()
+ defer fake.getStateByRangeWithPaginationMutex.RUnlock()
+ fake.getStateValidationParameterMutex.RLock()
+ defer fake.getStateValidationParameterMutex.RUnlock()
+ fake.getStringArgsMutex.RLock()
+ defer fake.getStringArgsMutex.RUnlock()
+ fake.getTransientMutex.RLock()
+ defer fake.getTransientMutex.RUnlock()
+ fake.getTxIDMutex.RLock()
+ defer fake.getTxIDMutex.RUnlock()
+ fake.getTxTimestampMutex.RLock()
+ defer fake.getTxTimestampMutex.RUnlock()
+ fake.invokeChaincodeMutex.RLock()
+ defer fake.invokeChaincodeMutex.RUnlock()
+ fake.putPrivateDataMutex.RLock()
+ defer fake.putPrivateDataMutex.RUnlock()
+ fake.putStateMutex.RLock()
+ defer fake.putStateMutex.RUnlock()
+ fake.setEventMutex.RLock()
+ defer fake.setEventMutex.RUnlock()
+ fake.setPrivateDataValidationParameterMutex.RLock()
+ defer fake.setPrivateDataValidationParameterMutex.RUnlock()
+ fake.setStateValidationParameterMutex.RLock()
+ defer fake.setStateValidationParameterMutex.RUnlock()
+ fake.splitCompositeKeyMutex.RLock()
+ defer fake.splitCompositeKeyMutex.RUnlock()
+ copiedInvocations := map[string][][]interface{}{}
+ for key, value := range fake.invocations {
+ copiedInvocations[key] = value
+ }
+ return copiedInvocations
+}
+
+func (fake *ChaincodeStub) recordInvocation(key string, args []interface{}) {
+ fake.invocationsMutex.Lock()
+ defer fake.invocationsMutex.Unlock()
+ if fake.invocations == nil {
+ fake.invocations = map[string][][]interface{}{}
+ }
+ if fake.invocations[key] == nil {
+ fake.invocations[key] = [][]interface{}{}
+ }
+ fake.invocations[key] = append(fake.invocations[key], args)
+}
diff --git a/asset-transfer-private-data/chaincode-go/chaincode/mocks/clientIdentity.go b/asset-transfer-private-data/chaincode-go/chaincode/mocks/clientIdentity.go
new file mode 100644
index 0000000..655dfd4
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/chaincode/mocks/clientIdentity.go
@@ -0,0 +1,399 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package mocks
+
+import (
+ "crypto/x509"
+ "sync"
+)
+
+type ClientIdentity struct {
+ AssertAttributeValueStub func(string, string) error
+ assertAttributeValueMutex sync.RWMutex
+ assertAttributeValueArgsForCall []struct {
+ arg1 string
+ arg2 string
+ }
+ assertAttributeValueReturns struct {
+ result1 error
+ }
+ assertAttributeValueReturnsOnCall map[int]struct {
+ result1 error
+ }
+ GetAttributeValueStub func(string) (string, bool, error)
+ getAttributeValueMutex sync.RWMutex
+ getAttributeValueArgsForCall []struct {
+ arg1 string
+ }
+ getAttributeValueReturns struct {
+ result1 string
+ result2 bool
+ result3 error
+ }
+ getAttributeValueReturnsOnCall map[int]struct {
+ result1 string
+ result2 bool
+ result3 error
+ }
+ GetIDStub func() (string, error)
+ getIDMutex sync.RWMutex
+ getIDArgsForCall []struct {
+ }
+ getIDReturns struct {
+ result1 string
+ result2 error
+ }
+ getIDReturnsOnCall map[int]struct {
+ result1 string
+ result2 error
+ }
+ GetMSPIDStub func() (string, error)
+ getMSPIDMutex sync.RWMutex
+ getMSPIDArgsForCall []struct {
+ }
+ getMSPIDReturns struct {
+ result1 string
+ result2 error
+ }
+ getMSPIDReturnsOnCall map[int]struct {
+ result1 string
+ result2 error
+ }
+ GetX509CertificateStub func() (*x509.Certificate, error)
+ getX509CertificateMutex sync.RWMutex
+ getX509CertificateArgsForCall []struct {
+ }
+ getX509CertificateReturns struct {
+ result1 *x509.Certificate
+ result2 error
+ }
+ getX509CertificateReturnsOnCall map[int]struct {
+ result1 *x509.Certificate
+ result2 error
+ }
+ invocations map[string][][]interface{}
+ invocationsMutex sync.RWMutex
+}
+
+func (fake *ClientIdentity) AssertAttributeValue(arg1 string, arg2 string) error {
+ fake.assertAttributeValueMutex.Lock()
+ ret, specificReturn := fake.assertAttributeValueReturnsOnCall[len(fake.assertAttributeValueArgsForCall)]
+ fake.assertAttributeValueArgsForCall = append(fake.assertAttributeValueArgsForCall, struct {
+ arg1 string
+ arg2 string
+ }{arg1, arg2})
+ fake.recordInvocation("AssertAttributeValue", []interface{}{arg1, arg2})
+ fake.assertAttributeValueMutex.Unlock()
+ if fake.AssertAttributeValueStub != nil {
+ return fake.AssertAttributeValueStub(arg1, arg2)
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.assertAttributeValueReturns
+ return fakeReturns.result1
+}
+
+func (fake *ClientIdentity) AssertAttributeValueCallCount() int {
+ fake.assertAttributeValueMutex.RLock()
+ defer fake.assertAttributeValueMutex.RUnlock()
+ return len(fake.assertAttributeValueArgsForCall)
+}
+
+func (fake *ClientIdentity) AssertAttributeValueCalls(stub func(string, string) error) {
+ fake.assertAttributeValueMutex.Lock()
+ defer fake.assertAttributeValueMutex.Unlock()
+ fake.AssertAttributeValueStub = stub
+}
+
+func (fake *ClientIdentity) AssertAttributeValueArgsForCall(i int) (string, string) {
+ fake.assertAttributeValueMutex.RLock()
+ defer fake.assertAttributeValueMutex.RUnlock()
+ argsForCall := fake.assertAttributeValueArgsForCall[i]
+ return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *ClientIdentity) AssertAttributeValueReturns(result1 error) {
+ fake.assertAttributeValueMutex.Lock()
+ defer fake.assertAttributeValueMutex.Unlock()
+ fake.AssertAttributeValueStub = nil
+ fake.assertAttributeValueReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ClientIdentity) AssertAttributeValueReturnsOnCall(i int, result1 error) {
+ fake.assertAttributeValueMutex.Lock()
+ defer fake.assertAttributeValueMutex.Unlock()
+ fake.AssertAttributeValueStub = nil
+ if fake.assertAttributeValueReturnsOnCall == nil {
+ fake.assertAttributeValueReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.assertAttributeValueReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *ClientIdentity) GetAttributeValue(arg1 string) (string, bool, error) {
+ fake.getAttributeValueMutex.Lock()
+ ret, specificReturn := fake.getAttributeValueReturnsOnCall[len(fake.getAttributeValueArgsForCall)]
+ fake.getAttributeValueArgsForCall = append(fake.getAttributeValueArgsForCall, struct {
+ arg1 string
+ }{arg1})
+ fake.recordInvocation("GetAttributeValue", []interface{}{arg1})
+ fake.getAttributeValueMutex.Unlock()
+ if fake.GetAttributeValueStub != nil {
+ return fake.GetAttributeValueStub(arg1)
+ }
+ if specificReturn {
+ return ret.result1, ret.result2, ret.result3
+ }
+ fakeReturns := fake.getAttributeValueReturns
+ return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
+}
+
+func (fake *ClientIdentity) GetAttributeValueCallCount() int {
+ fake.getAttributeValueMutex.RLock()
+ defer fake.getAttributeValueMutex.RUnlock()
+ return len(fake.getAttributeValueArgsForCall)
+}
+
+func (fake *ClientIdentity) GetAttributeValueCalls(stub func(string) (string, bool, error)) {
+ fake.getAttributeValueMutex.Lock()
+ defer fake.getAttributeValueMutex.Unlock()
+ fake.GetAttributeValueStub = stub
+}
+
+func (fake *ClientIdentity) GetAttributeValueArgsForCall(i int) string {
+ fake.getAttributeValueMutex.RLock()
+ defer fake.getAttributeValueMutex.RUnlock()
+ argsForCall := fake.getAttributeValueArgsForCall[i]
+ return argsForCall.arg1
+}
+
+func (fake *ClientIdentity) GetAttributeValueReturns(result1 string, result2 bool, result3 error) {
+ fake.getAttributeValueMutex.Lock()
+ defer fake.getAttributeValueMutex.Unlock()
+ fake.GetAttributeValueStub = nil
+ fake.getAttributeValueReturns = struct {
+ result1 string
+ result2 bool
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ClientIdentity) GetAttributeValueReturnsOnCall(i int, result1 string, result2 bool, result3 error) {
+ fake.getAttributeValueMutex.Lock()
+ defer fake.getAttributeValueMutex.Unlock()
+ fake.GetAttributeValueStub = nil
+ if fake.getAttributeValueReturnsOnCall == nil {
+ fake.getAttributeValueReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 bool
+ result3 error
+ })
+ }
+ fake.getAttributeValueReturnsOnCall[i] = struct {
+ result1 string
+ result2 bool
+ result3 error
+ }{result1, result2, result3}
+}
+
+func (fake *ClientIdentity) GetID() (string, error) {
+ fake.getIDMutex.Lock()
+ ret, specificReturn := fake.getIDReturnsOnCall[len(fake.getIDArgsForCall)]
+ fake.getIDArgsForCall = append(fake.getIDArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetID", []interface{}{})
+ fake.getIDMutex.Unlock()
+ if fake.GetIDStub != nil {
+ return fake.GetIDStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getIDReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ClientIdentity) GetIDCallCount() int {
+ fake.getIDMutex.RLock()
+ defer fake.getIDMutex.RUnlock()
+ return len(fake.getIDArgsForCall)
+}
+
+func (fake *ClientIdentity) GetIDCalls(stub func() (string, error)) {
+ fake.getIDMutex.Lock()
+ defer fake.getIDMutex.Unlock()
+ fake.GetIDStub = stub
+}
+
+func (fake *ClientIdentity) GetIDReturns(result1 string, result2 error) {
+ fake.getIDMutex.Lock()
+ defer fake.getIDMutex.Unlock()
+ fake.GetIDStub = nil
+ fake.getIDReturns = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ClientIdentity) GetIDReturnsOnCall(i int, result1 string, result2 error) {
+ fake.getIDMutex.Lock()
+ defer fake.getIDMutex.Unlock()
+ fake.GetIDStub = nil
+ if fake.getIDReturnsOnCall == nil {
+ fake.getIDReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 error
+ })
+ }
+ fake.getIDReturnsOnCall[i] = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ClientIdentity) GetMSPID() (string, error) {
+ fake.getMSPIDMutex.Lock()
+ ret, specificReturn := fake.getMSPIDReturnsOnCall[len(fake.getMSPIDArgsForCall)]
+ fake.getMSPIDArgsForCall = append(fake.getMSPIDArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetMSPID", []interface{}{})
+ fake.getMSPIDMutex.Unlock()
+ if fake.GetMSPIDStub != nil {
+ return fake.GetMSPIDStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getMSPIDReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ClientIdentity) GetMSPIDCallCount() int {
+ fake.getMSPIDMutex.RLock()
+ defer fake.getMSPIDMutex.RUnlock()
+ return len(fake.getMSPIDArgsForCall)
+}
+
+func (fake *ClientIdentity) GetMSPIDCalls(stub func() (string, error)) {
+ fake.getMSPIDMutex.Lock()
+ defer fake.getMSPIDMutex.Unlock()
+ fake.GetMSPIDStub = stub
+}
+
+func (fake *ClientIdentity) GetMSPIDReturns(result1 string, result2 error) {
+ fake.getMSPIDMutex.Lock()
+ defer fake.getMSPIDMutex.Unlock()
+ fake.GetMSPIDStub = nil
+ fake.getMSPIDReturns = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ClientIdentity) GetMSPIDReturnsOnCall(i int, result1 string, result2 error) {
+ fake.getMSPIDMutex.Lock()
+ defer fake.getMSPIDMutex.Unlock()
+ fake.GetMSPIDStub = nil
+ if fake.getMSPIDReturnsOnCall == nil {
+ fake.getMSPIDReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 error
+ })
+ }
+ fake.getMSPIDReturnsOnCall[i] = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ClientIdentity) GetX509Certificate() (*x509.Certificate, error) {
+ fake.getX509CertificateMutex.Lock()
+ ret, specificReturn := fake.getX509CertificateReturnsOnCall[len(fake.getX509CertificateArgsForCall)]
+ fake.getX509CertificateArgsForCall = append(fake.getX509CertificateArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetX509Certificate", []interface{}{})
+ fake.getX509CertificateMutex.Unlock()
+ if fake.GetX509CertificateStub != nil {
+ return fake.GetX509CertificateStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.getX509CertificateReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *ClientIdentity) GetX509CertificateCallCount() int {
+ fake.getX509CertificateMutex.RLock()
+ defer fake.getX509CertificateMutex.RUnlock()
+ return len(fake.getX509CertificateArgsForCall)
+}
+
+func (fake *ClientIdentity) GetX509CertificateCalls(stub func() (*x509.Certificate, error)) {
+ fake.getX509CertificateMutex.Lock()
+ defer fake.getX509CertificateMutex.Unlock()
+ fake.GetX509CertificateStub = stub
+}
+
+func (fake *ClientIdentity) GetX509CertificateReturns(result1 *x509.Certificate, result2 error) {
+ fake.getX509CertificateMutex.Lock()
+ defer fake.getX509CertificateMutex.Unlock()
+ fake.GetX509CertificateStub = nil
+ fake.getX509CertificateReturns = struct {
+ result1 *x509.Certificate
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ClientIdentity) GetX509CertificateReturnsOnCall(i int, result1 *x509.Certificate, result2 error) {
+ fake.getX509CertificateMutex.Lock()
+ defer fake.getX509CertificateMutex.Unlock()
+ fake.GetX509CertificateStub = nil
+ if fake.getX509CertificateReturnsOnCall == nil {
+ fake.getX509CertificateReturnsOnCall = make(map[int]struct {
+ result1 *x509.Certificate
+ result2 error
+ })
+ }
+ fake.getX509CertificateReturnsOnCall[i] = struct {
+ result1 *x509.Certificate
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *ClientIdentity) Invocations() map[string][][]interface{} {
+ fake.invocationsMutex.RLock()
+ defer fake.invocationsMutex.RUnlock()
+ fake.assertAttributeValueMutex.RLock()
+ defer fake.assertAttributeValueMutex.RUnlock()
+ fake.getAttributeValueMutex.RLock()
+ defer fake.getAttributeValueMutex.RUnlock()
+ fake.getIDMutex.RLock()
+ defer fake.getIDMutex.RUnlock()
+ fake.getMSPIDMutex.RLock()
+ defer fake.getMSPIDMutex.RUnlock()
+ fake.getX509CertificateMutex.RLock()
+ defer fake.getX509CertificateMutex.RUnlock()
+ copiedInvocations := map[string][][]interface{}{}
+ for key, value := range fake.invocations {
+ copiedInvocations[key] = value
+ }
+ return copiedInvocations
+}
+
+func (fake *ClientIdentity) recordInvocation(key string, args []interface{}) {
+ fake.invocationsMutex.Lock()
+ defer fake.invocationsMutex.Unlock()
+ if fake.invocations == nil {
+ fake.invocations = map[string][][]interface{}{}
+ }
+ if fake.invocations[key] == nil {
+ fake.invocations[key] = [][]interface{}{}
+ }
+ fake.invocations[key] = append(fake.invocations[key], args)
+}
diff --git a/asset-transfer-private-data/chaincode-go/chaincode/mocks/statequeryiterator.go b/asset-transfer-private-data/chaincode-go/chaincode/mocks/statequeryiterator.go
new file mode 100644
index 0000000..27e3034
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/chaincode/mocks/statequeryiterator.go
@@ -0,0 +1,232 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package mocks
+
+import (
+ "sync"
+
+ "github.com/hyperledger/fabric-protos-go/ledger/queryresult"
+)
+
+type StateQueryIterator struct {
+ CloseStub func() error
+ closeMutex sync.RWMutex
+ closeArgsForCall []struct {
+ }
+ closeReturns struct {
+ result1 error
+ }
+ closeReturnsOnCall map[int]struct {
+ result1 error
+ }
+ HasNextStub func() bool
+ hasNextMutex sync.RWMutex
+ hasNextArgsForCall []struct {
+ }
+ hasNextReturns struct {
+ result1 bool
+ }
+ hasNextReturnsOnCall map[int]struct {
+ result1 bool
+ }
+ NextStub func() (*queryresult.KV, error)
+ nextMutex sync.RWMutex
+ nextArgsForCall []struct {
+ }
+ nextReturns struct {
+ result1 *queryresult.KV
+ result2 error
+ }
+ nextReturnsOnCall map[int]struct {
+ result1 *queryresult.KV
+ result2 error
+ }
+ invocations map[string][][]interface{}
+ invocationsMutex sync.RWMutex
+}
+
+func (fake *StateQueryIterator) Close() error {
+ fake.closeMutex.Lock()
+ ret, specificReturn := fake.closeReturnsOnCall[len(fake.closeArgsForCall)]
+ fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
+ }{})
+ fake.recordInvocation("Close", []interface{}{})
+ fake.closeMutex.Unlock()
+ if fake.CloseStub != nil {
+ return fake.CloseStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.closeReturns
+ return fakeReturns.result1
+}
+
+func (fake *StateQueryIterator) CloseCallCount() int {
+ fake.closeMutex.RLock()
+ defer fake.closeMutex.RUnlock()
+ return len(fake.closeArgsForCall)
+}
+
+func (fake *StateQueryIterator) CloseCalls(stub func() error) {
+ fake.closeMutex.Lock()
+ defer fake.closeMutex.Unlock()
+ fake.CloseStub = stub
+}
+
+func (fake *StateQueryIterator) CloseReturns(result1 error) {
+ fake.closeMutex.Lock()
+ defer fake.closeMutex.Unlock()
+ fake.CloseStub = nil
+ fake.closeReturns = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *StateQueryIterator) CloseReturnsOnCall(i int, result1 error) {
+ fake.closeMutex.Lock()
+ defer fake.closeMutex.Unlock()
+ fake.CloseStub = nil
+ if fake.closeReturnsOnCall == nil {
+ fake.closeReturnsOnCall = make(map[int]struct {
+ result1 error
+ })
+ }
+ fake.closeReturnsOnCall[i] = struct {
+ result1 error
+ }{result1}
+}
+
+func (fake *StateQueryIterator) HasNext() bool {
+ fake.hasNextMutex.Lock()
+ ret, specificReturn := fake.hasNextReturnsOnCall[len(fake.hasNextArgsForCall)]
+ fake.hasNextArgsForCall = append(fake.hasNextArgsForCall, struct {
+ }{})
+ fake.recordInvocation("HasNext", []interface{}{})
+ fake.hasNextMutex.Unlock()
+ if fake.HasNextStub != nil {
+ return fake.HasNextStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.hasNextReturns
+ return fakeReturns.result1
+}
+
+func (fake *StateQueryIterator) HasNextCallCount() int {
+ fake.hasNextMutex.RLock()
+ defer fake.hasNextMutex.RUnlock()
+ return len(fake.hasNextArgsForCall)
+}
+
+func (fake *StateQueryIterator) HasNextCalls(stub func() bool) {
+ fake.hasNextMutex.Lock()
+ defer fake.hasNextMutex.Unlock()
+ fake.HasNextStub = stub
+}
+
+func (fake *StateQueryIterator) HasNextReturns(result1 bool) {
+ fake.hasNextMutex.Lock()
+ defer fake.hasNextMutex.Unlock()
+ fake.HasNextStub = nil
+ fake.hasNextReturns = struct {
+ result1 bool
+ }{result1}
+}
+
+func (fake *StateQueryIterator) HasNextReturnsOnCall(i int, result1 bool) {
+ fake.hasNextMutex.Lock()
+ defer fake.hasNextMutex.Unlock()
+ fake.HasNextStub = nil
+ if fake.hasNextReturnsOnCall == nil {
+ fake.hasNextReturnsOnCall = make(map[int]struct {
+ result1 bool
+ })
+ }
+ fake.hasNextReturnsOnCall[i] = struct {
+ result1 bool
+ }{result1}
+}
+
+func (fake *StateQueryIterator) Next() (*queryresult.KV, error) {
+ fake.nextMutex.Lock()
+ ret, specificReturn := fake.nextReturnsOnCall[len(fake.nextArgsForCall)]
+ fake.nextArgsForCall = append(fake.nextArgsForCall, struct {
+ }{})
+ fake.recordInvocation("Next", []interface{}{})
+ fake.nextMutex.Unlock()
+ if fake.NextStub != nil {
+ return fake.NextStub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ fakeReturns := fake.nextReturns
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *StateQueryIterator) NextCallCount() int {
+ fake.nextMutex.RLock()
+ defer fake.nextMutex.RUnlock()
+ return len(fake.nextArgsForCall)
+}
+
+func (fake *StateQueryIterator) NextCalls(stub func() (*queryresult.KV, error)) {
+ fake.nextMutex.Lock()
+ defer fake.nextMutex.Unlock()
+ fake.NextStub = stub
+}
+
+func (fake *StateQueryIterator) NextReturns(result1 *queryresult.KV, result2 error) {
+ fake.nextMutex.Lock()
+ defer fake.nextMutex.Unlock()
+ fake.NextStub = nil
+ fake.nextReturns = struct {
+ result1 *queryresult.KV
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *StateQueryIterator) NextReturnsOnCall(i int, result1 *queryresult.KV, result2 error) {
+ fake.nextMutex.Lock()
+ defer fake.nextMutex.Unlock()
+ fake.NextStub = nil
+ if fake.nextReturnsOnCall == nil {
+ fake.nextReturnsOnCall = make(map[int]struct {
+ result1 *queryresult.KV
+ result2 error
+ })
+ }
+ fake.nextReturnsOnCall[i] = struct {
+ result1 *queryresult.KV
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *StateQueryIterator) Invocations() map[string][][]interface{} {
+ fake.invocationsMutex.RLock()
+ defer fake.invocationsMutex.RUnlock()
+ fake.closeMutex.RLock()
+ defer fake.closeMutex.RUnlock()
+ fake.hasNextMutex.RLock()
+ defer fake.hasNextMutex.RUnlock()
+ fake.nextMutex.RLock()
+ defer fake.nextMutex.RUnlock()
+ copiedInvocations := map[string][][]interface{}{}
+ for key, value := range fake.invocations {
+ copiedInvocations[key] = value
+ }
+ return copiedInvocations
+}
+
+func (fake *StateQueryIterator) recordInvocation(key string, args []interface{}) {
+ fake.invocationsMutex.Lock()
+ defer fake.invocationsMutex.Unlock()
+ if fake.invocations == nil {
+ fake.invocations = map[string][][]interface{}{}
+ }
+ if fake.invocations[key] == nil {
+ fake.invocations[key] = [][]interface{}{}
+ }
+ fake.invocations[key] = append(fake.invocations[key], args)
+}
diff --git a/asset-transfer-private-data/chaincode-go/chaincode/mocks/transaction.go b/asset-transfer-private-data/chaincode-go/chaincode/mocks/transaction.go
new file mode 100644
index 0000000..eea37db
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/chaincode/mocks/transaction.go
@@ -0,0 +1,164 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package mocks
+
+import (
+ "sync"
+
+ "github.com/hyperledger/fabric-chaincode-go/pkg/cid"
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+)
+
+type TransactionContext struct {
+ GetClientIdentityStub func() cid.ClientIdentity
+ getClientIdentityMutex sync.RWMutex
+ getClientIdentityArgsForCall []struct {
+ }
+ getClientIdentityReturns struct {
+ result1 cid.ClientIdentity
+ }
+ getClientIdentityReturnsOnCall map[int]struct {
+ result1 cid.ClientIdentity
+ }
+ GetStubStub func() shim.ChaincodeStubInterface
+ getStubMutex sync.RWMutex
+ getStubArgsForCall []struct {
+ }
+ getStubReturns struct {
+ result1 shim.ChaincodeStubInterface
+ }
+ getStubReturnsOnCall map[int]struct {
+ result1 shim.ChaincodeStubInterface
+ }
+ invocations map[string][][]interface{}
+ invocationsMutex sync.RWMutex
+}
+
+func (fake *TransactionContext) GetClientIdentity() cid.ClientIdentity {
+ fake.getClientIdentityMutex.Lock()
+ ret, specificReturn := fake.getClientIdentityReturnsOnCall[len(fake.getClientIdentityArgsForCall)]
+ fake.getClientIdentityArgsForCall = append(fake.getClientIdentityArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetClientIdentity", []interface{}{})
+ fake.getClientIdentityMutex.Unlock()
+ if fake.GetClientIdentityStub != nil {
+ return fake.GetClientIdentityStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getClientIdentityReturns
+ return fakeReturns.result1
+}
+
+func (fake *TransactionContext) GetClientIdentityCallCount() int {
+ fake.getClientIdentityMutex.RLock()
+ defer fake.getClientIdentityMutex.RUnlock()
+ return len(fake.getClientIdentityArgsForCall)
+}
+
+func (fake *TransactionContext) GetClientIdentityCalls(stub func() cid.ClientIdentity) {
+ fake.getClientIdentityMutex.Lock()
+ defer fake.getClientIdentityMutex.Unlock()
+ fake.GetClientIdentityStub = stub
+}
+
+func (fake *TransactionContext) GetClientIdentityReturns(result1 cid.ClientIdentity) {
+ fake.getClientIdentityMutex.Lock()
+ defer fake.getClientIdentityMutex.Unlock()
+ fake.GetClientIdentityStub = nil
+ fake.getClientIdentityReturns = struct {
+ result1 cid.ClientIdentity
+ }{result1}
+}
+
+func (fake *TransactionContext) GetClientIdentityReturnsOnCall(i int, result1 cid.ClientIdentity) {
+ fake.getClientIdentityMutex.Lock()
+ defer fake.getClientIdentityMutex.Unlock()
+ fake.GetClientIdentityStub = nil
+ if fake.getClientIdentityReturnsOnCall == nil {
+ fake.getClientIdentityReturnsOnCall = make(map[int]struct {
+ result1 cid.ClientIdentity
+ })
+ }
+ fake.getClientIdentityReturnsOnCall[i] = struct {
+ result1 cid.ClientIdentity
+ }{result1}
+}
+
+func (fake *TransactionContext) GetStub() shim.ChaincodeStubInterface {
+ fake.getStubMutex.Lock()
+ ret, specificReturn := fake.getStubReturnsOnCall[len(fake.getStubArgsForCall)]
+ fake.getStubArgsForCall = append(fake.getStubArgsForCall, struct {
+ }{})
+ fake.recordInvocation("GetStub", []interface{}{})
+ fake.getStubMutex.Unlock()
+ if fake.GetStubStub != nil {
+ return fake.GetStubStub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ fakeReturns := fake.getStubReturns
+ return fakeReturns.result1
+}
+
+func (fake *TransactionContext) GetStubCallCount() int {
+ fake.getStubMutex.RLock()
+ defer fake.getStubMutex.RUnlock()
+ return len(fake.getStubArgsForCall)
+}
+
+func (fake *TransactionContext) GetStubCalls(stub func() shim.ChaincodeStubInterface) {
+ fake.getStubMutex.Lock()
+ defer fake.getStubMutex.Unlock()
+ fake.GetStubStub = stub
+}
+
+func (fake *TransactionContext) GetStubReturns(result1 shim.ChaincodeStubInterface) {
+ fake.getStubMutex.Lock()
+ defer fake.getStubMutex.Unlock()
+ fake.GetStubStub = nil
+ fake.getStubReturns = struct {
+ result1 shim.ChaincodeStubInterface
+ }{result1}
+}
+
+func (fake *TransactionContext) GetStubReturnsOnCall(i int, result1 shim.ChaincodeStubInterface) {
+ fake.getStubMutex.Lock()
+ defer fake.getStubMutex.Unlock()
+ fake.GetStubStub = nil
+ if fake.getStubReturnsOnCall == nil {
+ fake.getStubReturnsOnCall = make(map[int]struct {
+ result1 shim.ChaincodeStubInterface
+ })
+ }
+ fake.getStubReturnsOnCall[i] = struct {
+ result1 shim.ChaincodeStubInterface
+ }{result1}
+}
+
+func (fake *TransactionContext) Invocations() map[string][][]interface{} {
+ fake.invocationsMutex.RLock()
+ defer fake.invocationsMutex.RUnlock()
+ fake.getClientIdentityMutex.RLock()
+ defer fake.getClientIdentityMutex.RUnlock()
+ fake.getStubMutex.RLock()
+ defer fake.getStubMutex.RUnlock()
+ copiedInvocations := map[string][][]interface{}{}
+ for key, value := range fake.invocations {
+ copiedInvocations[key] = value
+ }
+ return copiedInvocations
+}
+
+func (fake *TransactionContext) recordInvocation(key string, args []interface{}) {
+ fake.invocationsMutex.Lock()
+ defer fake.invocationsMutex.Unlock()
+ if fake.invocations == nil {
+ fake.invocations = map[string][][]interface{}{}
+ }
+ if fake.invocations[key] == nil {
+ fake.invocations[key] = [][]interface{}{}
+ }
+ fake.invocations[key] = append(fake.invocations[key], args)
+}
diff --git a/asset-transfer-private-data/chaincode-go/collections_config.json b/asset-transfer-private-data/chaincode-go/collections_config.json
new file mode 100644
index 0000000..cb3729a
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/collections_config.json
@@ -0,0 +1,35 @@
+[
+ {
+ "name": "assetCollection",
+ "policy": "OR('Org1MSP.member', 'Org2MSP.member')",
+ "requiredPeerCount": 1,
+ "maxPeerCount": 1,
+ "blockToLive":1000000,
+ "memberOnlyRead": true,
+ "memberOnlyWrite": true
+},
+ {
+ "name": "Org1MSPPrivateCollection",
+ "policy": "OR('Org1MSP.member')",
+ "requiredPeerCount": 0,
+ "maxPeerCount": 1,
+ "blockToLive":3,
+ "memberOnlyRead": true,
+ "memberOnlyWrite": false,
+ "endorsementPolicy": {
+ "signaturePolicy": "OR('Org1MSP.member')"
+ }
+ },
+ {
+ "name": "Org2MSPPrivateCollection",
+ "policy": "OR('Org2MSP.member')",
+ "requiredPeerCount": 0,
+ "maxPeerCount": 1,
+ "blockToLive":3,
+ "memberOnlyRead": true,
+ "memberOnlyWrite": false,
+ "endorsementPolicy": {
+ "signaturePolicy": "OR('Org2MSP.member')"
+ }
+ }
+]
diff --git a/asset-transfer-private-data/chaincode-go/go.mod b/asset-transfer-private-data/chaincode-go/go.mod
new file mode 100644
index 0000000..a4725c4
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/go.mod
@@ -0,0 +1,25 @@
+module github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go
+
+go 1.14
+
+require (
+ github.com/go-openapi/jsonreference v0.19.4 // indirect
+ github.com/go-openapi/spec v0.19.8 // indirect
+ github.com/go-openapi/swag v0.19.9 // indirect
+ github.com/gobuffalo/envy v1.9.0 // indirect
+ github.com/gobuffalo/packd v1.0.0 // indirect
+ github.com/golang/protobuf v1.4.2 // indirect
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a
+ github.com/hyperledger/fabric-contract-api-go v1.1.0
+ github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23
+ github.com/mailru/easyjson v0.7.1 // indirect
+ github.com/rogpeppe/go-internal v1.6.0 // indirect
+ github.com/stretchr/testify v1.5.1
+ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
+ golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
+ golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 // indirect
+ google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 // indirect
+ google.golang.org/grpc v1.30.0 // indirect
+ google.golang.org/protobuf v1.25.0
+ gopkg.in/yaml.v2 v2.3.0 // indirect
+)
diff --git a/asset-transfer-private-data/chaincode-go/go.sum b/asset-transfer-private-data/chaincode-go/go.sum
new file mode 100644
index 0000000..f22cc6e
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/go.sum
@@ -0,0 +1,234 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=
+github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/spec v0.19.8 h1:qAdZLh1r6QF/hI/gTq+TJTvsQUodZsM7KLqkAJdiJNg=
+github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=
+github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE=
+github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM=
+github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a h1:KoFw2HnRfW+EItMP0zvUUl1FGzDb/7O0ov7uXZffQok=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23 h1:SEbB3yH4ISTGRifDamYXAst36gO2kM855ndMJlsv+pc=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
+github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.6.0 h1:IZRgg4sfrDH7nsAD1Y/Nwj+GzIfEwpJSLjCaNC3SbsI=
+github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8=
+golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 h1:10ohwcLf82I55O/aQxYqmWKoOdNbQTYYComeP1KDOS4=
+google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/asset-transfer-private-data/chaincode-go/main.go b/asset-transfer-private-data/chaincode-go/main.go
new file mode 100644
index 0000000..6e0d671
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-go/main.go
@@ -0,0 +1,23 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "log"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ "github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode"
+)
+
+func main() {
+ assetChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})
+ if err != nil {
+ log.Panicf("Error creating asset-transfer-private-data chaincode: %v", err)
+ }
+
+ if err := assetChaincode.Start(); err != nil {
+ log.Panicf("Error starting asset-transfer-private-data chaincode: %v", err)
+ }
+}
diff --git a/asset-transfer-private-data/chaincode-java/.gitattributes b/asset-transfer-private-data/chaincode-java/.gitattributes
new file mode 100644
index 0000000..00a51af
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat text eol=crlf
+
diff --git a/asset-transfer-private-data/chaincode-java/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json b/asset-transfer-private-data/chaincode-java/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json
new file mode 100644
index 0000000..2e2a5c6
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json
@@ -0,0 +1,11 @@
+{
+ "index": {
+ "fields": [
+ "objectType",
+ "owner"
+ ]
+ },
+ "ddoc": "indexOwnerDoc",
+ "name": "indexOwner",
+ "type": "json"
+}
diff --git a/asset-transfer-private-data/chaincode-java/build.gradle b/asset-transfer-private-data/chaincode-java/build.gradle
new file mode 100644
index 0000000..bb48d5b
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/build.gradle
@@ -0,0 +1,62 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+plugins {
+ id 'application'
+ id 'checkstyle'
+ id 'jacoco'
+}
+
+group 'org.hyperledger.fabric.samples'
+version '1.0-SNAPSHOT'
+
+dependencies {
+
+ compileOnly 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+
+ testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
+ testImplementation 'org.assertj:assertj-core:3.11.1'
+ testImplementation 'org.mockito:mockito-core:2.+'
+}
+
+repositories {
+ maven {
+ url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
+ }
+ jcenter()
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+application {
+ mainClass = 'org.hyperledger.fabric.contract.ContractRouter'
+}
+
+checkstyle {
+ toolVersion '8.21'
+ configFile file("config/checkstyle/checkstyle.xml")
+}
+
+checkstyleMain {
+ source ='src/main/java'
+}
+
+checkstyleTest {
+ source ='src/test/java'
+}
+
+jacocoTestReport {
+ dependsOn test
+}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
+
+installDist.dependsOn check
\ No newline at end of file
diff --git a/asset-transfer-private-data/chaincode-java/collections_config.json b/asset-transfer-private-data/chaincode-java/collections_config.json
new file mode 100644
index 0000000..cb3729a
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/collections_config.json
@@ -0,0 +1,35 @@
+[
+ {
+ "name": "assetCollection",
+ "policy": "OR('Org1MSP.member', 'Org2MSP.member')",
+ "requiredPeerCount": 1,
+ "maxPeerCount": 1,
+ "blockToLive":1000000,
+ "memberOnlyRead": true,
+ "memberOnlyWrite": true
+},
+ {
+ "name": "Org1MSPPrivateCollection",
+ "policy": "OR('Org1MSP.member')",
+ "requiredPeerCount": 0,
+ "maxPeerCount": 1,
+ "blockToLive":3,
+ "memberOnlyRead": true,
+ "memberOnlyWrite": false,
+ "endorsementPolicy": {
+ "signaturePolicy": "OR('Org1MSP.member')"
+ }
+ },
+ {
+ "name": "Org2MSPPrivateCollection",
+ "policy": "OR('Org2MSP.member')",
+ "requiredPeerCount": 0,
+ "maxPeerCount": 1,
+ "blockToLive":3,
+ "memberOnlyRead": true,
+ "memberOnlyWrite": false,
+ "endorsementPolicy": {
+ "signaturePolicy": "OR('Org2MSP.member')"
+ }
+ }
+]
diff --git a/asset-transfer-private-data/chaincode-java/config/checkstyle/checkstyle.xml b/asset-transfer-private-data/chaincode-java/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..acd5df4
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/config/checkstyle/checkstyle.xml
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/asset-transfer-private-data/chaincode-java/config/checkstyle/suppressions.xml b/asset-transfer-private-data/chaincode-java/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000..33dda04
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/config/checkstyle/suppressions.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/asset-transfer-private-data/chaincode-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-private-data/chaincode-java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
Binary files /dev/null and b/asset-transfer-private-data/chaincode-java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/asset-transfer-private-data/chaincode-java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-private-data/chaincode-java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bb8b2fc
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/asset-transfer-private-data/chaincode-java/gradlew b/asset-transfer-private-data/chaincode-java/gradlew
new file mode 100755
index 0000000..83f2acf
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/asset-transfer-private-data/chaincode-java/gradlew.bat b/asset-transfer-private-data/chaincode-java/gradlew.bat
new file mode 100644
index 0000000..9618d8d
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/asset-transfer-private-data/chaincode-java/settings.gradle b/asset-transfer-private-data/chaincode-java/settings.gradle
new file mode 100644
index 0000000..59cbbbc
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/settings.gradle
@@ -0,0 +1,5 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+rootProject.name = 'private'
diff --git a/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/Asset.java b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/Asset.java
new file mode 100644
index 0000000..11b2910
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/Asset.java
@@ -0,0 +1,126 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.privatedata;
+
+import java.util.Objects;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.json.JSONObject;
+
+@DataType()
+public final class Asset {
+
+ @Property()
+ private final String assetID;
+
+ @Property()
+ private final String objectType;
+
+ @Property()
+ private final String color;
+
+ @Property()
+ private final int size;
+
+ @Property()
+ private String owner;
+
+ public String getAssetID() {
+ return assetID;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public String getObjectType() {
+ return objectType;
+ }
+
+ public void setOwner(final String newowner) {
+ owner = newowner;
+ }
+
+ public Asset(final String type,
+ final String assetID, final String color,
+ final int size, final String owner) {
+ this.objectType = type;
+ this.assetID = assetID;
+ this.color = color;
+ this.size = size;
+ this.owner = owner;
+ }
+
+ public byte[] serialize() {
+ String jsonStr = new JSONObject(this).toString();
+ return jsonStr.getBytes(UTF_8);
+ }
+
+ public static Asset deserialize(final byte[] assetJSON) {
+ return deserialize(new String(assetJSON, UTF_8));
+ }
+
+ public static Asset deserialize(final String assetJSON) {
+ try {
+ JSONObject json = new JSONObject(assetJSON);
+ final String id = json.getString("assetID");
+ final String type = json.getString("objectType");
+ final String color = json.getString("color");
+ final String owner = json.getString("owner");
+ final int size = json.getInt("size");
+ return new Asset(type, id, color, size, owner);
+ } catch (Exception e) {
+ throw new ChaincodeException("Deserialize error: " + e.getMessage(), "DATA_ERROR");
+ }
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if ((obj == null) || (getClass() != obj.getClass())) {
+ return false;
+ }
+
+ Asset other = (Asset) obj;
+
+ return Objects.deepEquals(
+ new String[]{getAssetID(), getColor(), getOwner()},
+ new String[]{other.getAssetID(), other.getColor(), other.getOwner()})
+ &&
+ Objects.deepEquals(
+ new int[]{getSize()},
+ new int[]{other.getSize()});
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getObjectType(), getAssetID(), getColor(), getSize(), getOwner());
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode())
+ + " [assetID=" + assetID + ", type=" + objectType + ", color="
+ + color + ", size=" + size + ", owner=" + owner + "]";
+ }
+
+
+}
diff --git a/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetPrivateDetails.java b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetPrivateDetails.java
new file mode 100644
index 0000000..da1c6e2
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetPrivateDetails.java
@@ -0,0 +1,51 @@
+package org.hyperledger.fabric.samples.privatedata;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.json.JSONObject;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+@DataType()
+public final class AssetPrivateDetails {
+
+ @Property()
+ private final String assetID;
+
+ @Property()
+ private int appraisedValue;
+
+ public String getAssetID() {
+ return assetID;
+ }
+
+ public int getAppraisedValue() {
+ return appraisedValue;
+ }
+
+ public AssetPrivateDetails(final String assetID,
+ final int appraisedValue) {
+ this.assetID = assetID;
+ this.appraisedValue = appraisedValue;
+ }
+
+ public byte[] serialize() {
+ String jsonStr = new JSONObject(this).toString();
+ return jsonStr.getBytes(UTF_8);
+ }
+
+ public static AssetPrivateDetails deserialize(final byte[] assetJSON) {
+ try {
+ JSONObject json = new JSONObject(new String(assetJSON, UTF_8));
+ final String id = json.getString("assetID");
+ final int appraisedValue = json.getInt("appraisedValue");
+ return new AssetPrivateDetails(id, appraisedValue);
+ } catch (Exception e) {
+ throw new ChaincodeException("Deserialize error: " + e.getMessage(), "DATA_ERROR");
+ }
+ }
+
+
+}
diff --git a/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetTransfer.java b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetTransfer.java
new file mode 100644
index 0000000..f95a234
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/AssetTransfer.java
@@ -0,0 +1,593 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.privatedata;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.contract.ContractInterface;
+import org.hyperledger.fabric.contract.annotation.Contact;
+import org.hyperledger.fabric.contract.annotation.Contract;
+import org.hyperledger.fabric.contract.annotation.Default;
+import org.hyperledger.fabric.contract.annotation.Info;
+import org.hyperledger.fabric.contract.annotation.License;
+import org.hyperledger.fabric.contract.annotation.Transaction;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ledger.CompositeKey;
+
+import org.hyperledger.fabric.shim.ledger.KeyValue;
+import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Main Chaincode class. A ContractInterface gets converted to Chaincode internally.
+ * @see org.hyperledger.fabric.shim.Chaincode
+ *
+ * Each chaincode transaction function must take, Context as first parameter.
+ * Unless specified otherwise via annotation (@Contract or @Transaction), the contract name
+ * is the class name (without package)
+ * and the transaction name is the method name.
+ *
+ * To create fabric test-network
+ * cd fabric-samples/test-network
+ * ./network.sh up createChannel -ca -s couchdb
+ * To deploy this chaincode to test-network, use the collection config as described in
+ * See queryResults = new ArrayList<>();
+ // retrieve asset with keys between startKey (inclusive) and endKey(exclusive) in lexical order.
+ try (QueryResultsIterator results = stub.getPrivateDataByRange(ASSET_COLLECTION_NAME, startKey, endKey)) {
+ for (KeyValue result : results) {
+ if (result.getStringValue() == null || result.getStringValue().length() == 0) {
+ System.err.printf("Invalid Asset json: %s\n", result.getStringValue());
+ continue;
+ }
+ Asset asset = Asset.deserialize(result.getStringValue());
+ queryResults.add(asset);
+ System.out.println("QueryResult: " + asset.toString());
+ }
+ }
+ return queryResults.toArray(new Asset[0]);
+ }
+
+ // =======Rich queries =========================================================================
+ // Two examples of rich queries are provided below (parameterized query and ad hoc query).
+ // Rich queries pass a query string to the state database.
+ // Rich queries are only supported by state database implementations
+ // that support rich query (e.g. CouchDB).
+ // The query string is in the syntax of the underlying state database.
+ // With rich queries there is no guarantee that the result set hasn't changed between
+ // endorsement time and commit time, aka 'phantom reads'.
+ // Therefore, rich queries should not be used in update transactions, unless the
+ // application handles the possibility of result set changes between endorsement and commit time.
+ // Rich queries can be used for point-in-time queries against a peer.
+ // ============================================================================================
+
+ /**
+ * QueryAssetByOwner queries for assets based on assetType, owner.
+ * This is an example of a parameterized query where the query logic is baked into the chaincode,
+ * and accepting a single query parameter (owner).
+ *
+ * @param ctx the transaction context
+ * @param assetType type to query for
+ * @param owner asset owner to query for
+ * @return the asset found on the ledger if there was one
+ */
+ @Transaction(intent = Transaction.TYPE.EVALUATE)
+ public Asset[] QueryAssetByOwner(final Context ctx, final String assetType, final String owner) throws Exception {
+ String queryString = String.format("{\"selector\":{\"objectType\":\"%s\",\"owner\":\"%s\"}}", assetType, owner);
+ return getQueryResult(ctx, queryString);
+ }
+
+ /**
+ * QueryAssets uses a query string to perform a query for assets.
+ * Query string matching state database syntax is passed in and executed as is.
+ * Supports ad hoc queries that can be defined at runtime by the client.
+ *
+ * @param ctx the transaction context
+ * @param queryString query string matching state database syntax
+ * @return the asset found on the ledger if there was one
+ */
+ @Transaction(intent = Transaction.TYPE.EVALUATE)
+ public Asset[] QueryAssets(final Context ctx, final String queryString) throws Exception {
+ return getQueryResult(ctx, queryString);
+ }
+
+ private Asset[] getQueryResult(final Context ctx, final String queryString) throws Exception {
+ ChaincodeStub stub = ctx.getStub();
+ System.out.printf("QueryAssets: %s\n", queryString);
+
+ List queryResults = new ArrayList();
+ // retrieve asset with keys between startKey (inclusive) and endKey(exclusive) in lexical order.
+ try (QueryResultsIterator results = stub.getPrivateDataQueryResult(ASSET_COLLECTION_NAME, queryString)) {
+ for (KeyValue result : results) {
+ if (result.getStringValue() == null || result.getStringValue().length() == 0) {
+ System.err.printf("Invalid Asset json: %s\n", result.getStringValue());
+ continue;
+ }
+ Asset asset = Asset.deserialize(result.getStringValue());
+ queryResults.add(asset);
+ System.out.println("QueryResult: " + asset.toString());
+ }
+ }
+ return queryResults.toArray(new Asset[0]);
+ }
+
+
+ /**
+ * Creates a new asset on the ledger from asset properties passed in as transient map.
+ * Asset owner will be inferred from the ClientId via stub api
+ *
+ * @param ctx the transaction context
+ * Transient map with asset_properties key with asset json as value
+ * @return the created asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset CreateAsset(final Context ctx) {
+ ChaincodeStub stub = ctx.getStub();
+ Map transientMap = ctx.getStub().getTransient();
+ if (!transientMap.containsKey("asset_properties")) {
+ String errorMessage = String.format("CreateAsset call must specify asset_properties in Transient map input");
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ byte[] transientAssetJSON = transientMap.get("asset_properties");
+ final String assetID;
+ final String type;
+ final String color;
+ int appraisedValue = 0;
+ int size = 0;
+ try {
+ JSONObject json = new JSONObject(new String(transientAssetJSON, UTF_8));
+ Map tMap = json.toMap();
+
+ type = (String) tMap.get("objectType");
+ assetID = (String) tMap.get("assetID");
+ color = (String) tMap.get("color");
+ if (tMap.containsKey("size")) {
+ size = (Integer) tMap.get("size");
+ }
+ if (tMap.containsKey("appraisedValue")) {
+ appraisedValue = (Integer) tMap.get("appraisedValue");
+ }
+ } catch (Exception err) {
+ String errorMessage = String.format("TransientMap deserialized error: %s ", err);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ //input validations
+ String errorMessage = null;
+ if (assetID.equals("")) {
+ errorMessage = String.format("Empty input in Transient map: assetID");
+ }
+ if (type.equals("")) {
+ errorMessage = String.format("Empty input in Transient map: objectType");
+ }
+ if (color.equals("")) {
+ errorMessage = String.format("Empty input in Transient map: color");
+ }
+ if (size <= 0) {
+ errorMessage = String.format("Empty input in Transient map: size");
+ }
+ if (appraisedValue <= 0) {
+ errorMessage = String.format("Empty input in Transient map: appraisedValue");
+ }
+
+ if (errorMessage != null) {
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ Asset asset = new Asset(type, assetID, color, size, "");
+ // Check if asset already exists
+ byte[] assetJSON = ctx.getStub().getPrivateData(ASSET_COLLECTION_NAME, assetID);
+ if (assetJSON != null && assetJSON.length > 0) {
+ errorMessage = String.format("Asset %s already exists", assetID);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_ALREADY_EXISTS.toString());
+ }
+
+ // Get ID of submitting client identity
+ String clientID = ctx.getClientIdentity().getId();
+
+ // Verify that the client is submitting request to peer in their organization
+ // This is to ensure that a client from another org doesn't attempt to read or
+ // write private data from this peer.
+ verifyClientOrgMatchesPeerOrg(ctx);
+
+ //Make submitting client the owner
+ asset.setOwner(clientID);
+ System.out.printf("CreateAsset Put: collection %s, ID %s\n", ASSET_COLLECTION_NAME, assetID);
+ System.out.printf("Put: collection %s, ID %s\n", ASSET_COLLECTION_NAME, new String(asset.serialize()));
+ stub.putPrivateData(ASSET_COLLECTION_NAME, assetID, asset.serialize());
+
+ // Get collection name for this organization.
+ String orgCollectionName = getCollectionName(ctx);
+
+ //Save AssetPrivateDetails to org collection
+ AssetPrivateDetails assetPriv = new AssetPrivateDetails(assetID, appraisedValue);
+ System.out.printf("Put AssetPrivateDetails: collection %s, ID %s\n", orgCollectionName, assetID);
+ stub.putPrivateData(orgCollectionName, assetID, assetPriv.serialize());
+
+ return asset;
+ }
+
+ /**
+ * AgreeToTransfer is used by the potential buyer of the asset to agree to the
+ * asset value. The agreed to appraisal value is stored in the buying orgs
+ * org specifc collection, while the the buyer client ID is stored in the asset collection
+ * using a composite key
+ * Uses transient map with key asset_value
+ *
+ * @param ctx the transaction context
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public void AgreeToTransfer(final Context ctx) {
+ ChaincodeStub stub = ctx.getStub();
+ Map transientMap = ctx.getStub().getTransient();
+ if (!transientMap.containsKey("asset_value")) {
+ String errorMessage = String.format("AgreeToTransfer call must specify \"asset_value\" in Transient map input");
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ byte[] transientAssetJSON = transientMap.get("asset_value");
+ AssetPrivateDetails assetPriv;
+ String assetID;
+ try {
+ JSONObject json = new JSONObject(new String(transientAssetJSON, UTF_8));
+ assetID = json.getString("assetID");
+ final int appraisedValue = json.getInt("appraisedValue");
+
+ assetPriv = new AssetPrivateDetails(assetID, appraisedValue);
+ } catch (Exception err) {
+ String errorMessage = String.format("TransientMap deserialized error %s ", err);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ if (assetID.equals("")) {
+ String errorMessage = String.format("Invalid input in Transient map: assetID");
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+ if (assetPriv.getAppraisedValue() <= 0) { // appraisedValue field must be a positive integer
+ String errorMessage = String.format("Input must be positive integer: appraisedValue");
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+ System.out.printf("AgreeToTransfer: verify asset %s exists\n", assetID);
+ Asset existing = ReadAsset(ctx, assetID);
+ if (existing == null) {
+ String errorMessage = String.format("Asset does not exist in the collection: ", assetID);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+ // Get collection name for this organization.
+ String orgCollectionName = getCollectionName(ctx);
+
+ verifyClientOrgMatchesPeerOrg(ctx);
+
+ //Save AssetPrivateDetails to org collection
+ System.out.printf("Put AssetPrivateDetails: collection %s, ID %s\n", orgCollectionName, assetID);
+ stub.putPrivateData(orgCollectionName, assetID, assetPriv.serialize());
+
+ String clientID = ctx.getClientIdentity().getId();
+ //Write the AgreeToTransfer key in assetCollection
+ CompositeKey aggKey = stub.createCompositeKey(AGREEMENT_KEYPREFIX, assetID);
+ System.out.printf("AgreeToTransfer Put: collection %s, ID %s, Key %s\n", ASSET_COLLECTION_NAME, assetID, aggKey);
+ stub.putPrivateData(ASSET_COLLECTION_NAME, aggKey.toString(), clientID);
+ }
+
+ /**
+ * TransferAsset transfers the asset to the new owner by setting a new owner ID based on
+ * AgreeToTransfer data
+ *
+ * @param ctx the transaction context
+ * @return none
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public void TransferAsset(final Context ctx) {
+ ChaincodeStub stub = ctx.getStub();
+ Map transientMap = ctx.getStub().getTransient();
+ if (!transientMap.containsKey("asset_owner")) {
+ String errorMessage = "TransferAsset call must specify \"asset_owner\" in Transient map input";
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ byte[] transientAssetJSON = transientMap.get("asset_owner");
+ final String assetID;
+ final String buyerMSP;
+ try {
+ JSONObject json = new JSONObject(new String(transientAssetJSON, UTF_8));
+ assetID = json.getString("assetID");
+ buyerMSP = json.getString("buyerMSP");
+
+ } catch (Exception err) {
+ String errorMessage = String.format("TransientMap deserialized error %s ", err);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ if (assetID.equals("")) {
+ String errorMessage = String.format("Invalid input in Transient map: " + "assetID");
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+ if (buyerMSP.equals("")) {
+ String errorMessage = String.format("Invalid input in Transient map: " + "buyerMSP");
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ System.out.printf("TransferAsset: verify asset %s exists\n", assetID);
+ byte[] assetJSON = stub.getPrivateData(ASSET_COLLECTION_NAME, assetID);
+
+ if (assetJSON == null || assetJSON.length == 0) {
+ String errorMessage = String.format("Asset %s does not exist in the collection", assetID);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ verifyClientOrgMatchesPeerOrg(ctx);
+ Asset thisAsset = Asset.deserialize(assetJSON);
+ // Verify transfer details and transfer owner
+ verifyAgreement(ctx, assetID, thisAsset.getOwner(), buyerMSP);
+
+ TransferAgreement transferAgreement = ReadTransferAgreement(ctx, assetID);
+ if (transferAgreement == null) {
+ String errorMessage = String.format("TransferAgreement does not exist for asset: %s", assetID);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ // Transfer asset in private data collection to new owner
+ String newOwner = transferAgreement.getBuyerID();
+ thisAsset.setOwner(newOwner);
+
+ //Save updated Asset to collection
+ System.out.printf("Transfer Asset: collection %s, ID %s to owner %s\n", ASSET_COLLECTION_NAME, assetID, newOwner);
+ stub.putPrivateData(ASSET_COLLECTION_NAME, assetID, thisAsset.serialize());
+
+ // delete the key from owners collection
+ String ownersCollectionName = getCollectionName(ctx);
+ stub.delPrivateData(ownersCollectionName, assetID);
+
+ //Delete the transfer agreement from the asset collection
+ CompositeKey aggKey = stub.createCompositeKey(AGREEMENT_KEYPREFIX, assetID);
+ System.out.printf("AgreeToTransfer deleteKey: collection %s, ID %s, Key %s\n", ASSET_COLLECTION_NAME, assetID, aggKey);
+ stub.delPrivateData(ASSET_COLLECTION_NAME, aggKey.toString());
+ }
+
+ /**
+ * Deletes a asset & related details from the ledger.
+ * Input in transient map: asset_delete
+ *
+ * @param ctx the transaction context
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public void DeleteAsset(final Context ctx) {
+ ChaincodeStub stub = ctx.getStub();
+ Map transientMap = ctx.getStub().getTransient();
+ if (!transientMap.containsKey("asset_delete")) {
+ String errorMessage = String.format("DeleteAsset call must specify 'asset_delete' in Transient map input");
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ byte[] transientAssetJSON = transientMap.get("asset_delete");
+ final String assetID;
+
+ try {
+ JSONObject json = new JSONObject(new String(transientAssetJSON, UTF_8));
+ assetID = json.getString("assetID");
+
+ } catch (Exception err) {
+ String errorMessage = String.format("TransientMap deserialized error: %s ", err);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INCOMPLETE_INPUT.toString());
+ }
+
+ System.out.printf("DeleteAsset: verify asset %s exists\n", assetID);
+ byte[] assetJSON = stub.getPrivateData(ASSET_COLLECTION_NAME, assetID);
+
+ if (assetJSON == null || assetJSON.length == 0) {
+ String errorMessage = String.format("Asset %s does not exist", assetID);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+ String ownersCollectionName = getCollectionName(ctx);
+ byte[] apdJSON = stub.getPrivateData(ownersCollectionName, assetID);
+
+ if (apdJSON == null || apdJSON.length == 0) {
+ String errorMessage = String.format("Failed to read asset from owner's Collection %s", ownersCollectionName);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+ verifyClientOrgMatchesPeerOrg(ctx);
+
+ // delete the key from asset collection
+ System.out.printf("DeleteAsset: collection %s, ID %s\n", ASSET_COLLECTION_NAME, assetID);
+ stub.delPrivateData(ASSET_COLLECTION_NAME, assetID);
+
+ // Finally, delete private details of asset
+ stub.delPrivateData(ownersCollectionName, assetID);
+ }
+
+ // Used by TransferAsset to verify that the transfer is being initiated by the owner and that
+ // the buyer has agreed to the same appraisal value as the owner
+ private void verifyAgreement(final Context ctx, final String assetID, final String owner, final String buyerMSP) {
+ String clienID = ctx.getClientIdentity().getId();
+
+ // Check 1: verify that the transfer is being initiatied by the owner
+ if (!clienID.equals(owner)) {
+ throw new ChaincodeException("Submitting client identity does not own the asset", AssetTransferErrors.INVALID_ACCESS.toString());
+ }
+
+ // Check 2: verify that the buyer has agreed to the appraised value
+ String collectionOwner = getCollectionName(ctx); // get owner collection from caller identity
+ String collectionBuyer = buyerMSP + "PrivateCollection";
+
+ // Get hash of owners agreed to value
+ byte[] ownerAppraisedValueHash = ctx.getStub().getPrivateDataHash(collectionOwner, assetID);
+ if (ownerAppraisedValueHash == null) {
+ throw new ChaincodeException(String.format("Hash of appraised value for %s does not exist in collection %s", assetID, collectionOwner));
+ }
+
+ // Get hash of buyers agreed to value
+ byte[] buyerAppraisedValueHash = ctx.getStub().getPrivateDataHash(collectionBuyer, assetID);
+ if (buyerAppraisedValueHash == null) {
+ throw new ChaincodeException(String.format("Hash of appraised value for %s does not exist in collection %s. AgreeToTransfer must be called by the buyer first.", assetID, collectionBuyer));
+ }
+
+ // Verify that the two hashes match
+ if (!Arrays.equals(ownerAppraisedValueHash, buyerAppraisedValueHash)) {
+ throw new ChaincodeException(String.format("Hash for appraised value for owner %x does not match value for seller %x", ownerAppraisedValueHash, buyerAppraisedValueHash));
+ }
+ }
+
+ private void verifyClientOrgMatchesPeerOrg(final Context ctx) {
+ String clientMSPID = ctx.getClientIdentity().getMSPID();
+ String peerMSPID = ctx.getStub().getMspId();
+
+ if (!peerMSPID.equals(clientMSPID)) {
+ String errorMessage = String.format("Client from org %s is not authorized to read or write private data from an org %s peer", clientMSPID, peerMSPID);
+ System.err.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.INVALID_ACCESS.toString());
+ }
+ }
+
+ private String getCollectionName(final Context ctx) {
+
+ // Get the MSP ID of submitting client identity
+ String clientMSPID = ctx.getClientIdentity().getMSPID();
+ // Create the collection name
+ return clientMSPID + "PrivateCollection";
+ }
+
+}
diff --git a/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/TransferAgreement.java b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/TransferAgreement.java
new file mode 100644
index 0000000..2082d97
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/src/main/java/org/hyperledger/fabric/samples/privatedata/TransferAgreement.java
@@ -0,0 +1,51 @@
+package org.hyperledger.fabric.samples.privatedata;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.json.JSONObject;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+@DataType()
+public final class TransferAgreement {
+
+ @Property()
+ private final String assetID;
+
+
+ @Property()
+ private String buyerID;
+
+ public String getAssetID() {
+ return assetID;
+ }
+
+ public String getBuyerID() {
+ return buyerID;
+ }
+
+ public TransferAgreement(final String assetID,
+ final String buyer) {
+ this.assetID = assetID;
+ this.buyerID = buyer;
+ }
+
+ public byte[] serialize() {
+ String jsonStr = new JSONObject(this).toString();
+ return jsonStr.getBytes(UTF_8);
+ }
+
+ public static TransferAgreement deserialize(final byte[] assetJSON) {
+ try {
+ JSONObject json = new JSONObject(new String(assetJSON, UTF_8));
+ final String id = json.getString("assetID");
+ final String buyerID = json.getString("buyerID");
+ return new TransferAgreement(id, buyerID);
+ } catch (Exception e) {
+ throw new ChaincodeException("Deserialize error: " + e.getMessage(), "DATA_ERROR");
+ }
+ }
+
+
+}
diff --git a/asset-transfer-private-data/chaincode-java/src/test/java/org/hyperledger/fabric/samples/privatedata/AssetTransferTest.java b/asset-transfer-private-data/chaincode-java/src/test/java/org/hyperledger/fabric/samples/privatedata/AssetTransferTest.java
new file mode 100644
index 0000000..a11a5f0
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/src/test/java/org/hyperledger/fabric/samples/privatedata/AssetTransferTest.java
@@ -0,0 +1,170 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.privatedata;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.ThrowableAssert.catchThrowable;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.hyperledger.fabric.samples.privatedata.AssetTransfer.AGREEMENT_KEYPREFIX;
+import static org.hyperledger.fabric.samples.privatedata.AssetTransfer.ASSET_COLLECTION_NAME;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.util.HashMap;
+import java.util.Map;
+import org.hyperledger.fabric.contract.ClientIdentity;
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ledger.CompositeKey;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+public final class AssetTransferTest {
+
+ @Nested
+ class InvokeWriteTransaction {
+
+ @Test
+ public void createAssetWhenAssetExists() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ Map m = new HashMap();
+ m.put("asset_properties", dataAsset1Bytes);
+ when(ctx.getStub().getTransient()).thenReturn(m);
+ when(stub.getPrivateData(ASSET_COLLECTION_NAME, testAsset1ID))
+ .thenReturn(dataAsset1Bytes);
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.CreateAsset(ctx);
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Asset asset1 already exists");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_ALREADY_EXISTS".getBytes());
+ }
+
+ @Test
+ public void createAssetWhenNewAssetIsCreated() throws CertificateException, IOException {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getMspId()).thenReturn(testOrgOneMSP);
+ ClientIdentity ci = mock(ClientIdentity.class);
+ when(ci.getId()).thenReturn(testOrg1Client);
+ when(ci.getMSPID()).thenReturn(testOrgOneMSP);
+ when(ctx.getClientIdentity()).thenReturn(ci);
+
+ Map m = new HashMap();
+ m.put("asset_properties", dataAsset1Bytes);
+ when(ctx.getStub().getTransient()).thenReturn(m);
+
+ when(stub.getPrivateData(ASSET_COLLECTION_NAME, testAsset1ID))
+ .thenReturn(new byte[0]);
+
+ Asset created = contract.CreateAsset(ctx);
+ assertThat(created).isEqualTo(testAsset1);
+
+ verify(stub).putPrivateData(ASSET_COLLECTION_NAME, testAsset1ID, created.serialize());
+ }
+
+ @Test
+ public void transferAssetWhenExistingAssetIsTransferred() throws CertificateException, IOException {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getMspId()).thenReturn(testOrgOneMSP);
+ ClientIdentity ci = mock(ClientIdentity.class);
+ when(ci.getId()).thenReturn(testOrg1Client);
+ when(ctx.getClientIdentity()).thenReturn(ci);
+ when(ci.getMSPID()).thenReturn(testOrgOneMSP);
+ final String recipientOrgMsp = "TestOrg2";
+ final String buyerIdentity = "TestOrg2User";
+ Map m = new HashMap();
+ m.put("asset_owner", ("{ \"buyerMSP\": \"" + recipientOrgMsp + "\", \"assetID\": \"" + testAsset1ID + "\" }").getBytes());
+ when(ctx.getStub().getTransient()).thenReturn(m);
+
+ when(stub.getPrivateDataHash(anyString(), anyString())).thenReturn("TestHashValue".getBytes());
+ when(stub.getPrivateData(ASSET_COLLECTION_NAME, testAsset1ID))
+ .thenReturn(dataAsset1Bytes);
+ CompositeKey ck = mock(CompositeKey.class);
+ when(ck.toString()).thenReturn(AGREEMENT_KEYPREFIX + testAsset1ID);
+ when(stub.createCompositeKey(AGREEMENT_KEYPREFIX, testAsset1ID)).thenReturn(ck);
+ when(stub.getPrivateData(ASSET_COLLECTION_NAME, AGREEMENT_KEYPREFIX + testAsset1ID)).thenReturn(buyerIdentity.getBytes(UTF_8));
+ contract.TransferAsset(ctx);
+
+ Asset exptectedAfterTransfer = Asset.deserialize("{ \"objectType\": \"testasset\", \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"" + buyerIdentity + "\", \"appraisedValue\": 300 }");
+
+ verify(stub).putPrivateData(ASSET_COLLECTION_NAME, testAsset1ID, exptectedAfterTransfer.serialize());
+ String collectionOwner = testOrgOneMSP + "PrivateCollection";
+ verify(stub).delPrivateData(collectionOwner, testAsset1ID);
+ verify(stub).delPrivateData(ASSET_COLLECTION_NAME, AGREEMENT_KEYPREFIX + testAsset1ID);
+ }
+ }
+
+ @Nested
+ class QueryReadAssetTransaction {
+
+ @Test
+ public void whenAssetExists() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getPrivateData(ASSET_COLLECTION_NAME, testAsset1ID))
+ .thenReturn(dataAsset1Bytes);
+
+ Asset asset = contract.ReadAsset(ctx, testAsset1ID);
+
+ assertThat(asset).isEqualTo(testAsset1);
+ }
+
+ @Test
+ public void whenAssetDoesNotExist() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState(testAsset1ID)).thenReturn(null);
+
+ Asset asset = contract.ReadAsset(ctx, testAsset1ID);
+ assertThat(asset).isNull();
+ }
+
+ @Test
+ public void invokeUnknownTransaction() {
+ AssetTransfer contract = new AssetTransfer();
+ Context ctx = mock(Context.class);
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.unknownTransaction(ctx);
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Undefined contract method called");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo(null);
+
+ verifyZeroInteractions(ctx);
+ }
+
+ }
+
+ private static String testOrgOneMSP = "TestOrg1";
+ private static String testOrg1Client = "testOrg1User";
+
+ private static String testAsset1ID = "asset1";
+ private static Asset testAsset1 = new Asset("testasset", "asset1", "blue", 5, testOrg1Client);
+ private static byte[] dataAsset1Bytes = "{ \"objectType\": \"testasset\", \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"testOrg1User\", \"appraisedValue\": 300 }".getBytes();
+
+}
diff --git a/asset-transfer-private-data/chaincode-java/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/asset-transfer-private-data/chaincode-java/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d
--- /dev/null
+++ b/asset-transfer-private-data/chaincode-java/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/asset-transfer-sbe/README.md b/asset-transfer-sbe/README.md
new file mode 100644
index 0000000..00549bb
--- /dev/null
+++ b/asset-transfer-sbe/README.md
@@ -0,0 +1,169 @@
+# State-based endorsement asset transfer sample
+
+Transactions that are submitted to Hyperledger Fabric networks need to be endorsed
+by peers that are joined to a channel before the transaction can be added to the
+ledger. Fabric peers endorse transactions by executing a smart contract using the
+inputs of the transaction proposal. The peers then sign the input and output
+generated by the smart contract execution. The endorsement policy specifies the
+set of organizations whose peers need to endorse a transaction before it can be
+added to the ledger.
+
+Each chaincode that is deployed to a channel has an endorsement policy that governs
+the assets managed by the chaincode smart contracts. However, you can override the
+chaincode level endorsement policy to create an endorsement policy for a specific key,
+either on the public channel ledger or in a private collection.
+State-based endorsement policies, also known as key-level endorsement policies, allow
+channel members to use different endorsement policies for assets that are managed by
+the same smart contract. For more information about endorsement policies and
+state-based endorsement, visit the
+[Endorsement Policies](https://hyperledger-fabric.readthedocs.io/en/release-2.2/endorsement-policies.html)
+topic in the Fabric documentation.
+
+The implementation provided by State Based interface creates a policy which requires signatures from all the Org principals added, and hence is equivalent to an AND policy. For other advanced State Based policy implementations which are not supported by State Based interface directly like OR or NOutOf policies, please refer to method implementations setStateBasedEndorsementNOutOf(), which can be used as an alternative for setStateBasedEndorsement() inside asset-transfer-sbe smart contracts.
+
+## About the Sample
+
+The state-based endorsement (SBE) asset transfer sample demonstrates how to use
+key-level endorsement policies to ensure that an asset only is endorsed by an
+asset owner. In the course of the tutorial, you will use the smart contract
+to complete the following transfer scenario:
+
+- Deploy the SBE smart contract to a channel that was created using the Fabric
+test network. The channel will have two members, Org1 and Org2, that will
+participate in the asset transfer. Each organization operates one peer that
+is joined to the channel.
+- Create an asset using the chaincode endorsement policy. The chaincode level
+endorsement policy requires that a majority of organizations on the channel
+endorse a transaction. This means that a transaction that creates an asset
+needs to be endorsed by peers that belong to Org1 and Org2. When the asset is
+created, the smart contract creates a state-based endorsement policy that
+specifies that only the organization that owns that asset may endorse update
+transactions. Because the asset is owned by Org1, any future updates to the asset
+need to be endorsed by the Org1 peer.
+- Update the asset by only endorsing with Org1, this will use the state-based
+endorsement policy applied to the asset when it was created by the chaincode.
+- Transfer the asset to Org2. During the execution of the transfer transaction,
+the chaincode will create a new state-based endorsement policy that reflects
+the new asset owner for the asset.
+- Update the asset once more, this time with Org2 as the owner. Because the
+state-based endorsement policy has been updated, this transaction only needs
+to be endorsed by Org2.
+
+## Deploy the smart contract
+
+We are going to run the SBE smart contract using the Fabric test network. Open a command terminal and navigate to test network directory in your local clone of the `fabric-samples`. We will operate from the `test-network` directory for the remainder of the tutorial.
+```
+cd fabric-samples/test-network
+```
+
+Run the following command to deploy the test network and create a channel named `mychannel`:
+
+```
+./network.sh up createChannel
+```
+
+You can use the test network script to deploy the smart contract to the channel that was just created. The script uses the Fabric chaincode lifecycle to deploy the smart contract to the channel. We will use the default chaincode level endorsement policy used by the Fabric chaincode lifecycle, which requires an endorsement from a majority of channel members. In our case, this will require that both Org1 and Org2 endorse a transaction (2 of 2). Deploy the smart contract to `mychannel` using the following command:
+```
+./network.sh deployCC -ccn sbe -ccp ../asset-transfer-sbe/chaincode-typescript/ -ccl typescript
+```
+
+Set the following environment variables to interact with the network as a user from Org1:
+
+```
+export PATH=${PWD}/../bin:${PWD}:$PATH
+export FABRIC_CFG_PATH=$PWD/../config/
+export CORE_PEER_TLS_ENABLED=true
+export CORE_PEER_LOCALMSPID="Org1MSP"
+export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp
+export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
+export CORE_PEER_ADDRESS=localhost:7051
+```
+
+## Run the transfer scenario
+
+We can now invoke the SBE smart contract to create a new asset:
+```
+peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"CreateAsset","Args":["asset1","100","Org1User1"]}'
+```
+The create transaction needs to target both peers from Org1 and Org2 to meet the chaincode endorsement policy. The chaincode will read the MSP ID of the client user submitting the transaction and assign that organization as the asset owner. As a result, the asset will initially be owned by Org1.
+
+You can query the asset using with the following command:
+```
+peer chaincode query -C mychannel -n sbe -c '{"Args":["ReadAsset","asset1"]}'
+```
+The result is a new asset owned by Org1, identified using the Org1 MSP ID `Org1MSP`:
+`{"ID":"asset1","Value":100,"Owner":"Org1User1","OwnerOrg":"Org1MSP"}`
+
+In addition to creating the asset, the `CreateAsset` function also sets a state-based endorsement policy for the asset. Only a peer of the asset owner, can successfully endorse an asset update. To demonstrate the key-level endorsement policy, lets try to update the asset while targeting the Org2 peer:
+```
+peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"UpdateAsset","Args":["asset1","200"]}'
+```
+The result is an endorsement policy failure:
+```
+Error: transaction invalidated with status (ENDORSEMENT_POLICY_FAILURE) - proposal response:
+```
+
+If we attempt to update the asset with an endorsement from the Org1 peer, the update succeeds:
+```
+peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt -c '{"function":"UpdateAsset","Args":["asset1","200"]}'
+```
+You can query the asset one more time to verify that the update was successful:
+```
+peer chaincode query -C mychannel -n sbe -c '{"Args":["ReadAsset","asset1"]}'
+```
+
+The asset value is now 200:
+```
+{"ID":"asset1","Value":200,"Owner":"Org1User1","OwnerOrg":"Org1MSP"}
+```
+
+Now that we have tested the asset key-level endorsement policy, we can transfer the asset to Org2. Run the following command to transfer the asset from Org1 to Org2. This time the Org2 MSP ID is provided as a transaction input. The `TransferAsset` function will update the endorsement policy to specify that only a peer of the new owner can update the asset. Note that this command targets the Org1 peer.
+
+```
+peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt -c '{"function":"TransferAsset","Args":["asset1","Org2User1","Org2MSP"]}'
+```
+
+We can query the asset to see that the owner has been updated from Org1 to Org2:
+```
+peer chaincode query -C mychannel -n sbe -c '{"Args":["ReadAsset","asset1"]}'
+```
+
+The owning organization is now Org2:
+```
+{"ID":"asset1","Value":200,"Owner":"Org2User1","OwnerOrg":"Org2MSP"}
+```
+
+Org2 now needs to endorse any asset updates. Run the following command to try to update the asset with an endorsement from the Org1 peer:
+```
+peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt -c '{"function":"UpdateAsset","Args":["asset1","300"]}'
+```
+
+The response will be an endorsement policy failure:
+```
+Error: transaction invalidated with status (ENDORSEMENT_POLICY_FAILURE) - proposal response:
+```
+
+Now try to update the asset with an endorsement from the Org2 peer:
+```
+peer chaincode invoke -o localhost:7050 --waitForEvent --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n sbe --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"UpdateAsset","Args":["asset1","300"]}'
+```
+
+You can query the asset again to verify that the transaction update succeeded:
+```
+peer chaincode query -C mychannel -n sbe -c '{"Args":["ReadAsset","asset1"]}'
+```
+
+The asset value is now 300:
+```
+{"ID":"asset1","Value":300,"Owner":"Org2User1","OwnerOrg":"Org2MSP"}
+```
+
+Note that the transaction to update the asset was submitted by a user from Org1, even though the asset was owned by Org2. The transfer enabled by the SBE smart contract is a simple scenario meant only to demonstrate the use of state-based endorsement policies. The smart contract can use access control to specify that an asset can only be updated by its owner. Private data collections can also be used to ensure that transfers need to be endorsed by the owner and recipient of the transfer, instead of just the asset owner. For a more realistic example of an asset transfer scenario, see the [Secured asset transfer in Fabric](https://hyperledger-fabric.readthedocs.io/en/master/secured_asset_transfer/secured_private_asset_transfer_tutorial.html) tutorial.
+
+## Clean up
+
+When you are finished, you can bring down the test network. The command will remove all the nodes of the test network, and delete any ledger data that you created:
+
+```
+./network.sh down
+```
diff --git a/asset-transfer-sbe/application-javascript/.eslintignore b/asset-transfer-sbe/application-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-sbe/application-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-sbe/application-javascript/.eslintrc.js b/asset-transfer-sbe/application-javascript/.eslintrc.js
new file mode 100644
index 0000000..072edaf
--- /dev/null
+++ b/asset-transfer-sbe/application-javascript/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+'use strict';
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: 'eslint:recommended',
+ rules: {
+ indent: ['error', 'tab'],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/asset-transfer-sbe/application-javascript/.gitignore b/asset-transfer-sbe/application-javascript/.gitignore
new file mode 100644
index 0000000..21b287f
--- /dev/null
+++ b/asset-transfer-sbe/application-javascript/.gitignore
@@ -0,0 +1,14 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+package-lock.json
+
+wallet
+!wallet/.gitkeep
diff --git a/asset-transfer-sbe/application-javascript/app.js b/asset-transfer-sbe/application-javascript/app.js
new file mode 100644
index 0000000..47adcca
--- /dev/null
+++ b/asset-transfer-sbe/application-javascript/app.js
@@ -0,0 +1,349 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+/**
+ * A test application to show state based endorsements operations with a running
+ * asset-transfer-sbe chaincode with discovery.
+ * -- How to submit a transaction
+ * -- How to query
+ * -- How to limit the organizations involved in a transaction
+ *
+ * To see the SDK workings, try setting the logging to show on the console before running
+ * export HFC_LOGGING='{"debug":"console"}'
+ */
+
+// pre-requisites:
+// - fabric-sample two organization test-network setup with two peers, ordering service,
+// and 2 certificate authorities
+// ===> from directory /fabric-samples/test-network
+// ./network.sh up createChannel -ca
+// - Use any of the asset-transfer-sbe chaincodes deployed on the channel "mychannel"
+// with the chaincode name of "sbe". The following deploy command will package,
+// install, approve, and commit the javascript chaincode, all the actions it takes
+// to deploy a chaincode to a channel.
+// ===> from directory /fabric-samples/test-network
+// ./network.sh deployCC -ccn sbe -ccp ../asset-transfer-sbe/chaincode-typescript/ -ccl typescript
+// - Be sure that node.js is installed
+// ===> from directory /fabric-samples/asset-transfer-sbe/application-javascript
+// node -v
+// - npm installed code dependencies
+// ===> from directory /fabric-samples/asset-transfer-sbe/application-javascript
+// npm install
+// - to run this test application
+// ===> from directory /fabric-samples/asset-transfer-sbe/application-javascript
+// node app.js
+
+// NOTE: If you see an error like these:
+/*
+
+ Error in setup: Error: DiscoveryService: mychannel error: access denied
+
+ OR
+
+ Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
+
+ */
+// Delete the /fabric-samples/asset-transfer-sbe/application-javascript/wallet directory
+// and retry this application.
+//
+// The certificate authority must have been restarted and the saved certificates for the
+// admin and application user are not valid. Deleting the wallet store will force these to be reset
+// with the new certificate authority.
+//
+
+const { Gateway, Wallets } = require('fabric-network');
+const FabricCAServices = require('fabric-ca-client');
+const path = require('path');
+const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const channelName = 'mychannel';
+const chaincodeName = 'sbe';
+
+const org1 = 'Org1MSP';
+const org2 = 'Org2MSP';
+const Org1UserId = 'appUser1';
+const Org2UserId = 'appUser2';
+
+async function initGatewayForOrg1() {
+ console.log('\n--> Fabric client user & Gateway init: Using Org1 identity to Org1 Peer');
+ // build an in memory object with the network configuration (also known as a connection profile)
+ const ccpOrg1 = buildCCPOrg1();
+
+ // build an instance of the fabric ca services client based on
+ // the information in the network configuration
+ const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
+
+ // setup the wallet to cache the credentials of the application user, on the app server locally
+ const walletPathOrg1 = path.join(__dirname, 'wallet', 'org1');
+ const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
+
+ // in a real application this would be done on an administrative flow, and only once
+ // stores admin identity in local wallet, if needed
+ await enrollAdmin(caOrg1Client, walletOrg1, org1);
+ // register & enroll application user with CA, which is used as client identify to make chaincode calls
+ // and stores app user identity in local wallet
+ // In a real application this would be done only when a new user was required to be added
+ // and would be part of an administrative flow
+ await registerAndEnrollUser(caOrg1Client, walletOrg1, org1, Org1UserId, 'org1.department1');
+
+ try {
+ // Create a new gateway for connecting to Org's peer node.
+ const gatewayOrg1 = new Gateway();
+ //connect using Discovery enabled
+ await gatewayOrg1.connect(ccpOrg1,
+ { wallet: walletOrg1, identity: Org1UserId, discovery: { enabled: true, asLocalhost: true } });
+
+ return gatewayOrg1;
+ } catch (error) {
+ console.error(`Error in connecting to gateway for Org1: ${error}`);
+ process.exit(1);
+ }
+}
+
+async function initGatewayForOrg2() {
+ console.log('\n--> Fabric client user & Gateway init: Using Org2 identity to Org2 Peer');
+ const ccpOrg2 = buildCCPOrg2();
+ const caOrg2Client = buildCAClient(FabricCAServices, ccpOrg2, 'ca.org2.example.com');
+
+ const walletPathOrg2 = path.join(__dirname, 'wallet', 'org2');
+ const walletOrg2 = await buildWallet(Wallets, walletPathOrg2);
+
+ await enrollAdmin(caOrg2Client, walletOrg2, org2);
+ await registerAndEnrollUser(caOrg2Client, walletOrg2, org2, Org2UserId, 'org2.department1');
+
+ try {
+ // Create a new gateway for connecting to Org's peer node.
+ const gatewayOrg2 = new Gateway();
+ await gatewayOrg2.connect(ccpOrg2,
+ { wallet: walletOrg2, identity: Org2UserId, discovery: { enabled: true, asLocalhost: true } });
+
+ return gatewayOrg2;
+ } catch (error) {
+ console.error(`Error in connecting to gateway for Org2: ${error}`);
+ process.exit(1);
+ }
+}
+
+function checkAsset(org, assetKey, resultBuffer, value, ownerOrg) {
+ let asset;
+ if (resultBuffer) {
+ asset = JSON.parse(resultBuffer.toString('utf8'));
+ }
+
+ if (asset && value) {
+ if (asset.Value === value && asset.OwnerOrg === ownerOrg) {
+ console.log(`*** Result from ${org} - asset ${asset.ID} has value of ${asset.Value} and owned by ${asset.OwnerOrg}`);
+ } else {
+ console.log(`*** Failed from ${org} - asset ${asset.ID} has value of ${asset.Value} and owned by ${asset.OwnerOrg}`);
+ }
+ } else if (!asset && value === 0 ) {
+ console.log(`*** Success from ${org} - asset ${assetKey} does not exist`);
+ } else {
+ console.log('*** Failed - asset read failed');
+ }
+}
+
+async function readAssetByBothOrgs(assetKey, value, ownerOrg, contractOrg1, contractOrg2) {
+ if (value) {
+ console.log(`\n--> Evaluate Transaction: ReadAsset, - ${assetKey} should have a value of ${value} and owned by ${ownerOrg}`);
+ } else {
+ console.log(`\n--> Evaluate Transaction: ReadAsset, - ${assetKey} should not exist`);
+ }
+ let resultBuffer;
+ resultBuffer = await contractOrg1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset('Org1', assetKey, resultBuffer, value, ownerOrg);
+ resultBuffer = await contractOrg2.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset('Org2', assetKey, resultBuffer, value, ownerOrg);
+}
+
+// This application uses fabric-samples/test-network based setup and the companion chaincode
+// For this illustration, both Org1 & Org2 client identities will be used, however
+// notice they are used by two different "gateway"s to simulate two different running
+// applications from two different organizations.
+async function main() {
+ try {
+ // use a random key so that we can run multiple times
+ const assetKey = `asset-${Math.floor(Math.random() * 100) + 1}`;
+
+ /** ******* Fabric client init: Using Org1 identity to Org1 Peer ******* */
+ const gatewayOrg1 = await initGatewayForOrg1();
+ const networkOrg1 = await gatewayOrg1.getNetwork(channelName);
+ const contractOrg1 = networkOrg1.getContract(chaincodeName);
+
+ /** ******* Fabric client init: Using Org2 identity to Org2 Peer ******* */
+ const gatewayOrg2 = await initGatewayForOrg2();
+ const networkOrg2 = await gatewayOrg2.getNetwork(channelName);
+ const contractOrg2 = networkOrg2.getContract(chaincodeName);
+
+ try {
+ let transaction;
+
+ try {
+ // Create an asset by organization Org1, this will require that both organization endorse.
+ // The endorsement will be handled by Discovery, since the gateway was connected with discovery enabled.
+ console.log(`\n--> Submit Transaction: CreateAsset, ${assetKey} as Org1 - endorsed by Org1 and Org2`);
+ await contractOrg1.submitTransaction('CreateAsset', assetKey, '100', 'Tom');
+ console.log('*** Result: committed, now asset will only require Org1 to endorse');
+ } catch (createError) {
+ console.log(`*** Failed: create - ${createError}`);
+ }
+
+ await readAssetByBothOrgs(assetKey, 100, org1, contractOrg1, contractOrg2);
+
+ try {
+ // Since the gateway is using discovery we should limit the organizations used by
+ // discovery to endorse. This way we only have to know the organization and not
+ // the actual peers that may be active at any given time.
+ console.log(`\n--> Submit Transaction: UpdateAsset ${assetKey}, as Org1 - endorse by Org1`);
+ transaction = contractOrg1.createTransaction('UpdateAsset');
+ transaction.setEndorsingOrganizations(org1);
+ await transaction.submit(assetKey, '200');
+ console.log('*** Result: committed');
+ } catch (updateError) {
+ console.log(`*** Failed: update - ${updateError}`);
+ }
+
+ await readAssetByBothOrgs(assetKey, 200, org1, contractOrg1, contractOrg2);
+
+ try {
+ // Submit a transaction to make an update to the asset that has a key-level endorsement policy
+ // set to only allow Org1 to make updates. The following example will not use the "setEndorsingOrganizations"
+ // to limit the organizations that will do the endorsement, this means that it will be sent to all
+ // organizations in the chaincode endorsement policy. When Org1 endorses, the transaction will be committed
+ // if Org2 endorses or not.
+ console.log(`\n--> Submit Transaction: UpdateAsset ${assetKey}, as Org1 - endorse by Org1 and Org2`);
+ transaction = contractOrg1.createTransaction('UpdateAsset');
+ await transaction.submit(assetKey, '300');
+ console.log('*** Result: committed - because Org1 and Org2 both endorsed, while only the Org1 endorsement was required and checked');
+ } catch (updateError) {
+ console.log(`*** Failed: update - ${updateError}`);
+ }
+
+ await readAssetByBothOrgs(assetKey, 300, org1, contractOrg1, contractOrg2);
+
+ try {
+ // Again submit the change to both Organizations by not using "setEndorsingOrganizations". Since only
+ // Org1 is required to approve, the transaction will be committed.
+ console.log(`\n--> Submit Transaction: UpdateAsset ${assetKey}, as Org2 - endorse by Org1 and Org2`);
+ transaction = contractOrg2.createTransaction('UpdateAsset');
+ await transaction.submit(assetKey, '400');
+ console.log('*** Result: committed - because Org1 was on the discovery list, Org2 did not endorse');
+ } catch (updateError) {
+ console.log(`*** Failed: update - ${updateError}`);
+ }
+
+ await readAssetByBothOrgs(assetKey, 400, org1, contractOrg1, contractOrg2);
+
+ try {
+ // Try to update by sending only to Org2, since the state-based-endorsement says that
+ // Org1 is the only organization allowed to update, the transaction will fail.
+ console.log(`\n--> Submit Transaction: UpdateAsset ${assetKey}, as Org2 - endorse by Org2`);
+ transaction = contractOrg2.createTransaction('UpdateAsset');
+ transaction.setEndorsingOrganizations(org2);
+ await transaction.submit(assetKey, '500');
+ console.log('*** Failed: committed - this should have failed to endorse and commit');
+ } catch (updateError) {
+ console.log(`*** Successfully caught the error: \n ${updateError}`);
+ }
+
+ await readAssetByBothOrgs(assetKey, 400, org1, contractOrg1, contractOrg2);
+
+ try {
+ // Make a change to the state-based-endorsement policy making Org2 the owner.
+ console.log(`\n--> Submit Transaction: TransferAsset ${assetKey}, as Org1 - endorse by Org1`);
+ transaction = contractOrg1.createTransaction('TransferAsset');
+ transaction.setEndorsingOrganizations(org1);
+ await transaction.submit(assetKey, 'Henry', org2);
+ console.log('*** Result: committed');
+ } catch (transferError) {
+ console.log(`*** Failed: transfer - ${transferError}`);
+ }
+
+ await readAssetByBothOrgs(assetKey, 400, org2, contractOrg1, contractOrg2);
+
+ try {
+ // Make sure that Org2 can now make updates, notice how the transaction has limited the
+ // endorsement to only Org2.
+ console.log(`\n--> Submit Transaction: UpdateAsset ${assetKey}, as Org2 - endorse by Org2`);
+ transaction = contractOrg2.createTransaction('UpdateAsset');
+ transaction.setEndorsingOrganizations(org2);
+ await transaction.submit(assetKey, '600');
+ console.log('*** Result: committed');
+ } catch (updateError) {
+ console.log(`*** Failed: update - ${updateError}`);
+ }
+
+ await readAssetByBothOrgs(assetKey, 600, org2, contractOrg1, contractOrg2);
+
+ try {
+ // With Org2 now the owner and the state-based-endorsement policy only allowing organization Org2
+ // to make updates, a transaction only to Org1 will fail.
+ console.log(`\n--> Submit Transaction: UpdateAsset ${assetKey}, as Org1 - endorse by Org1`);
+ transaction = contractOrg1.createTransaction('UpdateAsset');
+ transaction.setEndorsingOrganizations(org1);
+ await transaction.submit(assetKey, '700');
+ console.log('*** Failed: committed - this should have failed to endorse and commit');
+ } catch (updateError) {
+ console.log(`*** Successfully caught the error: \n ${updateError}`);
+ }
+
+ await readAssetByBothOrgs(assetKey, 600, org2, contractOrg1, contractOrg2);
+
+ try {
+ // With Org2 the owner and the state-based-endorsement policy only allowing organization Org2
+ // to make updates, a transaction to delete by Org1 will fail.
+ console.log(`\n--> Submit Transaction: DeleteAsset ${assetKey}, as Org1 - endorse by Org1`);
+ transaction = contractOrg1.createTransaction('DeleteAsset');
+ transaction.setEndorsingOrganizations(org1);
+ await transaction.submit(assetKey);
+ console.log('*** Failed: committed - this should have failed to endorse and commit');
+ } catch (updateError) {
+ console.log(`*** Successfully caught the error: \n ${updateError}`);
+ }
+
+ try {
+ // With Org2 the owner and the state-based-endorsement policy only allowing organization Org2
+ // to make updates, a transaction to delete by Org2 will succeed.
+ console.log(`\n--> Submit Transaction: DeleteAsset ${assetKey}, as Org2 - endorse by Org2`);
+ transaction = contractOrg2.createTransaction('DeleteAsset');
+ transaction.setEndorsingOrganizations(org2);
+ await transaction.submit(assetKey);
+ console.log('*** Result: committed');
+ } catch (deleteError) {
+ console.log(`*** Failed: delete - ${deleteError}`);
+ }
+
+ // The asset should now be deleted, both orgs should not be able to read it
+ try {
+ await readAssetByBothOrgs(assetKey, 0, org2, contractOrg1, contractOrg2);
+ } catch (readDeleteError) {
+ console.log(`*** Successfully caught the error: ${readDeleteError}`);
+ }
+
+ } catch (runError) {
+ console.error(`Error in transaction: ${runError}`);
+ if (runError.stack) {
+ console.error(runError.stack);
+ }
+ process.exit(1);
+ } finally {
+ // Disconnect from the gateway peer when all work for this client identity is complete
+ gatewayOrg1.disconnect();
+ gatewayOrg2.disconnect();
+ }
+ } catch (error) {
+ console.error(`Error in setup: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/asset-transfer-sbe/application-javascript/package.json b/asset-transfer-sbe/application-javascript/package.json
new file mode 100644
index 0000000..aa8cefb
--- /dev/null
+++ b/asset-transfer-sbe/application-javascript/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "asset-transfer-sbe",
+ "version": "1.0.0",
+ "description": "Asset transfer state based endorsement application implemented in JavaScript",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.4",
+ "fabric-network": "^2.2.4"
+ }
+}
diff --git a/asset-transfer-sbe/chaincode-java/.gitattributes b/asset-transfer-sbe/chaincode-java/.gitattributes
new file mode 100644
index 0000000..00a51af
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat text eol=crlf
+
diff --git a/asset-transfer-sbe/chaincode-java/.gitignore b/asset-transfer-sbe/chaincode-java/.gitignore
new file mode 100644
index 0000000..ae1478c
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/.gitignore
@@ -0,0 +1,10 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+/.classpath
+/.gradle/
+/.project
+/.settings/
+/bin/
+/build/
diff --git a/asset-transfer-sbe/chaincode-java/build.gradle b/asset-transfer-sbe/chaincode-java/build.gradle
new file mode 100644
index 0000000..5f90c5a
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/build.gradle
@@ -0,0 +1,75 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+plugins {
+ id 'application'
+ id 'checkstyle'
+ id 'jacoco'
+}
+
+group 'org.hyperledger.fabric.samples'
+version '1.0-SNAPSHOT'
+
+dependencies {
+
+ compileOnly 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+ implementation 'com.owlike:genson:1.5'
+ testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
+ testImplementation 'org.assertj:assertj-core:3.11.1'
+ testImplementation 'org.mockito:mockito-core:2.+'
+}
+
+repositories {
+ maven {
+ url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
+ }
+ jcenter()
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+application {
+ mainClass = 'org.hyperledger.fabric.contract.ContractRouter'
+}
+
+checkstyle {
+ toolVersion '8.21'
+ configFile file("config/checkstyle/checkstyle.xml")
+}
+
+checkstyleMain {
+ source ='src/main/java'
+}
+
+checkstyleTest {
+ source ='src/test/java'
+}
+
+jacocoTestReport {
+ dependsOn test
+}
+
+jacocoTestCoverageVerification {
+ violationRules {
+ rule {
+ limit {
+ minimum = 0.9
+ }
+ }
+ }
+
+ finalizedBy jacocoTestReport
+}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
+
+check.dependsOn jacocoTestCoverageVerification
+installDist.dependsOn check
diff --git a/asset-transfer-sbe/chaincode-java/config/checkstyle/checkstyle.xml b/asset-transfer-sbe/chaincode-java/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..797da97
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/config/checkstyle/checkstyle.xml
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/asset-transfer-sbe/chaincode-java/config/checkstyle/suppressions.xml b/asset-transfer-sbe/chaincode-java/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000..8c44b0a
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/config/checkstyle/suppressions.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/asset-transfer-sbe/chaincode-java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-sbe/chaincode-java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
Binary files /dev/null and b/asset-transfer-sbe/chaincode-java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/asset-transfer-sbe/chaincode-java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-sbe/chaincode-java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bb8b2fc
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/asset-transfer-sbe/chaincode-java/gradlew b/asset-transfer-sbe/chaincode-java/gradlew
new file mode 100755
index 0000000..83f2acf
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/asset-transfer-sbe/chaincode-java/gradlew.bat b/asset-transfer-sbe/chaincode-java/gradlew.bat
new file mode 100644
index 0000000..9618d8d
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/asset-transfer-sbe/chaincode-java/settings.gradle b/asset-transfer-sbe/chaincode-java/settings.gradle
new file mode 100644
index 0000000..e435c09
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/settings.gradle
@@ -0,0 +1,5 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+rootProject.name = 'sbe'
diff --git a/asset-transfer-sbe/chaincode-java/src/main/java/org/hyperledger/fabric/samples/sbe/Asset.java b/asset-transfer-sbe/chaincode-java/src/main/java/org/hyperledger/fabric/samples/sbe/Asset.java
new file mode 100644
index 0000000..b49d692
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/src/main/java/org/hyperledger/fabric/samples/sbe/Asset.java
@@ -0,0 +1,97 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.sbe;
+
+import java.util.Objects;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+
+import com.owlike.genson.annotation.JsonProperty;
+
+@DataType()
+public final class Asset {
+
+ @Property()
+ private final String ID;
+
+ @Property()
+ private int Value;
+
+ @Property()
+ private String Owner;
+
+ @Property()
+ private String OwnerOrg;
+
+ @JsonProperty("ID")
+ public String getID() {
+ return ID;
+ }
+
+ @JsonProperty("Value")
+ public int getValue() {
+ return Value;
+ }
+
+ public void setValue(final int Value) {
+ this.Value = Value;
+ }
+
+ @JsonProperty("Owner")
+ public String getOwner() {
+ return Owner;
+ }
+
+ public void setOwner(final String Owner) {
+ this.Owner = Owner;
+ }
+
+ @JsonProperty("OwnerOrg")
+ public String getOwnerOrg() {
+ return OwnerOrg;
+ }
+
+ public void setOwnerOrg(final String OwnerOrg) {
+ this.OwnerOrg = OwnerOrg;
+ }
+
+ public Asset(@JsonProperty("ID") final String ID, @JsonProperty("Value") final int Value,
+ @JsonProperty("Owner") final String Owner, @JsonProperty("OwnerOrg") final String OwnerOrg) {
+ this.ID = ID;
+ this.Value = Value;
+ this.Owner = Owner;
+ this.OwnerOrg = OwnerOrg;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Asset asset = (Asset) o;
+ return getValue() == asset.getValue()
+ &&
+ getID().equals(asset.getID())
+ &&
+ getOwner().equals(asset.getOwner())
+ &&
+ getOwnerOrg().equals(asset.getOwnerOrg());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getID(), getValue(), getOwner(), getOwnerOrg());
+ }
+
+ @Override
+ public String toString() {
+ return "Asset{" + "ID='" + ID + '\'' + ", Value=" + Value + ", Owner='"
+ + Owner + '\'' + ", OwnerOrg='" + OwnerOrg + '\'' + '}';
+ }
+}
diff --git a/asset-transfer-sbe/chaincode-java/src/main/java/org/hyperledger/fabric/samples/sbe/AssetContract.java b/asset-transfer-sbe/chaincode-java/src/main/java/org/hyperledger/fabric/samples/sbe/AssetContract.java
new file mode 100644
index 0000000..9843ee3
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-java/src/main/java/org/hyperledger/fabric/samples/sbe/AssetContract.java
@@ -0,0 +1,255 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.sbe;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.contract.ContractInterface;
+import org.hyperledger.fabric.contract.annotation.Contract;
+import org.hyperledger.fabric.contract.annotation.Default;
+import org.hyperledger.fabric.contract.annotation.Info;
+import org.hyperledger.fabric.contract.annotation.License;
+import org.hyperledger.fabric.contract.annotation.Transaction;
+import org.hyperledger.fabric.protos.common.MspPrincipal;
+import org.hyperledger.fabric.protos.common.Policies;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement;
+import org.hyperledger.fabric.shim.ext.sbe.impl.StateBasedEndorsementFactory;
+
+import com.owlike.genson.Genson;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+@Contract(
+ name = "sbe",
+ info = @Info(
+ title = "Asset Contract",
+ description = "Asset Transfer Smart Contract, using State Based Endorsement(SBE), implemented in Java",
+ version = "0.0.1-SNAPSHOT",
+ license = @License(
+ name = "Apache 2.0 License",
+ url = "http://www.apache.org/licenses/LICENSE-2.0.html")))
+@Default
+public final class AssetContract implements ContractInterface {
+
+ private final Genson genson = new Genson();
+
+ private enum AssetTransferErrors {
+ ASSET_NOT_FOUND,
+ ASSET_ALREADY_EXISTS
+ }
+
+ /**
+ * Creates a new asset.
+ * Sets the endorsement policy of the assetId Key, such that current owner Org Peer is required to endorse future updates.
+ * Optionally, set the endorsement policy of the assetId Key, such that any 1(N) out of the Org's specified can endorse future updates.
+ *
+ * @param ctx the transaction context
+ * @param assetId the id of the new asset
+ * @param value the value of the new asset
+ * @param owner the owner of the new asset
+ * @return the created asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset CreateAsset(final Context ctx, final String assetId, final int value, final String owner) {
+ ChaincodeStub stub = ctx.getStub();
+
+ if (AssetExists(ctx, assetId)) {
+ String errorMessage = String.format("Asset %s already exists", assetId);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_ALREADY_EXISTS.toString());
+ }
+
+ final String ownerOrg = getClientOrgId(ctx);
+ Asset asset = new Asset(assetId, value, owner, ownerOrg);
+ String assetJSON = genson.serialize(asset);
+ stub.putStringState(assetId, assetJSON);
+
+ // Set the endorsement policy of the assetId Key, such that current owner Org is required to endorse future updates
+ setStateBasedEndorsement(ctx, assetId, new String[]{ownerOrg});
+
+ // Optionally, set the endorsement policy of the assetId Key, such that any 1 Org (N) out of the specified Orgs can endorse future updates
+ // setStateBasedEndorsementNOutOf(ctx, assetId, 1, new String[]{"Org1MSP", "Org2MSP"});
+
+ return asset;
+ }
+
+ /**
+ * Retrieves an asset with the given assetId.
+ *
+ * @param ctx the transaction context
+ * @param assetId the id of the asset
+ * @return the asset found on the ledger if there was one
+ */
+ @Transaction(intent = Transaction.TYPE.EVALUATE)
+ public String ReadAsset(final Context ctx, final String assetId) {
+ ChaincodeStub stub = ctx.getStub();
+ String assetJSON = stub.getStringState(assetId);
+
+ if (assetJSON == null || assetJSON.isEmpty()) {
+ String errorMessage = String.format("Asset %s does not exist", assetId);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+
+ return assetJSON;
+ }
+
+ /**
+ * Updates the properties of an existing asset.
+ * Needs an endorsement of current owner Org Peer.
+ *
+ * @param ctx the transaction context
+ * @param assetId the id of the asset being updated
+ * @param newValue the value of the asset being updated
+ * @return the updated asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset UpdateAsset(final Context ctx, final String assetId, final int newValue) {
+ ChaincodeStub stub = ctx.getStub();
+
+ String assetString = ReadAsset(ctx, assetId);
+ Asset asset = genson.deserialize(assetString, Asset.class);
+ asset.setValue(newValue);
+ String updatedAssetJSON = genson.serialize(asset);
+ stub.putStringState(assetId, updatedAssetJSON);
+
+ return asset;
+ }
+
+ /**
+ * Deletes the given asset.
+ * Needs an endorsement of current owner Org Peer.
+ *
+ * @param ctx the transaction context
+ * @param assetId the id of the asset being deleted
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public void DeleteAsset(final Context ctx, final String assetId) {
+ ChaincodeStub stub = ctx.getStub();
+
+ if (!AssetExists(ctx, assetId)) {
+ String errorMessage = String.format("Asset %s does not exist", assetId);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
+ }
+
+ stub.delState(assetId);
+ }
+
+ /**
+ * Updates the owner & ownerOrg field of asset with given assetId, ownerOrg must be a valid Org MSP Id.
+ * Needs an endorsement of current owner Org Peer.
+ * Re-sets the endorsement policy of the assetId Key, such that new owner Org Peer is required to endorse future updates.
+ *
+ * @param ctx the transaction context
+ * @param assetId the id of the asset being transferred
+ * @param newOwner the new owner
+ * @param newOwnerOrg the new owner Org MSPID
+ * @return the updated asset
+ */
+ @Transaction(intent = Transaction.TYPE.SUBMIT)
+ public Asset TransferAsset(final Context ctx, final String assetId, final String newOwner, final String newOwnerOrg) {
+ ChaincodeStub stub = ctx.getStub();
+
+ String assetString = ReadAsset(ctx, assetId);
+ Asset asset = genson.deserialize(assetString, Asset.class);
+ asset.setOwner(newOwner);
+ asset.setOwnerOrg(newOwnerOrg);
+ String updatedAssetJSON = genson.serialize(asset);
+ stub.putStringState(assetId, updatedAssetJSON);
+
+ // Re-Set the endorsement policy of the assetId Key, such that a new owner Org Peer is required to endorse future updates
+ setStateBasedEndorsement(ctx, assetId, new String[]{newOwnerOrg});
+
+ // Optionally, set the endorsement policy of the assetId Key, such that any 1 Org (N) out of the specified Orgs can endorse future updates
+ // setStateBasedEndorsementNOutOf(ctx, assetId, 1, new String[]{"Org1MSP", "Org2MSP"});
+
+ return asset;
+ }
+
+ /**
+ * Checks the existence of the asset.
+ *
+ * @param ctx the transaction context
+ * @param assetId the id of the asset
+ * @return boolean indicating the existence of the asset
+ */
+ private boolean AssetExists(final Context ctx, final String assetId) {
+ ChaincodeStub stub = ctx.getStub();
+ String assetJSON = stub.getStringState(assetId);
+
+ return (assetJSON != null && !assetJSON.isEmpty());
+ }
+
+ /**
+ * Retrieves the client's OrgId (MSPID)
+ *
+ * @param ctx the transaction context
+ * @return String value of the Org MSPID
+ */
+ private static String getClientOrgId(final Context ctx) {
+ return ctx.getClientIdentity().getMSPID();
+ }
+
+ /**
+ * Sets an endorsement policy to the assetId Key.
+ * Enforces that the owner Org must endorse future update transactions for the specified assetId Key.
+ *
+ * @param ctx the transaction context
+ * @param assetId the id of the asset
+ * @param ownerOrgs the list of Owner Org MSPID's
+ */
+ private static void setStateBasedEndorsement(final Context ctx, final String assetId, final String[] ownerOrgs) {
+ StateBasedEndorsement stateBasedEndorsement = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(null);
+ stateBasedEndorsement.addOrgs(StateBasedEndorsement.RoleType.RoleTypeMember, ownerOrgs);
+ ctx.getStub().setStateValidationParameter(assetId, stateBasedEndorsement.policy());
+ }
+
+ /**
+ * Sets an endorsement policy to the assetId Key.
+ * Enforces that a given number of Orgs (N) out of the specified Orgs must endorse future update transactions for the specified assetId Key.
+ *
+ * @param ctx the transaction context
+ * @param assetId the id of the asset
+ * @param nOrgs the number of N Orgs to endorse out of the list of Orgs provided
+ * @param ownerOrgs the list of Owner Org MSPID's
+ */
+ private static void setStateBasedEndorsementNOutOf(final Context ctx, final String assetId, final int nOrgs, final String[] ownerOrgs) {
+ ctx.getStub().setStateValidationParameter(assetId, policy(nOrgs, Arrays.asList(ownerOrgs)));
+ }
+
+ /**
+ * Create a policy that requires a given number (N) of Org principals signatures out of the provided list of Orgs
+ *
+ * @param nOrgs the number of Org principals signatures required to endorse (out of the provided list of Orgs)
+ * @param mspids the list of Owner Org MSPID's
+ */
+ private static byte[] policy(final int nOrgs, final List mspids) {
+ mspids.sort(Comparator.naturalOrder());
+ final List principals = new ArrayList<>();
+ final List signPolicy = new ArrayList<>();
+ for (int i = 0; i < mspids.size(); i++) {
+ final String mspid = mspids.get(i);
+ principals.add(MspPrincipal.MSPPrincipal.newBuilder().setPrincipalClassification(MspPrincipal.MSPPrincipal.Classification.ROLE)
+ .setPrincipal(MspPrincipal.MSPRole.newBuilder().setMspIdentifier(mspid).setRole(MspPrincipal.MSPRole.MSPRoleType.MEMBER).build().toByteString()).build());
+ signPolicy.add(signedBy(i));
+ }
+ // Create the policy such that it requires any N signature's from all of the principals provided
+ return Policies.SignaturePolicyEnvelope.newBuilder().setVersion(0).setRule(nOutOf(nOrgs, signPolicy))
+ .addAllIdentities(principals).build().toByteArray();
+ }
+
+ private static Policies.SignaturePolicy signedBy(final int index) {
+ return Policies.SignaturePolicy.newBuilder().setSignedBy(index).build();
+ }
+
+ private static Policies.SignaturePolicy nOutOf(final int n, final List policies) {
+ return Policies.SignaturePolicy.newBuilder().setNOutOf(Policies.SignaturePolicy.NOutOf.newBuilder().setN(n).addAllRules(policies).build()).build();
+ }
+}
diff --git a/asset-transfer-sbe/chaincode-typescript/.gitignore b/asset-transfer-sbe/chaincode-typescript/.gitignore
new file mode 100644
index 0000000..2a76b3d
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-typescript/.gitignore
@@ -0,0 +1,20 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+package-lock.json
+
+# Compiled TypeScript files
+dist
+
+# Editor Config
+.editorconfig
+
+# npm ignore
+.npmignore
diff --git a/asset-transfer-sbe/chaincode-typescript/package.json b/asset-transfer-sbe/chaincode-typescript/package.json
new file mode 100644
index 0000000..43be5e7
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-typescript/package.json
@@ -0,0 +1,65 @@
+{
+ "name": "asset-transfer-sbe",
+ "version": "0.0.1",
+ "description": "Asset Transfer contract, using State Based Endorsement(SBE), implemented in TypeScript",
+ "main": "dist/index.js",
+ "typings": "dist/index.d.ts",
+ "engines": {
+ "node": ">=8",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "tslint -c tslint.json 'src/**/*.ts'",
+ "pretest": "npm run lint",
+ "test": "nyc mocha -r ts-node/register src/**/*.spec.ts",
+ "start": "fabric-chaincode-node start",
+ "build": "tsc",
+ "build:watch": "tsc -w",
+ "prepublishOnly": "npm run build"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ },
+ "devDependencies": {
+ "@types/chai": "^4.2.11",
+ "@types/chai-as-promised": "^7.1.2",
+ "@types/mocha": "^7.0.2",
+ "@types/node": "^13.9.3",
+ "@types/sinon": "^7.5.2",
+ "@types/sinon-chai": "^3.2.3",
+ "chai": "^4.2.0",
+ "chai-as-promised": "^7.1.1",
+ "mocha": "^7.1.1",
+ "nyc": "^15.0.0",
+ "sinon": "^9.0.1",
+ "sinon-chai": "^3.5.0",
+ "ts-node": "^8.8.1",
+ "tslint": "^6.1.0",
+ "typescript": "^3.8.3",
+ "winston": "^3.2.1"
+ },
+ "nyc": {
+ "extension": [
+ ".ts",
+ ".tsx"
+ ],
+ "exclude": [
+ "coverage/**",
+ "dist/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/asset-transfer-sbe/chaincode-typescript/src/asset.ts b/asset-transfer-sbe/chaincode-typescript/src/asset.ts
new file mode 100644
index 0000000..58bd07a
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-typescript/src/asset.ts
@@ -0,0 +1,20 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Object, Property } from 'fabric-contract-api';
+
+@Object()
+export class Asset {
+ @Property()
+ public ID: string;
+
+ @Property()
+ public Value: number;
+
+ @Property()
+ public Owner: string;
+
+ @Property()
+ public OwnerOrg: string;
+}
diff --git a/asset-transfer-sbe/chaincode-typescript/src/assetContract.ts b/asset-transfer-sbe/chaincode-typescript/src/assetContract.ts
new file mode 100644
index 0000000..493f85c
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-typescript/src/assetContract.ts
@@ -0,0 +1,152 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Context, Contract, Info, Transaction } from 'fabric-contract-api';
+import { Asset } from './asset';
+import { KeyEndorsementPolicy } from 'fabric-shim';
+import * as fabprotos from 'fabric-shim/bundle';
+
+@Info({title: 'AssetContract', description: 'Asset Transfer Smart Contract, using State Based Endorsement(SBE), implemented in TypeScript' })
+export class AssetContract extends Contract {
+
+ // CreateAsset creates a new asset
+ // CreateAsset sets the endorsement policy of the assetId Key, such that current owner Org Peer is required to endorse future updates
+ @Transaction()
+ public async CreateAsset(ctx: Context, assetId: string, value: number, owner: string): Promise {
+ const exists = await this.AssetExists(ctx, assetId);
+ if (exists) {
+ throw new Error(`The asset ${assetId} already exists`);
+ }
+ const ownerOrg = AssetContract.getClientOrgId(ctx);
+ const asset = new Asset();
+ asset.ID = assetId;
+ asset.Value = value;
+ asset.Owner = owner;
+ asset.OwnerOrg = ownerOrg;
+ const buffer = Buffer.from(JSON.stringify(asset));
+ // Create the asset
+ await ctx.stub.putState(assetId, buffer);
+
+ // Set the endorsement policy of the assetId Key, such that current owner Org is required to endorse future updates
+ await AssetContract.setStateBasedEndorsement(ctx, assetId, [ownerOrg]);
+
+ // Optionally, set the endorsement policy of the assetId Key, such that any 1 Org (N) out of the specified Orgs can endorse future updates
+ // await AssetContract.setStateBasedEndorsementNOutOf(ctx, assetId, 1, ["Org1MSP", "Org2MSP"]);
+ }
+
+ // ReadAsset returns asset with given assetId
+ @Transaction(false)
+ public async ReadAsset(ctx: Context, assetId: string): Promise {
+ const exists = await this.AssetExists(ctx, assetId);
+ if (!exists) {
+ throw new Error(`The asset ${assetId} does not exist`);
+ }
+ // Read the asset
+ const assetJSON = await ctx.stub.getState(assetId);
+ return assetJSON.toString();
+ }
+
+ // UpdateAsset updates an existing asset
+ // UpdateAsset needs an endorsement of current owner Org Peer
+ @Transaction()
+ public async UpdateAsset(ctx: Context, assetId: string, newValue: number): Promise {
+ const assetString = await this.ReadAsset(ctx, assetId);
+ const asset = JSON.parse(assetString) as Asset;
+ asset.Value = newValue;
+ const buffer = Buffer.from(JSON.stringify(asset));
+ // Update the asset
+ await ctx.stub.putState(assetId, buffer);
+ }
+
+ // DeleteAsset deletes an given asset
+ // DeleteAsset needs an endorsement of current owner Org Peer
+ @Transaction()
+ public async DeleteAsset(ctx: Context, assetId: string): Promise {
+ const exists = await this.AssetExists(ctx, assetId);
+ if (!exists) {
+ throw new Error(`The asset ${assetId} does not exist`);
+ }
+ // Delete the asset
+ await ctx.stub.deleteState(assetId);
+ }
+
+ // TransferAsset updates the Owner & OwnerOrg field of asset with given assetId, OwnerOrg must be a valid Org MSP Id
+ // TransferAsset needs an endorsement of current owner Org Peer
+ // TransferAsset re-sets the endorsement policy of the assetId Key, such that new owner Org Peer is required to endorse future updates
+ @Transaction()
+ public async TransferAsset(ctx: Context, assetId: string, newOwner: string, newOwnerOrg: string): Promise {
+ const assetString = await this.ReadAsset(ctx, assetId);
+ const asset = JSON.parse(assetString) as Asset;
+ asset.Owner = newOwner;
+ asset.OwnerOrg = newOwnerOrg;
+ // Update the asset
+ await ctx.stub.putState(assetId, Buffer.from(JSON.stringify(asset)));
+ // Re-Set the endorsement policy of the assetId Key, such that a new owner Org Peer is required to endorse future updates
+ await AssetContract.setStateBasedEndorsement(ctx, asset.ID, [newOwnerOrg]);
+
+ // Optionally, set the endorsement policy of the assetId Key, such that any 1 Org (N) out of the specified Orgs can endorse future updates
+ // await AssetContract.setStateBasedEndorsementNOutOf(ctx, assetId, 1, ["Org1MSP", "Org2MSP"]);
+ }
+
+ // AssetExists returns true when asset with given ID exists
+ public async AssetExists(ctx: Context, assetId: string): Promise {
+ const buffer = await ctx.stub.getState(assetId);
+ return (!!buffer && buffer.length > 0);
+ }
+
+ // getClientOrgId gets the client's OrgId (MSPID)
+ private static getClientOrgId(ctx: Context): string {
+ return ctx.clientIdentity.getMSPID();
+ }
+
+ // setStateBasedEndorsement sets an endorsement policy to the assetId Key
+ // setStateBasedEndorsement enforces that the owner Org must endorse future update transactions for the specified assetId Key
+ private static async setStateBasedEndorsement(ctx: Context, assetId: string, ownerOrgs: string[]): Promise {
+ const ep = new KeyEndorsementPolicy();
+ ep.addOrgs('MEMBER', ...ownerOrgs);
+ await ctx.stub.setStateValidationParameter(assetId, ep.getPolicy());
+ }
+
+ // setStateBasedEndorsementNOutOf sets an endorsement policy to the assetId Key
+ // setStateBasedEndorsementNOutOf enforces that a given number of Orgs (N) out of the specified Orgs must endorse future update transactions for the specified assetId Key.
+ private static async setStateBasedEndorsementNOutOf(ctx: Context, assetId: string, nOrgs:number, ownerOrgs: string[]): Promise {
+ await ctx.stub.setStateValidationParameter(assetId, AssetContract.policy(nOrgs, ownerOrgs));
+ }
+
+ // Create a policy that requires a given number (N) of Org principals signatures out of the provided list of Orgs
+ private static policy(nOrgs: number, mspIds: string[]): Uint8Array {
+ const principals = [];
+ const sigsPolicies = [];
+ mspIds.forEach((mspId, i) => {
+ const mspRole = {
+ role: fabprotos.common.MSPRole.MSPRoleType.MEMBER,
+ mspIdentifier: mspId
+ };
+ const principal = {
+ principalClassification: fabprotos.common.MSPPrincipal.Classification.ROLE,
+ principal: fabprotos.common.MSPRole.encode(mspRole).finish()
+ };
+ principals.push(principal);
+ const signedBy = {
+ signedBy: i,
+ };
+ sigsPolicies.push(signedBy);
+ });
+
+ // create the policy such that it requires any N signature's from all of the principals provided
+ const allOf = {
+ n: nOrgs,
+ rules: sigsPolicies
+ };
+ const noutof = {
+ nOutOf: allOf
+ };
+ const spe = {
+ version: 0,
+ rule: noutof,
+ identities: principals
+ };
+ return fabprotos.common.SignaturePolicyEnvelope.encode(spe).finish();
+ }
+}
diff --git a/asset-transfer-sbe/chaincode-typescript/src/index.ts b/asset-transfer-sbe/chaincode-typescript/src/index.ts
new file mode 100644
index 0000000..41a40ec
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-typescript/src/index.ts
@@ -0,0 +1,8 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { AssetContract } from './assetContract';
+export { AssetContract } from './assetContract';
+
+export const contracts: any[] = [ AssetContract ];
diff --git a/asset-transfer-sbe/chaincode-typescript/tsconfig.json b/asset-transfer-sbe/chaincode-typescript/tsconfig.json
new file mode 100644
index 0000000..7201e49
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-typescript/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "outDir": "dist",
+ "target": "es2017",
+ "moduleResolution": "node",
+ "module": "commonjs",
+ "declaration": true,
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "sourceMap": true
+ },
+ "include": [
+ "./src/**/*"
+ ],
+ "exclude": [
+ "./src/**/*.spec.ts"
+ ]
+}
diff --git a/asset-transfer-sbe/chaincode-typescript/tslint.json b/asset-transfer-sbe/chaincode-typescript/tslint.json
new file mode 100644
index 0000000..2044fa6
--- /dev/null
+++ b/asset-transfer-sbe/chaincode-typescript/tslint.json
@@ -0,0 +1,21 @@
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint:recommended"
+ ],
+ "jsRules": {},
+ "rules": {
+ "indent": [true, "spaces", 4],
+ "quotemark": [true, "single"],
+ "semicolon": [true, "always"],
+ "no-console": false,
+ "curly": true,
+ "triple-equals": true,
+ "no-string-throw": true,
+ "no-var-keyword": true,
+ "no-trailing-whitespace": true,
+ "object-literal-key-quotes": [true, "as-needed"],
+ "max-line-length": false
+ },
+ "rulesDirectory": []
+}
diff --git a/asset-transfer-secured-agreement/application-javascript/.eslintignore b/asset-transfer-secured-agreement/application-javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/asset-transfer-secured-agreement/application-javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/asset-transfer-secured-agreement/application-javascript/.eslintrc.js b/asset-transfer-secured-agreement/application-javascript/.eslintrc.js
new file mode 100644
index 0000000..072edaf
--- /dev/null
+++ b/asset-transfer-secured-agreement/application-javascript/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+'use strict';
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: 'eslint:recommended',
+ rules: {
+ indent: ['error', 'tab'],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/asset-transfer-secured-agreement/application-javascript/.gitignore b/asset-transfer-secured-agreement/application-javascript/.gitignore
new file mode 100644
index 0000000..21b287f
--- /dev/null
+++ b/asset-transfer-secured-agreement/application-javascript/.gitignore
@@ -0,0 +1,14 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Dependency directories
+node_modules/
+jspm_packages/
+package-lock.json
+
+wallet
+!wallet/.gitkeep
diff --git a/asset-transfer-secured-agreement/application-javascript/app.js b/asset-transfer-secured-agreement/application-javascript/app.js
new file mode 100644
index 0000000..fca8579
--- /dev/null
+++ b/asset-transfer-secured-agreement/application-javascript/app.js
@@ -0,0 +1,567 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+/**
+ * Application that uses implicit private data collections, state-based endorsement,
+ * and organization-based ownership and access control to keep data private and securely
+ * transfer an asset with the consent of both the current owner and buyer
+ * -- How to submit a transaction
+ * -- How to query
+ * -- How to limit the organizations involved in a transaction
+ *
+ * To see the SDK workings, try setting the logging to show on the console before running
+ * export HFC_LOGGING='{"debug":"console"}'
+ */
+
+// pre-requisites:
+// - fabric-sample two organization test-network setup with two peers, ordering service,
+// and 2 certificate authorities
+// ===> from directory /fabric-samples/test-network
+// ./network.sh up createChannel -ca
+// - Use the asset-transfer-secured-agreement/chaincode-go chaincode deployed on
+// the channel "mychannel". The following deploy command will package, install,
+// approve, and commit the golang chaincode, all the actions it takes
+// to deploy a chaincode to a channel with the endorsement and private collection
+// settings.
+// ===> from directory /fabric-samples/test-network
+// ./network.sh deployCC -ccn secured -ccp ../asset-transfer-secured-agreement/chaincode-go/ -ccl go -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
+//
+// - Be sure that node.js is installed
+// ===> from directory /fabric-samples/asset-transfer-secured-agreement/application-javascript
+// node -v
+// - npm installed code dependencies
+// ===> from directory /fabric-samples/asset-transfer-secured-agreement/application-javascript
+// npm install
+// - to run this test application
+// ===> from directory /fabric-samples/asset-transfer-secured-agreement/application-javascript
+// node app.js
+
+// NOTE: If you see an error like these:
+/*
+
+ Error in setup: Error: DiscoveryService: mychannel error: access denied
+
+ OR
+
+ Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
+
+ */
+// Delete the /fabric-samples/asset-transfer-secured-agreement/application-javascript/wallet directory
+// and retry this application.
+//
+// The certificate authority must have been restarted and the saved certificates for the
+// admin and application user are not valid. Deleting the wallet store will force these to be reset
+// with the new certificate authority.
+//
+
+const { Gateway, Wallets } = require('fabric-network');
+const FabricCAServices = require('fabric-ca-client');
+const path = require('path');
+const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const channelName = 'mychannel';
+const chaincodeName = 'secured';
+
+const org1 = 'Org1MSP';
+const org2 = 'Org2MSP';
+const Org1UserId = 'appUser1';
+const Org2UserId = 'appUser2';
+
+const RED = '\x1b[31m\n';
+const GREEN = '\x1b[32m\n';
+const RESET = '\x1b[0m';
+
+async function initGatewayForOrg1() {
+ console.log(`${GREEN}--> Fabric client user & Gateway init: Using Org1 identity to Org1 Peer${RESET}`);
+ // build an in memory object with the network configuration (also known as a connection profile)
+ const ccpOrg1 = buildCCPOrg1();
+
+ // build an instance of the fabric ca services client based on
+ // the information in the network configuration
+ const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
+
+ // setup the wallet to cache the credentials of the application user, on the app server locally
+ const walletPathOrg1 = path.join(__dirname, 'wallet', 'org1');
+ const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
+
+ // in a real application this would be done on an administrative flow, and only once
+ // stores admin identity in local wallet, if needed
+ await enrollAdmin(caOrg1Client, walletOrg1, org1);
+ // register & enroll application user with CA, which is used as client identify to make chaincode calls
+ // and stores app user identity in local wallet
+ // In a real application this would be done only when a new user was required to be added
+ // and would be part of an administrative flow
+ await registerAndEnrollUser(caOrg1Client, walletOrg1, org1, Org1UserId, 'org1.department1');
+
+ try {
+ // Create a new gateway for connecting to Org's peer node.
+ const gatewayOrg1 = new Gateway();
+ //connect using Discovery enabled
+ await gatewayOrg1.connect(ccpOrg1,
+ { wallet: walletOrg1, identity: Org1UserId, discovery: { enabled: true, asLocalhost: true } });
+
+ return gatewayOrg1;
+ } catch (error) {
+ console.error(`Error in connecting to gateway for Org1: ${error}`);
+ process.exit(1);
+ }
+}
+
+async function initGatewayForOrg2() {
+ console.log(`${GREEN}--> Fabric client user & Gateway init: Using Org2 identity to Org2 Peer${RESET}`);
+ const ccpOrg2 = buildCCPOrg2();
+ const caOrg2Client = buildCAClient(FabricCAServices, ccpOrg2, 'ca.org2.example.com');
+
+ const walletPathOrg2 = path.join(__dirname, 'wallet', 'org2');
+ const walletOrg2 = await buildWallet(Wallets, walletPathOrg2);
+
+ await enrollAdmin(caOrg2Client, walletOrg2, org2);
+ await registerAndEnrollUser(caOrg2Client, walletOrg2, org2, Org2UserId, 'org2.department1');
+
+ try {
+ // Create a new gateway for connecting to Org's peer node.
+ const gatewayOrg2 = new Gateway();
+ await gatewayOrg2.connect(ccpOrg2,
+ { wallet: walletOrg2, identity: Org2UserId, discovery: { enabled: true, asLocalhost: true } });
+
+ return gatewayOrg2;
+ } catch (error) {
+ console.error(`Error in connecting to gateway for Org2: ${error}`);
+ process.exit(1);
+ }
+}
+
+async function readPrivateAsset(assetKey, org, contract) {
+ console.log(`${GREEN}--> Evaluate Transaction: GetAssetPrivateProperties, - ${assetKey} from organization ${org}${RESET}`);
+ try {
+ const resultBuffer = await contract.evaluateTransaction('GetAssetPrivateProperties', assetKey);
+ const asset = JSON.parse(resultBuffer.toString('utf8'));
+ console.log(`*** Result: GetAssetPrivateProperties, ${JSON.stringify(asset)}`);
+
+ } catch (evalError) {
+ console.log(`*** Failed evaluateTransaction readPrivateAsset: ${evalError}`);
+ }
+}
+
+async function readBidPrice(assetKey, org, contract) {
+ console.log(`${GREEN}--> Evaluate Transaction: GetAssetBidPrice, - ${assetKey} from organization ${org}${RESET}`);
+ try {
+ const resultBuffer = await contract.evaluateTransaction('GetAssetBidPrice', assetKey);
+ const asset = JSON.parse(resultBuffer.toString('utf8'));
+ console.log(`*** Result: GetAssetBidPrice, ${JSON.stringify(asset)}`);
+
+ } catch (evalError) {
+ console.log(`*** Failed evaluateTransaction GetAssetBidPrice: ${evalError}`);
+ }
+}
+
+async function readSalePrice(assetKey, org, contract) {
+ console.log(`${GREEN}--> Evaluate Transaction: GetAssetSalesPrice, - ${assetKey} from organization ${org}${RESET}`);
+ try {
+ const resultBuffer = await contract.evaluateTransaction('GetAssetSalesPrice', assetKey);
+ const asset = JSON.parse(resultBuffer.toString('utf8'));
+ console.log(`*** Result: GetAssetSalesPrice, ${JSON.stringify(asset)}`);
+
+ } catch (evalError) {
+ console.log(`*** Failed evaluateTransaction GetAssetSalesPrice: ${evalError}`);
+ }
+}
+
+function checkAsset(org, resultBuffer, ownerOrg) {
+ let asset;
+ if (resultBuffer) {
+ asset = JSON.parse(resultBuffer.toString('utf8'));
+ }
+
+ if (asset) {
+ if (asset.ownerOrg === ownerOrg) {
+ console.log(`*** Result from ${org} - asset ${asset.assetID} owned by ${asset.ownerOrg} DESC:${asset.publicDescription}`);
+ } else {
+ console.log(`${RED}*** Failed owner check from ${org} - asset ${asset.assetID} owned by ${asset.ownerOrg} DESC:${asset.publicDescription}${RESET}`);
+ }
+ }
+}
+
+// This is not a real function for an application, this simulates when two applications are running
+// from different organizations and what they would see if they were to both query the asset
+async function readAssetByBothOrgs(assetKey, ownerOrg, contractOrg1, contractOrg2) {
+ console.log(`${GREEN}--> Evaluate Transactions: ReadAsset, - ${assetKey} should be owned by ${ownerOrg}${RESET}`);
+ let resultBuffer;
+ resultBuffer = await contractOrg1.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset('Org1', resultBuffer, ownerOrg);
+ resultBuffer = await contractOrg2.evaluateTransaction('ReadAsset', assetKey);
+ checkAsset('Org2', resultBuffer, ownerOrg);
+}
+
+// This application uses fabric-samples/test-network based setup and the companion chaincode
+// For this illustration, both Org1 & Org2 client identities will be used, however
+// notice they are used by two different "gateway"s to simulate two different running
+// applications from two different organizations.
+async function main() {
+ console.log(`${GREEN} **** START ****${RESET}`);
+ try {
+ const randomNumber = Math.floor(Math.random() * 100) + 1;
+ // use a random key so that we can run multiple times
+ const assetKey = `asset-${randomNumber}`;
+
+ /** ******* Fabric client init: Using Org1 identity to Org1 Peer ******* */
+ const gatewayOrg1 = await initGatewayForOrg1();
+ const networkOrg1 = await gatewayOrg1.getNetwork(channelName);
+ const contractOrg1 = networkOrg1.getContract(chaincodeName);
+
+ /** ******* Fabric client init: Using Org2 identity to Org2 Peer ******* */
+ const gatewayOrg2 = await initGatewayForOrg2();
+ const networkOrg2 = await gatewayOrg2.getNetwork(channelName);
+ const contractOrg2 = networkOrg2.getContract(chaincodeName);
+
+ try {
+ let transaction;
+
+ try {
+ // Create an asset by organization Org1, this only requires the owning
+ // organization to endorse.
+ // With the gateway using discovery, we should limit the organizations used
+ // to endorse. This only requires knowledge of the Organizations and not
+ // the actual peers that may be active at any given time.
+ const asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: assetKey,
+ color: 'blue',
+ size: 35,
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ const asset_properties_string = JSON.stringify(asset_properties);
+ console.log(`${GREEN}--> Submit Transaction: CreateAsset, ${assetKey} as Org1 - endorsed by Org1${RESET}`);
+ console.log(`${asset_properties_string}`);
+ transaction = contractOrg1.createTransaction('CreateAsset');
+ transaction.setEndorsingOrganizations(org1);
+ transaction.setTransient({
+ asset_properties: Buffer.from(asset_properties_string)
+ });
+ await transaction.submit(assetKey, `Asset ${assetKey} owned by ${org1} is not for sale`);
+ console.log(`*** Result: committed, asset ${assetKey} is owned by Org1`);
+ } catch (createError) {
+ console.log(`${RED}*** Failed: CreateAsset - ${createError}${RESET}`);
+ }
+
+ // read the public details by both orgs
+ await readAssetByBothOrgs(assetKey, org1, contractOrg1, contractOrg2);
+ // Org1 should be able to read the private data details of this asset
+ await readPrivateAsset(assetKey, org1, contractOrg1);
+ // Org2 is not the owner and does not have the private details, this should fail
+ await readPrivateAsset(assetKey, org2, contractOrg2);
+
+ try {
+ // This is an update to the public state and requires only the owner to endorse.
+ console.log(`${GREEN}--> Submit Transaction: ChangePublicDescription ${assetKey}, as Org1 - endorse by Org1${RESET}`);
+ transaction = contractOrg1.createTransaction('ChangePublicDescription');
+ transaction.setEndorsingOrganizations(org1);
+ await transaction.submit(assetKey, `Asset ${assetKey} owned by ${org1} is for sale`);
+ console.log(`*** Result: committed, asset ${assetKey} is now for sale by Org1`);
+ } catch (updateError) {
+ console.log(`${RED}*** Failed: ChangePublicDescription - ${updateError}${RESET}`);
+ }
+
+ // read the public details by both orgs
+ await readAssetByBothOrgs(assetKey, org1, contractOrg1, contractOrg2);
+
+ try {
+ // This is an update to the public state and requires the owner(Org1) to endorse and
+ // sent by the owner org client (Org1).
+ // Since the client is from Org2, which is not the owner, this will fail
+ console.log(`${GREEN}--> Submit Transaction: ChangePublicDescription ${assetKey}, as Org2 - endorse by Org2${RESET}`);
+ transaction = contractOrg2.createTransaction('ChangePublicDescription');
+ transaction.setEndorsingOrganizations(org2);
+ await transaction.submit(assetKey, `Asset ${assetKey} owned by ${org2} is NOT for sale`);
+ console.log(`${RESET}*** Failed: Org2 is not the owner and this should have failed${RESET}`);
+ } catch (updateError) {
+ console.log(`*** Success: ChangePublicDescription has failed endorsememnt by Org2 sent by Org2 - ${updateError}`);
+ }
+
+ try {
+ // This is an update to the public state and requires the owner(Org1) to endorse and
+ // sent by the owner org client (Org1).
+ // Since this is being sent by Org2, which is not the owner, this will fail
+ console.log(`${GREEN}--> Submit Transaction: ChangePublicDescription ${assetKey}, as Org2 - endorse by Org1${RESET}`);
+ transaction = contractOrg2.createTransaction('ChangePublicDescription');
+ transaction.setEndorsingOrganizations(org1);
+ await transaction.submit(assetKey, `Asset ${assetKey} owned by ${org2} is NOT for sale`);
+ console.log(`${RESET}*** Failed: Org2 is not the owner and this should have failed${RESET}`);
+ } catch (updateError) {
+ console.log(`*** Success: ChangePublicDescription has failed endorsement by Org1 sent by Org2 - ${updateError}`);
+ }
+
+ // read the public details by both orgs
+ await readAssetByBothOrgs(assetKey, org1, contractOrg1, contractOrg2);
+
+ try {
+ // Agree to a sell by Org1
+ const asset_price = {
+ asset_id: assetKey,
+ price: 110,
+ trade_id: randomNumber.toString()
+ };
+ const asset_price_string = JSON.stringify(asset_price);
+ console.log(`${GREEN}--> Submit Transaction: AgreeToSell, ${assetKey} as Org1 - endorsed by Org1${RESET}`);
+ transaction = contractOrg1.createTransaction('AgreeToSell');
+ transaction.setEndorsingOrganizations(org1);
+ transaction.setTransient({
+ asset_price: Buffer.from(asset_price_string)
+ });
+ await transaction.submit(assetKey);
+ console.log(`*** Result: committed, Org1 has agreed to sell asset ${assetKey} for 110`);
+ } catch (sellError) {
+ console.log(`${RED}*** Failed: AgreeToSell - ${sellError}${RESET}`);
+ }
+
+ try {
+ // check the private information about the asset from Org2
+ // Org1 would have to send Org2 these details, so the hash of the
+ // details may be checked by the chaincode.
+ const asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: assetKey,
+ color: 'blue',
+ size: 35,
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ const asset_properties_string = JSON.stringify(asset_properties);
+ console.log(`${GREEN}--> Evalute: VerifyAssetProperties, ${assetKey} as Org2 - endorsed by Org2${RESET}`);
+ console.log(`${asset_properties_string}`);
+ transaction = contractOrg2.createTransaction('VerifyAssetProperties');
+ transaction.setTransient({
+ asset_properties: Buffer.from(asset_properties_string)
+ });
+ const verifyResultBuffer = await transaction.evaluate(assetKey);
+ if (verifyResultBuffer) {
+ const verifyResult = Boolean(verifyResultBuffer.toString());
+ if (verifyResult) {
+ console.log(`*** Successfully VerifyAssetProperties, private information about asset ${assetKey} has been verified by Org2`);
+ } else {
+ console.log(`*** Failed: VerifyAssetProperties, private information about asset ${assetKey} has not been verified by Org2`);
+ }
+ } else {
+ console.log(`*** Failed: VerifyAssetProperties, private information about asset ${assetKey} has not been verified by Org2`);
+ }
+ } catch (verifyError) {
+ console.log(`${RED}*** Failed: VerifyAssetProperties - ${verifyError}${RESET}`);
+ }
+
+ try {
+ // Agree to a buy by Org2
+ const asset_price = {
+ asset_id: assetKey,
+ price: 100,
+ trade_id: randomNumber.toString()
+ };
+ const asset_price_string = JSON.stringify(asset_price);
+ console.log(`${GREEN}--> Submit Transaction: AgreeToBuy, ${assetKey} as Org2 - endorsed by Org2${RESET}`);
+ transaction = contractOrg2.createTransaction('AgreeToBuy');
+ transaction.setEndorsingOrganizations(org2);
+ transaction.setTransient({
+ asset_price: Buffer.from(asset_price_string)
+ });
+ await transaction.submit(assetKey);
+ console.log(`*** Result: committed, Org2 has agreed to buy asset ${assetKey} for 100`);
+ } catch (buyError) {
+ console.log(`${RED}*** Failed: AgreeToBuy - ${buyError}${RESET}`);
+ }
+
+ // read the public details by both orgs
+ await readAssetByBothOrgs(assetKey, org1, contractOrg1, contractOrg2);
+
+ // Org1 should be able to read the private data details of this asset
+ await readPrivateAsset(assetKey, org1, contractOrg1);
+ // Org2 is not the owner and does not have the private details, this should fail
+ await readPrivateAsset(assetKey, org2, contractOrg2);
+
+ // Org1 should be able to read the sale price of this asset
+ await readSalePrice(assetKey, org1, contractOrg1);
+ // Org2 has not set a sale price and this should fail
+ await readSalePrice(assetKey, org2, contractOrg2);
+
+ // Org1 has not agreed to buy so this should fail
+ await readBidPrice(assetKey, org1, contractOrg1);
+ // Org2 should be able to see the price it has agreed
+ await readBidPrice(assetKey, org2, contractOrg2);
+
+ try {
+ // Org1 will try to transfer the asset to Org2
+ // This will fail due to the sell price and the bid price
+ // are not the same
+ const asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: assetKey,
+ color: 'blue',
+ size: 35,
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ const asset_properties_string = JSON.stringify(asset_properties);
+ const asset_price = {
+ asset_id: assetKey,
+ price: 110,
+ trade_id: randomNumber.toString()
+ };
+ const asset_price_string = JSON.stringify(asset_price);
+
+ console.log(`${GREEN}--> Submit Transaction: TransferAsset, ${assetKey} as Org1 - endorsed by Org1${RESET}`);
+ console.log(`${asset_properties_string}`);
+ transaction = contractOrg1.createTransaction('TransferAsset');
+ transaction.setEndorsingOrganizations(org1);
+ transaction.setTransient({
+ asset_properties: Buffer.from(asset_properties_string),
+ asset_price: Buffer.from(asset_price_string)
+ });
+ await transaction.submit(assetKey, org2);
+ console.log(`${RED}*** Failed: committed, TransferAsset should have failed for asset ${assetKey}${RESET}`);
+ } catch (transferError) {
+ console.log(`*** Success: TransferAsset - ${transferError}`);
+ }
+
+ try {
+ // Agree to a sell by Org1
+ // Org1, the seller will agree to the bid price of Org2
+ const asset_price = {
+ asset_id: assetKey,
+ price: 100,
+ trade_id: randomNumber.toString()
+ };
+ const asset_price_string = JSON.stringify(asset_price);
+ console.log(`${GREEN}--> Submit Transaction: AgreeToSell, ${assetKey} as Org1 - endorsed by Org1${RESET}`);
+ transaction = contractOrg1.createTransaction('AgreeToSell');
+ transaction.setEndorsingOrganizations(org1);
+ transaction.setTransient({
+ asset_price: Buffer.from(asset_price_string)
+ });
+ await transaction.submit(assetKey);
+ console.log(`*** Result: committed, Org1 has agreed to sell asset ${assetKey} for 100`);
+ } catch (sellError) {
+ console.log(`${RED}*** Failed: AgreeToSell - ${sellError}${RESET}`);
+ }
+
+ // read the public details by both orgs
+ await readAssetByBothOrgs(assetKey, org1, contractOrg1, contractOrg2);
+
+ // Org1 should be able to read the private data details of this asset
+ await readPrivateAsset(assetKey, org1, contractOrg1);
+
+ // Org1 should be able to read the sale price of this asset
+ await readSalePrice(assetKey, org1, contractOrg1);
+
+ // Org2 should be able to see the price it has agreed
+ await readBidPrice(assetKey, org2, contractOrg2);
+
+ try {
+ // Org2 user will try to transfer the asset to Org2
+ // This will fail as the owner is Org1
+ const asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: assetKey,
+ color: 'blue',
+ size: 35,
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ const asset_properties_string = JSON.stringify(asset_properties);
+ const asset_price = {
+ asset_id: assetKey,
+ price: 100,
+ trade_id: randomNumber.toString()
+ };
+ const asset_price_string = JSON.stringify(asset_price);
+
+ console.log(`${GREEN}--> Submit Transaction: TransferAsset, ${assetKey} as Org2 - endorsed by Org1${RESET}`);
+ console.log(`${asset_properties_string}`);
+ transaction = contractOrg2.createTransaction('TransferAsset');
+ transaction.setEndorsingOrganizations(org1, org2);
+ transaction.setTransient({
+ asset_properties: Buffer.from(asset_properties_string),
+ asset_price: Buffer.from(asset_price_string)
+ });
+ await transaction.submit(assetKey, org2);
+ console.log(`${RED}*** FAILED: committed, TransferAsset - Org2 now owns the asset ${assetKey}${RESET}`);
+ } catch (transferError) {
+ console.log(`*** Succeded: TransferAsset - ${transferError}`);
+ }
+
+ try {
+ // Org1 will transfer the asset to Org2
+ // This will now complete as the sell price and the bid price are the same
+ const asset_properties = {
+ object_type: 'asset_properties',
+ asset_id: assetKey,
+ color: 'blue',
+ size: 35,
+ salt: Buffer.from(randomNumber.toString()).toString('hex')
+ };
+ const asset_properties_string = JSON.stringify(asset_properties);
+ const asset_price = {
+ asset_id: assetKey,
+ price: 100,
+ trade_id: randomNumber.toString()
+ };
+ const asset_price_string = JSON.stringify(asset_price);
+
+ console.log(`${GREEN}--> Submit Transaction: TransferAsset, ${assetKey} as Org1 - endorsed by Org1${RESET}`);
+ console.log(`${asset_properties_string}`);
+ transaction = contractOrg1.createTransaction('TransferAsset');
+ transaction.setEndorsingOrganizations(org1, org2);
+ transaction.setTransient({
+ asset_properties: Buffer.from(asset_properties_string),
+ asset_price: Buffer.from(asset_price_string)
+ });
+ await transaction.submit(assetKey, org2);
+ console.log(`*** Results: committed, TransferAsset - Org2 now owns the asset ${assetKey}`);
+ } catch (transferError) {
+ console.log(`${RED}*** Failed: TransferAsset - ${transferError}${RESET}`);
+ }
+
+ // read the public details by both orgs
+ await readAssetByBothOrgs(assetKey, org2, contractOrg1, contractOrg2);
+
+ // Org2 should be able to read the private data details of this asset
+ await readPrivateAsset(assetKey, org2, contractOrg2);
+ // Org1 should not be able to read the private data details of this asset
+ await readPrivateAsset(assetKey, org1, contractOrg1);
+
+ try {
+ // This is an update to the public state and requires only the owner to endorse.
+ // Org2 wants to indicate that the items is no longer for sale
+ console.log(`${GREEN}--> Submit Transaction: ChangePublicDescription ${assetKey}, as Org2 - endorse by Org2${RESET}`);
+ transaction = contractOrg2.createTransaction('ChangePublicDescription');
+ transaction.setEndorsingOrganizations(org2);
+ await transaction.submit(assetKey, `Asset ${assetKey} owned by ${org2} is NOT for sale`);
+ console.log('*** Results: committed - Org2 is now the owner and asset is not for sale');
+ } catch (updateError) {
+ console.log(`${RED}*** Failed: ChangePublicDescription has failed by Org2 - ${updateError}${RESET}`);
+ }
+
+ // read the public details by both orgs
+ await readAssetByBothOrgs(assetKey, org2, contractOrg1, contractOrg2);
+ } catch (runError) {
+ console.error(`Error in transaction: ${runError}`);
+ if (runError.stack) {
+ console.error(runError.stack);
+ }
+ process.exit(1);
+ } finally {
+ // Disconnect from the gateway peer when all work for this client identity is complete
+ console.log(`${GREEN}--> Close gateways`);
+ gatewayOrg1.disconnect();
+ gatewayOrg2.disconnect();
+ }
+ } catch (error) {
+ console.error(`Error in setup: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+ console.log(`${GREEN} **** END ****${RESET}`);
+}
+main();
diff --git a/asset-transfer-secured-agreement/application-javascript/package.json b/asset-transfer-secured-agreement/application-javascript/package.json
new file mode 100644
index 0000000..d342456
--- /dev/null
+++ b/asset-transfer-secured-agreement/application-javascript/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "asset-transfer-secured-agreement",
+ "version": "1.0.0",
+ "description": "Javascript application that uses implicit private data collections, state-based endorsement, and organization-based ownership and access control to keep data private and securely transfer an asset with the consent of both the current owner and buyer",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.4",
+ "fabric-network": "^2.2.4"
+ }
+}
diff --git a/asset-transfer-secured-agreement/chaincode-go/README.md b/asset-transfer-secured-agreement/chaincode-go/README.md
new file mode 100644
index 0000000..f65f09b
--- /dev/null
+++ b/asset-transfer-secured-agreement/chaincode-go/README.md
@@ -0,0 +1 @@
+[Secured asset transfer in Fabric Tutorial](https://hyperledger-fabric.readthedocs.io/en/latest/secured_asset_transfer/secured_private_asset_transfer_tutorial.html)
diff --git a/asset-transfer-secured-agreement/chaincode-go/asset_transfer.go b/asset-transfer-secured-agreement/chaincode-go/asset_transfer.go
new file mode 100644
index 0000000..cce1c2c
--- /dev/null
+++ b/asset-transfer-secured-agreement/chaincode-go/asset_transfer.go
@@ -0,0 +1,556 @@
+/*
+ SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "encoding/json"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/golang/protobuf/ptypes"
+ "github.com/hyperledger/fabric-chaincode-go/pkg/statebased"
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+const (
+ typeAssetForSale = "S"
+ typeAssetBid = "B"
+ typeAssetSaleReceipt = "SR"
+ typeAssetBuyReceipt = "BR"
+)
+
+type SmartContract struct {
+ contractapi.Contract
+}
+
+// Asset struct and properties must be exported (start with capitals) to work with contract api metadata
+type Asset struct {
+ ObjectType string `json:"objectType"` // ObjectType is used to distinguish different object types in the same chaincode namespace
+ ID string `json:"assetID"`
+ OwnerOrg string `json:"ownerOrg"`
+ PublicDescription string `json:"publicDescription"`
+}
+
+type receipt struct {
+ price int
+ timestamp time.Time
+}
+
+// CreateAsset creates an asset and sets it as owned by the client's org
+func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, assetID, publicDescription string) error {
+ transientMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("error getting transient: %v", err)
+ }
+
+ // Asset properties must be retrieved from the transient field as they are private
+ immutablePropertiesJSON, ok := transientMap["asset_properties"]
+ if !ok {
+ return fmt.Errorf("asset_properties key not found in the transient map")
+ }
+
+ // Get client org id and verify it matches peer org id.
+ // In this scenario, client is only authorized to read/write private data from its own peer.
+ clientOrgID, err := getClientOrgID(ctx, true)
+ if err != nil {
+ return fmt.Errorf("failed to get verified OrgID: %v", err)
+ }
+
+ asset := Asset{
+ ObjectType: "asset",
+ ID: assetID,
+ OwnerOrg: clientOrgID,
+ PublicDescription: publicDescription,
+ }
+ assetBytes, err := json.Marshal(asset)
+ if err != nil {
+ return fmt.Errorf("failed to create asset JSON: %v", err)
+ }
+
+ err = ctx.GetStub().PutState(asset.ID, assetBytes)
+ if err != nil {
+ return fmt.Errorf("failed to put asset in public data: %v", err)
+ }
+
+ // Set the endorsement policy such that an owner org peer is required to endorse future updates
+ err = setAssetStateBasedEndorsement(ctx, asset.ID, clientOrgID)
+ if err != nil {
+ return fmt.Errorf("failed setting state based endorsement for owner: %v", err)
+ }
+
+ // Persist private immutable asset properties to owner's private data collection
+ collection := buildCollectionName(clientOrgID)
+ err = ctx.GetStub().PutPrivateData(collection, asset.ID, immutablePropertiesJSON)
+ if err != nil {
+ return fmt.Errorf("failed to put Asset private details: %v", err)
+ }
+
+ return nil
+}
+
+// ChangePublicDescription updates the assets public description. Only the current owner can update the public description
+func (s *SmartContract) ChangePublicDescription(ctx contractapi.TransactionContextInterface, assetID string, newDescription string) error {
+ // No need to check client org id matches peer org id, rely on the asset ownership check instead.
+ clientOrgID, err := getClientOrgID(ctx, false)
+ if err != nil {
+ return fmt.Errorf("failed to get verified OrgID: %v", err)
+ }
+
+ asset, err := s.ReadAsset(ctx, assetID)
+ if err != nil {
+ return fmt.Errorf("failed to get asset: %v", err)
+ }
+
+ // Auth check to ensure that client's org actually owns the asset
+ if clientOrgID != asset.OwnerOrg {
+ return fmt.Errorf("a client from %s cannot update the description of a asset owned by %s", clientOrgID, asset.OwnerOrg)
+ }
+
+ asset.PublicDescription = newDescription
+ updatedAssetJSON, err := json.Marshal(asset)
+ if err != nil {
+ return fmt.Errorf("failed to marshal asset: %v", err)
+ }
+
+ return ctx.GetStub().PutState(assetID, updatedAssetJSON)
+}
+
+// AgreeToSell adds seller's asking price to seller's implicit private data collection
+func (s *SmartContract) AgreeToSell(ctx contractapi.TransactionContextInterface, assetID string) error {
+ asset, err := s.ReadAsset(ctx, assetID)
+ if err != nil {
+ return err
+ }
+
+ clientOrgID, err := getClientOrgID(ctx, true)
+ if err != nil {
+ return fmt.Errorf("failed to get verified OrgID: %v", err)
+ }
+
+ // Verify that this clientOrgId actually owns the asset.
+ if clientOrgID != asset.OwnerOrg {
+ return fmt.Errorf("a client from %s cannot sell an asset owned by %s", clientOrgID, asset.OwnerOrg)
+ }
+
+ return agreeToPrice(ctx, assetID, typeAssetForSale)
+}
+
+// AgreeToBuy adds buyer's bid price to buyer's implicit private data collection
+func (s *SmartContract) AgreeToBuy(ctx contractapi.TransactionContextInterface, assetID string) error {
+ return agreeToPrice(ctx, assetID, typeAssetBid)
+}
+
+// agreeToPrice adds a bid or ask price to caller's implicit private data collection
+func agreeToPrice(ctx contractapi.TransactionContextInterface, assetID string, priceType string) error {
+ // In this scenario, client is only authorized to read/write private data from its own peer.
+ clientOrgID, err := getClientOrgID(ctx, true)
+ if err != nil {
+ return fmt.Errorf("failed to get verified OrgID: %v", err)
+ }
+
+ transMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("error getting transient: %v", err)
+ }
+
+ // Asset price must be retrieved from the transient field as they are private
+ price, ok := transMap["asset_price"]
+ if !ok {
+ return fmt.Errorf("asset_price key not found in the transient map")
+ }
+
+ collection := buildCollectionName(clientOrgID)
+
+ // Persist the agreed to price in a collection sub-namespace based on priceType key prefix,
+ // to avoid collisions between private asset properties, sell price, and buy price
+ assetPriceKey, err := ctx.GetStub().CreateCompositeKey(priceType, []string{assetID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ // The Price hash will be verified later, therefore always pass and persist price bytes as is,
+ // so that there is no risk of nondeterministic marshaling.
+ err = ctx.GetStub().PutPrivateData(collection, assetPriceKey, price)
+ if err != nil {
+ return fmt.Errorf("failed to put asset bid: %v", err)
+ }
+
+ return nil
+}
+
+// VerifyAssetProperties Allows a buyer to validate the properties of
+// an asset against the owner's implicit private data collection
+func (s *SmartContract) VerifyAssetProperties(ctx contractapi.TransactionContextInterface, assetID string) (bool, error) {
+ transMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return false, fmt.Errorf("error getting transient: %v", err)
+ }
+
+ /// Asset properties must be retrieved from the transient field as they are private
+ immutablePropertiesJSON, ok := transMap["asset_properties"]
+ if !ok {
+ return false, fmt.Errorf("asset_properties key not found in the transient map")
+ }
+
+ asset, err := s.ReadAsset(ctx, assetID)
+ if err != nil {
+ return false, fmt.Errorf("failed to get asset: %v", err)
+ }
+
+ collectionOwner := buildCollectionName(asset.OwnerOrg)
+ immutablePropertiesOnChainHash, err := ctx.GetStub().GetPrivateDataHash(collectionOwner, assetID)
+ if err != nil {
+ return false, fmt.Errorf("failed to read asset private properties hash from seller's collection: %v", err)
+ }
+ if immutablePropertiesOnChainHash == nil {
+ return false, fmt.Errorf("asset private properties hash does not exist: %s", assetID)
+ }
+
+ hash := sha256.New()
+ hash.Write(immutablePropertiesJSON)
+ calculatedPropertiesHash := hash.Sum(nil)
+
+ // verify that the hash of the passed immutable properties matches the on-chain hash
+ if !bytes.Equal(immutablePropertiesOnChainHash, calculatedPropertiesHash) {
+ return false, fmt.Errorf("hash %x for passed immutable properties %s does not match on-chain hash %x",
+ calculatedPropertiesHash,
+ immutablePropertiesJSON,
+ immutablePropertiesOnChainHash,
+ )
+ }
+
+ return true, nil
+}
+
+// TransferAsset checks transfer conditions and then transfers asset state to buyer.
+// TransferAsset can only be called by current owner
+func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, assetID string, buyerOrgID string) error {
+ clientOrgID, err := getClientOrgID(ctx, false)
+ if err != nil {
+ return fmt.Errorf("failed to get verified OrgID: %v", err)
+ }
+
+ transMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("error getting transient data: %v", err)
+ }
+
+ immutablePropertiesJSON, ok := transMap["asset_properties"]
+ if !ok {
+ return fmt.Errorf("asset_properties key not found in the transient map")
+ }
+
+ priceJSON, ok := transMap["asset_price"]
+ if !ok {
+ return fmt.Errorf("asset_price key not found in the transient map")
+ }
+
+ var agreement Agreement
+ err = json.Unmarshal(priceJSON, &agreement)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal price JSON: %v", err)
+ }
+
+ asset, err := s.ReadAsset(ctx, assetID)
+ if err != nil {
+ return fmt.Errorf("failed to get asset: %v", err)
+ }
+
+ err = verifyTransferConditions(ctx, asset, immutablePropertiesJSON, clientOrgID, buyerOrgID, priceJSON)
+ if err != nil {
+ return fmt.Errorf("failed transfer verification: %v", err)
+ }
+
+ err = transferAssetState(ctx, asset, immutablePropertiesJSON, clientOrgID, buyerOrgID, agreement.Price)
+ if err != nil {
+ return fmt.Errorf("failed asset transfer: %v", err)
+ }
+
+ return nil
+
+}
+
+// verifyTransferConditions checks that client org currently owns asset and that both parties have agreed on price
+func verifyTransferConditions(ctx contractapi.TransactionContextInterface,
+ asset *Asset,
+ immutablePropertiesJSON []byte,
+ clientOrgID string,
+ buyerOrgID string,
+ priceJSON []byte) error {
+
+ // CHECK1: Auth check to ensure that client's org actually owns the asset
+
+ if clientOrgID != asset.OwnerOrg {
+ return fmt.Errorf("a client from %s cannot transfer a asset owned by %s", clientOrgID, asset.OwnerOrg)
+ }
+
+ // CHECK2: Verify that the hash of the passed immutable properties matches the on-chain hash
+
+ collectionSeller := buildCollectionName(clientOrgID)
+ immutablePropertiesOnChainHash, err := ctx.GetStub().GetPrivateDataHash(collectionSeller, asset.ID)
+ if err != nil {
+ return fmt.Errorf("failed to read asset private properties hash from seller's collection: %v", err)
+ }
+ if immutablePropertiesOnChainHash == nil {
+ return fmt.Errorf("asset private properties hash does not exist: %s", asset.ID)
+ }
+
+ hash := sha256.New()
+ hash.Write(immutablePropertiesJSON)
+ calculatedPropertiesHash := hash.Sum(nil)
+
+ // verify that the hash of the passed immutable properties matches the on-chain hash
+ if !bytes.Equal(immutablePropertiesOnChainHash, calculatedPropertiesHash) {
+ return fmt.Errorf("hash %x for passed immutable properties %s does not match on-chain hash %x",
+ calculatedPropertiesHash,
+ immutablePropertiesJSON,
+ immutablePropertiesOnChainHash,
+ )
+ }
+
+ // CHECK3: Verify that seller and buyer agreed on the same price
+
+ // Get sellers asking price
+ assetForSaleKey, err := ctx.GetStub().CreateCompositeKey(typeAssetForSale, []string{asset.ID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key: %v", err)
+ }
+ sellerPriceHash, err := ctx.GetStub().GetPrivateDataHash(collectionSeller, assetForSaleKey)
+ if err != nil {
+ return fmt.Errorf("failed to get seller price hash: %v", err)
+ }
+ if sellerPriceHash == nil {
+ return fmt.Errorf("seller price for %s does not exist", asset.ID)
+ }
+
+ // Get buyers bid price
+ collectionBuyer := buildCollectionName(buyerOrgID)
+ assetBidKey, err := ctx.GetStub().CreateCompositeKey(typeAssetBid, []string{asset.ID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key: %v", err)
+ }
+ buyerPriceHash, err := ctx.GetStub().GetPrivateDataHash(collectionBuyer, assetBidKey)
+ if err != nil {
+ return fmt.Errorf("failed to get buyer price hash: %v", err)
+ }
+ if buyerPriceHash == nil {
+ return fmt.Errorf("buyer price for %s does not exist", asset.ID)
+ }
+
+ hash = sha256.New()
+ hash.Write(priceJSON)
+ calculatedPriceHash := hash.Sum(nil)
+
+ // Verify that the hash of the passed price matches the on-chain sellers price hash
+ if !bytes.Equal(calculatedPriceHash, sellerPriceHash) {
+ return fmt.Errorf("hash %x for passed price JSON %s does not match on-chain hash %x, seller hasn't agreed to the passed trade id and price",
+ calculatedPriceHash,
+ priceJSON,
+ sellerPriceHash,
+ )
+ }
+
+ // Verify that the hash of the passed price matches the on-chain buyer price hash
+ if !bytes.Equal(calculatedPriceHash, buyerPriceHash) {
+ return fmt.Errorf("hash %x for passed price JSON %s does not match on-chain hash %x, buyer hasn't agreed to the passed trade id and price",
+ calculatedPriceHash,
+ priceJSON,
+ buyerPriceHash,
+ )
+ }
+
+ return nil
+}
+
+// transferAssetState performs the public and private state updates for the transferred asset
+func transferAssetState(ctx contractapi.TransactionContextInterface, asset *Asset, immutablePropertiesJSON []byte, clientOrgID string, buyerOrgID string, price int) error {
+ asset.OwnerOrg = buyerOrgID
+ updatedAsset, err := json.Marshal(asset)
+ if err != nil {
+ return err
+ }
+
+ err = ctx.GetStub().PutState(asset.ID, updatedAsset)
+ if err != nil {
+ return fmt.Errorf("failed to write asset for buyer: %v", err)
+ }
+
+ // Change the endorsement policy to the new owner
+ err = setAssetStateBasedEndorsement(ctx, asset.ID, buyerOrgID)
+ if err != nil {
+ return fmt.Errorf("failed setting state based endorsement for new owner: %v", err)
+ }
+
+ // Transfer the private properties (delete from seller collection, create in buyer collection)
+ collectionSeller := buildCollectionName(clientOrgID)
+ err = ctx.GetStub().DelPrivateData(collectionSeller, asset.ID)
+ if err != nil {
+ return fmt.Errorf("failed to delete Asset private details from seller: %v", err)
+ }
+
+ collectionBuyer := buildCollectionName(buyerOrgID)
+ err = ctx.GetStub().PutPrivateData(collectionBuyer, asset.ID, immutablePropertiesJSON)
+ if err != nil {
+ return fmt.Errorf("failed to put Asset private properties for buyer: %v", err)
+ }
+
+ // Delete the price records for seller
+ assetPriceKey, err := ctx.GetStub().CreateCompositeKey(typeAssetForSale, []string{asset.ID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key for seller: %v", err)
+ }
+
+ err = ctx.GetStub().DelPrivateData(collectionSeller, assetPriceKey)
+ if err != nil {
+ return fmt.Errorf("failed to delete asset price from implicit private data collection for seller: %v", err)
+ }
+
+ // Delete the price records for buyer
+ assetPriceKey, err = ctx.GetStub().CreateCompositeKey(typeAssetBid, []string{asset.ID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key for buyer: %v", err)
+ }
+
+ err = ctx.GetStub().DelPrivateData(collectionBuyer, assetPriceKey)
+ if err != nil {
+ return fmt.Errorf("failed to delete asset price from implicit private data collection for buyer: %v", err)
+ }
+
+ // Keep record for a 'receipt' in both buyers and sellers private data collection to record the sale price and date.
+ // Persist the agreed to price in a collection sub-namespace based on receipt key prefix.
+ receiptBuyKey, err := ctx.GetStub().CreateCompositeKey(typeAssetBuyReceipt, []string{asset.ID, ctx.GetStub().GetTxID()})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key for receipt: %v", err)
+ }
+
+ txTimestamp, err := ctx.GetStub().GetTxTimestamp()
+ if err != nil {
+ return fmt.Errorf("failed to create timestamp for receipt: %v", err)
+ }
+
+ timestamp, err := ptypes.Timestamp(txTimestamp)
+ if err != nil {
+ return err
+ }
+ assetReceipt := receipt{
+ price: price,
+ timestamp: timestamp,
+ }
+ receipt, err := json.Marshal(assetReceipt)
+ if err != nil {
+ return fmt.Errorf("failed to marshal receipt: %v", err)
+ }
+
+ err = ctx.GetStub().PutPrivateData(collectionBuyer, receiptBuyKey, receipt)
+ if err != nil {
+ return fmt.Errorf("failed to put private asset receipt for buyer: %v", err)
+ }
+
+ receiptSaleKey, err := ctx.GetStub().CreateCompositeKey(typeAssetSaleReceipt, []string{ctx.GetStub().GetTxID(), asset.ID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key for receipt: %v", err)
+ }
+
+ err = ctx.GetStub().PutPrivateData(collectionSeller, receiptSaleKey, receipt)
+ if err != nil {
+ return fmt.Errorf("failed to put private asset receipt for seller: %v", err)
+ }
+
+ return nil
+}
+
+// getClientOrgID gets the client org ID.
+// The client org ID can optionally be verified against the peer org ID, to ensure that a client
+// from another org doesn't attempt to read or write private data from this peer.
+// The only exception in this scenario is for TransferAsset, since the current owner
+// needs to get an endorsement from the buyer's peer.
+func getClientOrgID(ctx contractapi.TransactionContextInterface, verifyOrg bool) (string, error) {
+ clientOrgID, err := ctx.GetClientIdentity().GetMSPID()
+ if err != nil {
+ return "", fmt.Errorf("failed getting client's orgID: %v", err)
+ }
+
+ if verifyOrg {
+ err = verifyClientOrgMatchesPeerOrg(clientOrgID)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ return clientOrgID, nil
+}
+
+// verifyClientOrgMatchesPeerOrg checks the client org id matches the peer org id.
+func verifyClientOrgMatchesPeerOrg(clientOrgID string) error {
+ peerOrgID, err := shim.GetMSPID()
+ if err != nil {
+ return fmt.Errorf("failed getting peer's orgID: %v", err)
+ }
+
+ if clientOrgID != peerOrgID {
+ return fmt.Errorf("client from org %s is not authorized to read or write private data from an org %s peer",
+ clientOrgID,
+ peerOrgID,
+ )
+ }
+
+ return nil
+}
+
+// setAssetStateBasedEndorsement adds an endorsement policy to a asset so that only a peer from an owning org
+// can update or transfer the asset.
+func setAssetStateBasedEndorsement(ctx contractapi.TransactionContextInterface, assetID string, orgToEndorse string) error {
+ endorsementPolicy, err := statebased.NewStateEP(nil)
+ if err != nil {
+ return err
+ }
+ err = endorsementPolicy.AddOrgs(statebased.RoleTypePeer, orgToEndorse)
+ if err != nil {
+ return fmt.Errorf("failed to add org to endorsement policy: %v", err)
+ }
+ policy, err := endorsementPolicy.Policy()
+ if err != nil {
+ return fmt.Errorf("failed to create endorsement policy bytes from org: %v", err)
+ }
+ err = ctx.GetStub().SetStateValidationParameter(assetID, policy)
+ if err != nil {
+ return fmt.Errorf("failed to set validation parameter on asset: %v", err)
+ }
+
+ return nil
+}
+
+func buildCollectionName(clientOrgID string) string {
+ return fmt.Sprintf("_implicit_org_%s", clientOrgID)
+}
+
+func getClientImplicitCollectionName(ctx contractapi.TransactionContextInterface) (string, error) {
+ clientOrgID, err := getClientOrgID(ctx, true)
+ if err != nil {
+ return "", fmt.Errorf("failed to get verified OrgID: %v", err)
+ }
+
+ err = verifyClientOrgMatchesPeerOrg(clientOrgID)
+ if err != nil {
+ return "", err
+ }
+
+ return buildCollectionName(clientOrgID), nil
+}
+
+func main() {
+ chaincode, err := contractapi.NewChaincode(new(SmartContract))
+ if err != nil {
+ log.Panicf("Error create transfer asset chaincode: %v", err)
+ }
+
+ if err := chaincode.Start(); err != nil {
+ log.Panicf("Error starting asset chaincode: %v", err)
+ }
+}
diff --git a/asset-transfer-secured-agreement/chaincode-go/asset_transfer_queries.go b/asset-transfer-secured-agreement/chaincode-go/asset_transfer_queries.go
new file mode 100644
index 0000000..7bc567f
--- /dev/null
+++ b/asset-transfer-secured-agreement/chaincode-go/asset_transfer_queries.go
@@ -0,0 +1,176 @@
+/*
+ SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "github.com/golang/protobuf/ptypes"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// QueryResult structure used for handling result of query
+type QueryResult struct {
+ Record *Asset
+ TxId string `json:"txId"`
+ Timestamp time.Time `json:"timestamp"`
+}
+
+type Agreement struct {
+ ID string `json:"asset_id"`
+ Price int `json:"price"`
+ TradeID string `json:"trade_id"`
+}
+
+// ReadAsset returns the public asset data
+func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, assetID string) (*Asset, error) {
+ // Since only public data is accessed in this function, no access control is required
+ assetJSON, err := ctx.GetStub().GetState(assetID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read from world state: %v", err)
+ }
+ if assetJSON == nil {
+ return nil, fmt.Errorf("%s does not exist", assetID)
+ }
+
+ var asset *Asset
+ err = json.Unmarshal(assetJSON, &asset)
+ if err != nil {
+ return nil, err
+ }
+ return asset, nil
+}
+
+// GetAssetPrivateProperties returns the immutable asset properties from owner's private data collection
+func (s *SmartContract) GetAssetPrivateProperties(ctx contractapi.TransactionContextInterface, assetID string) (string, error) {
+ // In this scenario, client is only authorized to read/write private data from its own peer.
+ collection, err := getClientImplicitCollectionName(ctx)
+ if err != nil {
+ return "", err
+ }
+
+ immutableProperties, err := ctx.GetStub().GetPrivateData(collection, assetID)
+ if err != nil {
+ return "", fmt.Errorf("failed to read asset private properties from client org's collection: %v", err)
+ }
+ if immutableProperties == nil {
+ return "", fmt.Errorf("asset private details does not exist in client org's collection: %s", assetID)
+ }
+
+ return string(immutableProperties), nil
+}
+
+// GetAssetSalesPrice returns the sales price
+func (s *SmartContract) GetAssetSalesPrice(ctx contractapi.TransactionContextInterface, assetID string) (string, error) {
+ return getAssetPrice(ctx, assetID, typeAssetForSale)
+}
+
+// GetAssetBidPrice returns the bid price
+func (s *SmartContract) GetAssetBidPrice(ctx contractapi.TransactionContextInterface, assetID string) (string, error) {
+ return getAssetPrice(ctx, assetID, typeAssetBid)
+}
+
+// getAssetPrice gets the bid or ask price from caller's implicit private data collection
+func getAssetPrice(ctx contractapi.TransactionContextInterface, assetID string, priceType string) (string, error) {
+ collection, err := getClientImplicitCollectionName(ctx)
+ if err != nil {
+ return "", err
+ }
+
+ assetPriceKey, err := ctx.GetStub().CreateCompositeKey(priceType, []string{assetID})
+ if err != nil {
+ return "", fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ price, err := ctx.GetStub().GetPrivateData(collection, assetPriceKey)
+ if err != nil {
+ return "", fmt.Errorf("failed to read asset price from implicit private data collection: %v", err)
+ }
+ if price == nil {
+ return "", fmt.Errorf("asset price does not exist: %s", assetID)
+ }
+
+ return string(price), nil
+}
+
+// QueryAssetSaleAgreements returns all of an organization's proposed sales
+func (s *SmartContract) QueryAssetSaleAgreements(ctx contractapi.TransactionContextInterface) ([]Agreement, error) {
+ return queryAgreementsByType(ctx, typeAssetForSale)
+}
+
+// QueryAssetBuyAgreements returns all of an organization's proposed bids
+func (s *SmartContract) QueryAssetBuyAgreements(ctx contractapi.TransactionContextInterface) ([]Agreement, error) {
+ return queryAgreementsByType(ctx, typeAssetBid)
+}
+
+func queryAgreementsByType(ctx contractapi.TransactionContextInterface, agreeType string) ([]Agreement, error) {
+ collection, err := getClientImplicitCollectionName(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // Query for any object type starting with `agreeType`
+ agreementsIterator, err := ctx.GetStub().GetPrivateDataByPartialCompositeKey(collection, agreeType, []string{})
+ if err != nil {
+ return nil, fmt.Errorf("failed to read from private data collection: %v", err)
+ }
+ defer agreementsIterator.Close()
+
+ var agreements []Agreement
+ for agreementsIterator.HasNext() {
+ resp, err := agreementsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ var agreement Agreement
+ err = json.Unmarshal(resp.Value, &agreement)
+ if err != nil {
+ return nil, err
+ }
+
+ agreements = append(agreements, agreement)
+ }
+
+ return agreements, nil
+}
+
+// QueryAssetHistory returns the chain of custody for a asset since issuance
+func (s *SmartContract) QueryAssetHistory(ctx contractapi.TransactionContextInterface, assetID string) ([]QueryResult, error) {
+ resultsIterator, err := ctx.GetStub().GetHistoryForKey(assetID)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ var results []QueryResult
+ for resultsIterator.HasNext() {
+ response, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ var asset *Asset
+ err = json.Unmarshal(response.Value, &asset)
+ if err != nil {
+ return nil, err
+ }
+
+ timestamp, err := ptypes.Timestamp(response.Timestamp)
+ if err != nil {
+ return nil, err
+ }
+ record := QueryResult{
+ TxId: response.TxId,
+ Timestamp: timestamp,
+ Record: asset,
+ }
+ results = append(results, record)
+ }
+
+ return results, nil
+}
diff --git a/asset-transfer-secured-agreement/chaincode-go/go.mod b/asset-transfer-secured-agreement/chaincode-go/go.mod
new file mode 100644
index 0000000..4f36be9
--- /dev/null
+++ b/asset-transfer-secured-agreement/chaincode-go/go.mod
@@ -0,0 +1,9 @@
+module github.com/hyperledger/fabric-samples/chaincode/tradingMarbles
+
+go 1.14
+
+require (
+ github.com/golang/protobuf v1.3.2
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20200128192331-2d899240a7ed
+ github.com/hyperledger/fabric-contract-api-go v1.0.0
+)
diff --git a/asset-transfer-secured-agreement/chaincode-go/go.sum b/asset-transfer-secured-agreement/chaincode-go/go.sum
new file mode 100644
index 0000000..6d9ad15
--- /dev/null
+++ b/asset-transfer-secured-agreement/chaincode-go/go.sum
@@ -0,0 +1,136 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200128192331-2d899240a7ed h1:VNnrD/ilIUO9DDHQP/uioYSy1309rYy0Z1jf3GLNRIc=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200128192331-2d899240a7ed/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.0.0 h1:ma1nQX1S/a3zDkfkTb0QXQHNGgJUmEfqHA9/CWmz8Y0=
+github.com/hyperledger/fabric-contract-api-go v1.0.0/go.mod h1:PHF7I0hYI0cZF2j7cdyNHaY5FJD3Q49qnnNgsmxEPbM=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200124220212-e9cfc186ba7b h1:rZ3Vro68vStzLYfcSrQlprjjCf5UmFk7QjKGgHL8IQg=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200124220212-e9cfc186ba7b/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/auction/README.md b/auction/README.md
new file mode 100644
index 0000000..7389172
--- /dev/null
+++ b/auction/README.md
@@ -0,0 +1,416 @@
+## Simple blind auction sample
+
+The simple blind auction sample uses Hyperledger Fabric to run an auction where bids are kept private until the auction period is over. Instead of displaying the full bid on the public ledger, buyers can only see hashes of other bids while bidding is underway. This prevents buyers from changing their bids in response to bids submitted by others. After the bidding period ends, participants reveal their bid to try to win the auction. The organizations participating in the auction verify that a revealed bid matches the hash on the public ledger. Whichever has the highest bid wins.
+
+A user that wants to sell one item can use the smart contract to create an auction. The auction is stored on the channel ledger and can be read by all channel members. The auctions created by the smart contract are run in three steps:
+1. Each auction is created with the status **open**. While the auction is open, buyers can add new bids to the auction. The full bids of each buyer are stored in the implicit private data collections of their organization. After the bid is created, the bidder can submit the hash of the bid to the auction. A bid is added to the auction in two steps because the transaction that creates the bid only needs to be endorsed by a peer of the bidders organization, while a transaction that updates the auction may need to be endorsed by multiple organizations. When the bid is added to the auction, the bidder's organization is added to the list of organizations that need to endorse any updates to the auction.
+2. The auction is **closed** to prevent additional bids from being added to the auction. After the auction is closed, bidders that submitted bids to the auction can reveal their full bid. Only revealed bids can win the auction.
+3. The auction is **ended** to calculate the winner from the set of revealed bids. All organizations participating in the auction calculate the price that clears the auction and the winning bid. The seller can end the auction only if all bidding organizations endorse the same winner and price.
+
+Before endorsing the transaction that ends the auction, each organization queries the implicit private data collection on their peers to check if any organization member has a winning bid that has not yet been revealed. If a winning bid is found, the organization will withhold their endorsement and prevent the auction from being closed. This prevents the seller from ending the auction prematurely, or colluding with buyers to end the auction at an artificially low price.
+
+The sample uses several Fabric features to make the auction private and secure. Bids are stored in private data collections to prevent bids from being distributed to other peers in the channel. When bidding is closed, the auction smart contract uses the `GetPrivateDataHash()` API to verify that the bid stored in private data is the same bid that is being revealed. State based endorsement is used to add the organization of each bidder to the auction endorsement policy. The smart contract uses the `GetClientIdentity.GetID()` API to ensure that only the potential buyer can read their bid from private state and only the seller can close or end the auction.
+
+This tutorial uses the auction smart contract in a scenario where one seller wants to auction a painting. Four potential buyers from two different organizations will submit bids to the auction and try to win the auction.
+
+## Deploy the chaincode
+
+We will run the auction smart contract using the Fabric test network. Open a command terminal and navigate to the test network directory:
+```
+cd fabric-samples/test-network
+```
+
+You can then run the following command to deploy the test network.
+```
+./network.sh up createChannel -ca
+```
+
+Note that we use the `-ca` flag to deploy the network using certificate authorities. We will use the CA to register and enroll our sellers and buyers.
+
+Run the following command to deploy the auction smart contract. We will override the default endorsement policy to allow any channel member to create an auction without requiring an endorsement from another organization.
+```
+./network.sh deployCC -ccn auction -ccp ../auction/chaincode-go/ -ccl go -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
+```
+
+## Install the application dependencies
+
+We will interact with the auction smart contract through a set of Node.js applications. Change into the `application-javascript` directory:
+```
+cd fabric-samples/auction/application-javascript
+```
+
+From this directory, run the following command to download the application dependencies:
+```
+npm install
+```
+
+## Register and enroll the application identities
+
+To interact with the network, you will need to enroll the Certificate Authority administrators of Org1 and Org2. You can use the `enrollAdmin.js` program for this task. Run the following command to enroll the Org1 admin:
+```
+node enrollAdmin.js org1
+```
+You should see the logs of the admin wallet being created on your local file system. Now run the command to enroll the CA admin of Org2:
+```
+node enrollAdmin.js org2
+```
+
+We can use the CA admins of both organizations to register and enroll the identities of the seller that will create the auction and the bidders who will try to purchase the painting.
+
+Run the following command to register and enroll the seller identity that will create the auction. The seller will belong to Org1.
+```
+node registerEnrollUser.js org1 seller
+```
+
+You should see the logs of the seller wallet being created as well. Run the following commands to register and enroll 2 bidders from Org1 and another 2 bidders from Org2:
+```
+node registerEnrollUser.js org1 bidder1
+node registerEnrollUser.js org1 bidder2
+node registerEnrollUser.js org2 bidder3
+node registerEnrollUser.js org2 bidder4
+```
+
+## Create the auction
+
+The seller from Org1 would like to create an auction to sell a vintage Matchbox painting. Run the following command to use the seller wallet to run the `createAuction.js` application. The program will submit a transaction to the network that creates the auction on the channel ledger. The organization and identity name are passed to the application to use the wallet that was created by the `registerEnrollUser.js` application. The seller needs to provide an ID for the auction and the item to be sold to create the auction:
+```
+node createAuction.js org1 seller PaintingAuction painting
+```
+
+After the transaction is complete, the `createAuction.js` application will query the auction stored in the public channel ledger:
+```
+*** Result: Auction: {
+ "objectType": "auction",
+ "item": "painting",
+ "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
+ "organizations": [
+ "Org1MSP"
+ ],
+ "privateBids": {},
+ "revealedBids": {},
+ "winner": "",
+ "price": 0,
+ "status": "open"
+}
+```
+The smart contract uses the `GetClientIdentity().GetID()` API to read identity that creates the auction and defines that identity as the auction `"seller"`. You can see the seller information by decoding the `"seller"` string out of base64 format:
+
+```
+echo eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT | base64 --decode
+```
+
+The result is the name and issuer of the seller's certificate:
+```
+x509::CN=org1admin,OU=admin,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.example.com,O=org1.example.com,L=Durham,ST=North Carolina,C=USn
+```
+
+## Bid on the auction
+
+We can now use the bidder wallets to submit bids to the auction:
+
+### Bid as bidder1
+
+Bidder1 will create a bid to purchase the painting for 800 dollars.
+```
+node bid.js org1 bidder1 PaintingAuction 800
+```
+
+The application will query the bid after it is created:
+```
+*** Result: Bid: {
+ "objectType": "bid",
+ "price": 800,
+ "org": "Org1MSP",
+ "bidder": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
+}
+```
+
+The bid is stored in the Org1 implicit data collection. The `"bidder"` parameter is the information from the certificate of the user that created the bid. Only this identity will be able can query the bid from private state or reveal the bid during the auction.
+
+The `bid.js` application also prints the bidID:
+```
+*** Result ***SAVE THIS VALUE*** BidID: 8ef83011a5fb791f75ed008337839426f6b87981519e5d58ef5ada39c3044edd
+```
+
+The BidID acts as the unique identifier for the bid. This ID allows you to query the bid using the `queryBid.js` program and add the bid to the auction. Save the bidID returned by the application as an environment variable in your terminal:
+```
+export BIDDER1_BID_ID=8ef83011a5fb791f75ed008337839426f6b87981519e5d58ef5ada39c3044edd
+```
+This value will be different for each transaction, so you will need to use the value returned in your terminal.
+
+Now that the bid has been created, you can submit the bid to the auction. Run the following command to submit the bid that was just created:
+```
+node submitBid.js org1 bidder1 PaintingAuction $BIDDER1_BID_ID
+```
+
+The hash of bid will be added to the list private bids in that have been submitted to `PaintingAuction`. Storing the hash in the public auction allows users to accurately reveal the bid after bidding is closed. The application will query the auction to verify that the bid was added:
+```
+*** Result: Auction: {
+ "objectType": "auction",
+ "item": "painting",
+ "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
+ "organizations": [
+ "Org1MSP"
+ ],
+ "privateBids": {
+ "\u0000bid\u0000PaintingAuction\u00008ef83011a5fb791f75ed008337839426f6b87981519e5d58ef5ada39c3044edd\u0000": {
+ "org": "Org1MSP",
+ "hash": "5cb50a17b5a21c02fc01306e3e9b54f4db67e9a440552ce898bbd7daa62dce0f"
+ }
+ },
+ "revealedBids": {},
+ "winner": "",
+ "price": 0,
+ "status": "open"
+}
+```
+
+### Bid as bidder2
+
+Let's submit another bid. Bidder2 would like to purchase the painting for 500 dollars.
+```
+node bid.js org1 bidder2 PaintingAuction 500
+```
+
+Save the Bid ID returned by the application:
+```
+export BIDDER2_BID_ID=915a908c8f2c368f4a3aedd73176656af81ddfab000b11629503403f3d97b185
+```
+
+Submit bidder2's bid to the auction:
+```
+node submitBid.js org1 bidder2 PaintingAuction $BIDDER2_BID_ID
+```
+
+### Bid as bidder3 from Org2
+
+Bidder3 will bid 700 dollars for the painting:
+```
+node bid.js org2 bidder3 PaintingAuction 700
+```
+
+Save the Bid ID returned by the application:
+```
+export BIDDER3_BID_ID=5e4e637c68833b178739575f6fe09820b019551a8cfbb43a4d172e0aa864dfad
+```
+
+Add bidder3's bid to the auction:
+```
+node submitBid.js org2 bidder3 PaintingAuction $BIDDER3_BID_ID
+```
+
+Because bidder3 belongs to Org2, submitting the bid will add Org2 to the list of participating organizations. You can see the Org2 MSP ID has been added to the list of `"organizations"` in the updated auction returned by the application:
+```
+*** Result: Auction: {
+ "objectType": "auction",
+ "item": "painting",
+ "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
+ "organizations": [
+ "Org1MSP",
+ "Org2MSP"
+ ],
+ "privateBids": {
+ "\u0000bid\u0000PaintingAuction\u00005e4e637c68833b178739575f6fe09820b019551a8cfbb43a4d172e0aa864dfad\u0000": {
+ "org": "Org2MSP",
+ "hash": "40107eab7a99dfc2f25d02b8ab840f12fd802a9f86d8d42b78d7b4409b2c15bd"
+ },
+ "\u0000bid\u0000PaintingAuction\u00008ef83011a5fb791f75ed008337839426f6b87981519e5d58ef5ada39c3044edd\u0000": {
+ "org": "Org1MSP",
+ "hash": "5cb50a17b5a21c02fc01306e3e9b54f4db67e9a440552ce898bbd7daa62dce0f"
+ },
+ "\u0000bid\u0000PaintingAuction\u0000915a908c8f2c368f4a3aedd73176656af81ddfab000b11629503403f3d97b185\u0000": {
+ "org": "Org1MSP",
+ "hash": "a458df18b12dffe4ae6d56a270134c2d55bd53fface034bd24381d0073d46a45"
+ }
+ },
+ "revealedBids": {},
+ "winner": "",
+ "price": 0,
+ "status": "open"
+}
+```
+
+Now that a bid from Org2 has been added to the auction, any updates to the auction need to be endorsed by the Org2 peer. The applications will use `"organizations"` field to specify which organizations need to endorse submitting a new bid, revealing a bid, or updating the auction status.
+
+### Bid as bidder4
+
+Bidder4 from Org2 would like to purchase the painting for 900 dollars:
+```
+node bid.js org2 bidder4 PaintingAuction 900
+```
+
+Save the Bid ID returned by the application:
+```
+export BIDDER4_BID_ID=49466271ae879bd009e75a60730a12bfa986e75f263202ab81ccd3deec544a35
+```
+
+Add bidder4's bid to the auction:
+```
+node submitBid.js org2 bidder4 PaintingAuction $BIDDER4_BID_ID
+```
+
+## Close the auction
+
+Now that all four bidders have joined the auction, the seller would like to close the auction and allow buyers to reveal their bids. The seller identity that created the auction needs to submit the transaction:
+```
+node closeAuction.js org1 seller PaintingAuction
+```
+
+The application will query the auction to allow you to verify that the auction status has changed to closed. As a test, you can try to create and submit a new bid to verify that no new bids can be added to the auction.
+
+## Reveal bids
+
+After the auction is closed, bidders can try to win the auction by revealing their bids. The transaction to reveal a bid needs to pass four checks:
+1. The auction is closed.
+2. The transaction was submitted by the identity that created the bid.
+3. The hash of the revealed bid matches the hash of the bid on the channel ledger. This confirms that the bid is the same as the bid that is stored in the private data collection.
+4. The hash of the revealed bid matches the hash that was submitted to the auction. This confirms that the bid was not altered after the auction was closed.
+
+Use the `revealBid.js` application to reveal the bid of Bidder1:
+```
+node revealBid.js org1 bidder1 PaintingAuction $BIDDER1_BID_ID
+```
+
+The full bid details, including the price, are now visible:
+```
+*** Result: Auction: {
+ "objectType": "auction",
+ "item": "painting",
+ "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
+ "organizations": [
+ "Org1MSP",
+ "Org2MSP"
+ ],
+ "privateBids": {
+ "\u0000bid\u0000PaintingAuction\u000049466271ae879bd009e75a60730a12bfa986e75f263202ab81ccd3deec544a35\u0000": {
+ "org": "Org2MSP",
+ "hash": "b8eaeb4422b93abdfe4ccb6aa11b745b3d1cb072a99bd3eb3618f081fb1b1f89"
+ },
+ "\u0000bid\u0000PaintingAuction\u00005e4e637c68833b178739575f6fe09820b019551a8cfbb43a4d172e0aa864dfad\u0000": {
+ "org": "Org2MSP",
+ "hash": "40107eab7a99dfc2f25d02b8ab840f12fd802a9f86d8d42b78d7b4409b2c15bd"
+ },
+ "\u0000bid\u0000PaintingAuction\u00008ef83011a5fb791f75ed008337839426f6b87981519e5d58ef5ada39c3044edd\u0000": {
+ "org": "Org1MSP",
+ "hash": "5cb50a17b5a21c02fc01306e3e9b54f4db67e9a440552ce898bbd7daa62dce0f"
+ },
+ "\u0000bid\u0000PaintingAuction\u0000915a908c8f2c368f4a3aedd73176656af81ddfab000b11629503403f3d97b185\u0000": {
+ "org": "Org1MSP",
+ "hash": "a458df18b12dffe4ae6d56a270134c2d55bd53fface034bd24381d0073d46a45"
+ }
+ },
+ "revealedBids": {
+ "\u0000bid\u0000PaintingAuction\u00008ef83011a5fb791f75ed008337839426f6b87981519e5d58ef5ada39c3044edd\u0000": {
+ "objectType": "bid",
+ "price": 800,
+ "org": "Org1MSP",
+ "bidder": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
+ }
+ },
+ "winner": "",
+ "price": 0,
+ "status": "closed"
+}
+```
+
+Bidder3 from Org2 will also reveal their bid:
+```
+node revealBid.js org2 bidder3 PaintingAuction $BIDDER3_BID_ID
+```
+
+If the auction ended now, Bidder1 would win. Let's try to end the auction using the seller identity and see what happens.
+
+```
+node endAuction.js org1 seller PaintingAuction
+```
+
+The output should look something like the following:
+
+```
+--> Submit the transaction to end the auction
+2020-11-06T13:16:11.591Z - warn: [TransactionEventHandler]: strategyFail: commit failure for transaction "99feade5b7ec223839200867b57d18971c3e9f923efc95aaeec720727f927366": TransactionError: Commit of transaction 99feade5b7ec223839200867b57d18971c3e9f923efc95aaeec720727f927366 failed on peer peer0.org1.example.com:7051 with status ENDORSEMENT_POLICY_FAILURE
+******** FAILED to submit bid: TransactionError: Commit of transaction 99feade5b7ec223839200867b57d18971c3e9f923efc95aaeec720727f927366 failed on peer peer0.org1.example.com:7051 with status ENDORSEMENT_POLICY_FAILURE
+```
+
+Instead of ending the auction, the transaction results in an endorsement policy failure. The end of the auction needs to be endorsed by Org2. Before endorsing the transaction, the Org2 peer queries its private data collection for any winning bids that have not yet been revealed. Because Bidder4 created a bid that is above the winning price, the Org2 peer refuses to endorse the transaction that would end the auction.
+
+Before we can end the auction, we need to reveal the bid from bidder4.
+```
+node revealBid.js org2 bidder4 PaintingAuction $BIDDER4_BID_ID
+```
+
+Bidder2 from Org1 would not win the auction in either case. As a result, Bidder2 decides not to reveal their bid.
+
+## End the auction
+
+Now that the winning bids have been revealed, we can end the auction:
+```
+node endAuction org1 seller PaintingAuction
+```
+
+The transaction was successfully endorsed by both Org1 and Org2, who both calculated the same price and winner. The winning bidder is listed along with the price:
+```
+*** Result: Auction: {
+ "objectType": "auction",
+ "item": "painting",
+ "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
+ "organizations": [
+ "Org1MSP",
+ "Org2MSP"
+ ],
+ "privateBids": {
+ "\u0000bid\u0000PaintingAuction\u000049466271ae879bd009e75a60730a12bfa986e75f263202ab81ccd3deec544a35\u0000": {
+ "org": "Org2MSP",
+ "hash": "b8eaeb4422b93abdfe4ccb6aa11b745b3d1cb072a99bd3eb3618f081fb1b1f89"
+ },
+ "\u0000bid\u0000PaintingAuction\u00005e4e637c68833b178739575f6fe09820b019551a8cfbb43a4d172e0aa864dfad\u0000": {
+ "org": "Org2MSP",
+ "hash": "40107eab7a99dfc2f25d02b8ab840f12fd802a9f86d8d42b78d7b4409b2c15bd"
+ },
+ "\u0000bid\u0000PaintingAuction\u00008ef83011a5fb791f75ed008337839426f6b87981519e5d58ef5ada39c3044edd\u0000": {
+ "org": "Org1MSP",
+ "hash": "5cb50a17b5a21c02fc01306e3e9b54f4db67e9a440552ce898bbd7daa62dce0f"
+ },
+ "\u0000bid\u0000PaintingAuction\u0000915a908c8f2c368f4a3aedd73176656af81ddfab000b11629503403f3d97b185\u0000": {
+ "org": "Org1MSP",
+ "hash": "a458df18b12dffe4ae6d56a270134c2d55bd53fface034bd24381d0073d46a45"
+ }
+ },
+ "revealedBids": {
+ "\u0000bid\u0000PaintingAuction\u000049466271ae879bd009e75a60730a12bfa986e75f263202ab81ccd3deec544a35\u0000": {
+ "objectType": "bid",
+ "price": 900,
+ "org": "Org2MSP",
+ "bidder": "eDUwOTo6Q049YmlkZGVyNCxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL"
+ },
+ "\u0000bid\u0000PaintingAuction\u00005e4e637c68833b178739575f6fe09820b019551a8cfbb43a4d172e0aa864dfad\u0000": {
+ "objectType": "bid",
+ "price": 700,
+ "org": "Org2MSP",
+ "bidder": "eDUwOTo6Q049YmlkZGVyMyxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL"
+ },
+ "\u0000bid\u0000PaintingAuction\u00008ef83011a5fb791f75ed008337839426f6b87981519e5d58ef5ada39c3044edd\u0000": {
+ "objectType": "bid",
+ "price": 800,
+ "org": "Org1MSP",
+ "bidder": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
+ }
+ },
+ "winner": "eDUwOTo6Q049YmlkZGVyNCxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL",
+ "price": 900,
+ "status": "ended"
+}
+```
+
+## Clean up
+
+When your are done using the auction smart contract, you can bring down the network and clean up the environment. In the `auction/application-javascript` directory, run the following command to remove the wallets used to run the applications:
+```
+rm -rf wallet
+```
+
+You can then navigate to the test network directory and bring down the network:
+````
+cd ../../test-network/
+./network.sh down
+````
diff --git a/auction/application-javascript/bid.js b/auction/application-javascript/bid.js
new file mode 100644
index 0000000..a5540d8
--- /dev/null
+++ b/auction/application-javascript/bid.js
@@ -0,0 +1,112 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'auction';
+
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function bid(ccp,wallet,user,orgMSP,auctionID,price) {
+ try {
+
+ const gateway = new Gateway();
+ //connect using Discovery enabled
+
+ await gateway.connect(ccp,
+ { wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
+
+ const network = await gateway.getNetwork(myChannel);
+ const contract = network.getContract(myChaincodeName);
+
+ console.log('\n--> Evaluate Transaction: get your client ID');
+ let bidder = await contract.evaluateTransaction('GetID');
+ console.log('*** Result: Bidder ID is ' + bidder.toString());
+
+ let bidData = { objectType: 'bid', price: parseInt(price), org: orgMSP, bidder: bidder.toString()};
+
+ let statefulTxn = contract.createTransaction('Bid');
+ statefulTxn.setEndorsingOrganizations(orgMSP);
+ let tmapData = Buffer.from(JSON.stringify(bidData));
+ statefulTxn.setTransient({
+ bid: tmapData
+ });
+
+ let bidID = statefulTxn.getTransactionId();
+
+ console.log('\n--> Submit Transaction: Create the bid that is stored in your organization\'s private data collection');
+ await statefulTxn.submit(auctionID);
+ console.log('*** Result: committed');
+ console.log('*** Result ***SAVE THIS VALUE*** BidID: ' + bidID.toString());
+
+ console.log('\n--> Evaluate Transaction: read the bid that was just created');
+ let result = await contract.evaluateTransaction('QueryBid',auctionID,bidID);
+ console.log('*** Result: Bid: ' + prettyJSONString(result.toString()));
+
+ gateway.disconnect();
+ } catch (error) {
+ console.error(`******** FAILED to submit bid: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+}
+
+async function main() {
+ try {
+
+ if (process.argv[2] == undefined || process.argv[3] == undefined
+ || process.argv[4] == undefined || process.argv[5] == undefined) {
+ console.log("Usage: node bid.js org userID auctionID price");
+ process.exit(1);
+ }
+
+ const org = process.argv[2]
+ const user = process.argv[3];
+ const auctionID = process.argv[4];
+ const price = process.argv[5];
+
+ if (org == 'Org1' || org == 'org1') {
+
+ const orgMSP = 'Org1MSP';
+ const ccp = buildCCPOrg1();
+ const walletPath = path.join(__dirname, 'wallet/org1');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await bid(ccp,wallet,user,orgMSP,auctionID,price);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+
+ const orgMSP = 'Org2MSP';
+ const ccp = buildCCPOrg2();
+ const walletPath = path.join(__dirname, 'wallet/org2');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await bid(ccp,wallet,user,orgMSP,auctionID,price);
+ } else {
+ console.log("Usage: node bid.js org userID auctionID price");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ process.exit(1);
+ }
+}
+
+
+main();
diff --git a/auction/application-javascript/closeAuction.js b/auction/application-javascript/closeAuction.js
new file mode 100644
index 0000000..7934fbf
--- /dev/null
+++ b/auction/application-javascript/closeAuction.js
@@ -0,0 +1,109 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'auction';
+
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function closeAuction(ccp,wallet,user,auctionID) {
+ try {
+
+ const gateway = new Gateway();
+ //connect using Discovery enabled
+
+ await gateway.connect(ccp,
+ { wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
+
+ const network = await gateway.getNetwork(myChannel);
+ const contract = network.getContract(myChaincodeName);
+
+ // Query the auction to get the list of endorsing orgs.
+ //console.log('\n--> Evaluate Transaction: query the auction you want to close');
+ let auctionString = await contract.evaluateTransaction('QueryAuction',auctionID);
+ //console.log('*** Result: Bid: ' + prettyJSONString(auctionString.toString()));
+ var auctionJSON = JSON.parse(auctionString);
+
+ let statefulTxn = contract.createTransaction('CloseAuction');
+
+ if (auctionJSON.organizations.length == 2) {
+ statefulTxn.setEndorsingOrganizations(auctionJSON.organizations[0],auctionJSON.organizations[1]);
+ } else {
+ statefulTxn.setEndorsingOrganizations(auctionJSON.organizations[0]);
+ }
+
+ console.log('\n--> Submit Transaction: close auction');
+ await statefulTxn.submit(auctionID);
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: query the updated auction');
+ let result = await contract.evaluateTransaction('QueryAuction',auctionID);
+ console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
+
+ gateway.disconnect();
+ } catch (error) {
+ console.error(`******** FAILED to submit bid: ${error}`);
+ process.exit(1);
+ }
+}
+
+async function main() {
+ try {
+
+ if (process.argv[2] == undefined || process.argv[3] == undefined
+ || process.argv[4] == undefined) {
+ console.log("Usage: node closeAuction.js org userID auctionID");
+ process.exit(1);
+ }
+
+ const org = process.argv[2]
+ const user = process.argv[3];
+ const auctionID = process.argv[4];
+
+ if (org == 'Org1' || org == 'org1') {
+
+ const orgMSP = 'Org1MSP';
+ const ccp = buildCCPOrg1();
+ const walletPath = path.join(__dirname, 'wallet/org1');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await closeAuction(ccp,wallet,user,auctionID);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+
+ const orgMSP = 'Org2MSP';
+ const ccp = buildCCPOrg2();
+ const walletPath = path.join(__dirname, 'wallet/org2');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await closeAuction(ccp,wallet,user,auctionID);
+ } else {
+ console.log("Usage: node closeAuction.js org userID auctionID ");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+}
+
+
+main();
diff --git a/auction/application-javascript/createAuction.js b/auction/application-javascript/createAuction.js
new file mode 100644
index 0000000..8d083d7
--- /dev/null
+++ b/auction/application-javascript/createAuction.js
@@ -0,0 +1,93 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'auction';
+
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function createAuction(ccp,wallet,user,auctionID,item) {
+ try {
+
+ const gateway = new Gateway();
+ //connect using Discovery enabled
+
+ await gateway.connect(ccp,
+ { wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
+
+ const network = await gateway.getNetwork(myChannel);
+ const contract = network.getContract(myChaincodeName);
+
+ let statefulTxn = contract.createTransaction('CreateAuction');
+
+ console.log('\n--> Submit Transaction: Propose a new auction');
+ await statefulTxn.submit(auctionID,item);
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: query the auction that was just created');
+ let result = await contract.evaluateTransaction('QueryAuction',auctionID);
+ console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
+
+ gateway.disconnect();
+ } catch (error) {
+ console.error(`******** FAILED to submit bid: ${error}`);
+ }
+}
+
+async function main() {
+ try {
+
+ if (process.argv[2] == undefined || process.argv[3] == undefined
+ || process.argv[4] == undefined || process.argv[5] == undefined) {
+ console.log("Usage: node createAuction.js org userID auctionID item");
+ process.exit(1);
+ }
+
+ const org = process.argv[2]
+ const user = process.argv[3];
+ const auctionID = process.argv[4];
+ const item = process.argv[5];
+
+ if (org == 'Org1' || org == 'org1') {
+
+ const orgMSP = 'Org1MSP';
+ const ccp = buildCCPOrg1();
+ const walletPath = path.join(__dirname, 'wallet/org1');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await createAuction(ccp,wallet,user,auctionID,item);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+
+ const orgMSP = 'Org2MSP';
+ const ccp = buildCCPOrg2();
+ const walletPath = path.join(__dirname, 'wallet/org2');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await createAuction(ccp,wallet,user,auctionID,item);
+ } else {
+ console.log("Usage: node createAuction.js org userID auctionID item");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ }
+}
+
+
+main();
diff --git a/auction/application-javascript/endAuction.js b/auction/application-javascript/endAuction.js
new file mode 100644
index 0000000..3c16c7f
--- /dev/null
+++ b/auction/application-javascript/endAuction.js
@@ -0,0 +1,109 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'auction';
+
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function endAuction(ccp,wallet,user,auctionID) {
+ try {
+
+ const gateway = new Gateway();
+ //connect using Discovery enabled
+
+ await gateway.connect(ccp,
+ { wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
+
+ const network = await gateway.getNetwork(myChannel);
+ const contract = network.getContract(myChaincodeName);
+
+ // Query the auction to get the list of endorsing orgs.
+ //console.log('\n--> Evaluate Transaction: query the auction you want to end');
+ let auctionString = await contract.evaluateTransaction('QueryAuction',auctionID);
+ //console.log('*** Result: Bid: ' + prettyJSONString(auctionString.toString()));
+ var auctionJSON = JSON.parse(auctionString);
+
+ let statefulTxn = contract.createTransaction('EndAuction');
+
+ if (auctionJSON.organizations.length == 2) {
+ statefulTxn.setEndorsingOrganizations(auctionJSON.organizations[0],auctionJSON.organizations[1]);
+ } else {
+ statefulTxn.setEndorsingOrganizations(auctionJSON.organizations[0]);
+ }
+
+ console.log('\n--> Submit the transaction to end the auction');
+ await statefulTxn.submit(auctionID);
+ console.log('*** Result: committed');
+
+ console.log('\n--> Evaluate Transaction: query the updated auction');
+ let result = await contract.evaluateTransaction('QueryAuction',auctionID);
+ console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
+
+ gateway.disconnect();
+ } catch (error) {
+ console.error(`******** FAILED to submit bid: ${error}`);
+ process.exit(1);
+ }
+}
+
+async function main() {
+ try {
+
+ if (process.argv[2] == undefined || process.argv[3] == undefined
+ || process.argv[4] == undefined) {
+ console.log("Usage: node endAuction.js org userID auctionID");
+ process.exit(1);
+ }
+
+ const org = process.argv[2]
+ const user = process.argv[3];
+ const auctionID = process.argv[4];
+
+ if (org == 'Org1' || org == 'org1') {
+
+ const orgMSP = 'Org1MSP';
+ const ccp = buildCCPOrg1();
+ const walletPath = path.join(__dirname, 'wallet/org1');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await endAuction(ccp,wallet,user,auctionID);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+
+ const orgMSP = 'Org2MSP';
+ const ccp = buildCCPOrg2();
+ const walletPath = path.join(__dirname, 'wallet/org2');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await endAuction(ccp,wallet,user,auctionID);
+ } else {
+ console.log("Usage: node endAuction.js org userID auctionID");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+}
+
+
+main();
diff --git a/auction/application-javascript/enrollAdmin.js b/auction/application-javascript/enrollAdmin.js
new file mode 100644
index 0000000..e2df9cf
--- /dev/null
+++ b/auction/application-javascript/enrollAdmin.js
@@ -0,0 +1,76 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Wallets } = require('fabric-network');
+const FabricCAServices = require('fabric-ca-client');
+const path = require('path');
+const { buildCAClient, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const mspOrg1 = 'Org1MSP';
+const mspOrg2 = 'Org2MSP';
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function connectToOrg1CA() {
+ console.log('\n--> Enrolling the Org1 CA admin');
+ const ccpOrg1 = buildCCPOrg1();
+ const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
+
+ const walletPathOrg1 = path.join(__dirname, 'wallet/org1');
+ const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
+
+ await enrollAdmin(caOrg1Client, walletOrg1, mspOrg1);
+
+}
+
+async function connectToOrg2CA() {
+ console.log('\n--> Enrolling the Org2 CA admin');
+ const ccpOrg2 = buildCCPOrg2();
+ const caOrg2Client = buildCAClient(FabricCAServices, ccpOrg2, 'ca.org2.example.com');
+
+ const walletPathOrg2 = path.join(__dirname, 'wallet/org2');
+ const walletOrg2 = await buildWallet(Wallets, walletPathOrg2);
+
+ await enrollAdmin(caOrg2Client, walletOrg2, mspOrg2);
+
+}
+async function main() {
+
+ if (process.argv[2] == undefined) {
+ console.log("Usage: node enrollAdmin.js Org");
+ process.exit(1);
+ }
+
+ const org = process.argv[2];
+
+ try {
+
+ if (org == 'Org1' || org == 'org1') {
+ await connectToOrg1CA();
+ }
+ else if (org == 'Org2' || org == 'org2') {
+ await connectToOrg2CA();
+ } else {
+ console.log("Usage: node registerUser.js org userID");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`Error in enrolling admin: ${error}`);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/auction/application-javascript/package.json b/auction/application-javascript/package.json
new file mode 100644
index 0000000..4a410a6
--- /dev/null
+++ b/auction/application-javascript/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "auction",
+ "version": "1.0.0",
+ "description": "auction application implemented in JavaScript",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=5"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.4",
+ "fabric-network": "^2.2.4"
+ }
+}
diff --git a/auction/application-javascript/queryAuction.js b/auction/application-javascript/queryAuction.js
new file mode 100644
index 0000000..258d19c
--- /dev/null
+++ b/auction/application-javascript/queryAuction.js
@@ -0,0 +1,86 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'auction';
+
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function queryAuction(ccp,wallet,user,auctionID) {
+ try {
+
+ const gateway = new Gateway();
+ //connect using Discovery enabled
+
+ await gateway.connect(ccp,
+ { wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
+
+ const network = await gateway.getNetwork(myChannel);
+ const contract = network.getContract(myChaincodeName);
+
+ console.log('\n--> Evaluate Transaction: query the auction');
+ let result = await contract.evaluateTransaction('QueryAuction',auctionID);
+ console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
+
+ gateway.disconnect();
+ } catch (error) {
+ console.error(`******** FAILED to submit bid: ${error}`);
+ }
+}
+
+async function main() {
+ try {
+
+ if (process.argv[2] == undefined || process.argv[3] == undefined
+ || process.argv[4] == undefined) {
+ console.log("Usage: node queryAuction.js org userID auctionID");
+ process.exit(1);
+ }
+
+ const org = process.argv[2]
+ const user = process.argv[3];
+ const auctionID = process.argv[4];
+
+ if (org == 'Org1' || org == 'org1') {
+
+ const orgMSP = 'Org1MSP';
+ const ccp = buildCCPOrg1();
+ const walletPath = path.join(__dirname, 'wallet/org1');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await queryAuction(ccp,wallet,user,auctionID);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+
+ const orgMSP = 'Org2MSP';
+ const ccp = buildCCPOrg2();
+ const walletPath = path.join(__dirname, 'wallet/org2');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await queryAuction(ccp,wallet,user,auctionID);
+ } else {
+ console.log("Usage: node queryAuction.js org userID auctionID");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ }
+}
+
+
+main();
diff --git a/auction/application-javascript/queryBid.js b/auction/application-javascript/queryBid.js
new file mode 100644
index 0000000..b4ffea6
--- /dev/null
+++ b/auction/application-javascript/queryBid.js
@@ -0,0 +1,87 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'auction';
+
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function queryBid(ccp,wallet,user,auctionID,bidID) {
+ try {
+
+ const gateway = new Gateway();
+ //connect using Discovery enabled
+
+ await gateway.connect(ccp,
+ { wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
+
+ const network = await gateway.getNetwork(myChannel);
+ const contract = network.getContract(myChaincodeName);
+
+ console.log('\n--> Evaluate Transaction: read bid from private data store');
+ let result = await contract.evaluateTransaction('QueryBid',auctionID,bidID);
+ console.log('*** Result: Bid: ' + prettyJSONString(result.toString()));
+
+ gateway.disconnect();
+ } catch (error) {
+ console.error(`******** FAILED to submit bid: ${error}`);
+ }
+}
+
+async function main() {
+ try {
+
+ if (process.argv[2] == undefined || process.argv[3] == undefined
+ || process.argv[4] == undefined || process.argv[5] == undefined) {
+ console.log("Usage: node bid.js org userID auctionID bidID");
+ process.exit(1);
+ }
+
+ const org = process.argv[2]
+ const user = process.argv[3];
+ const auctionID = process.argv[4];
+ const bidID = process.argv[5];
+
+ if (org == 'Org1' || org == 'org1') {
+
+ const orgMSP = 'Org1MSP';
+ const ccp = buildCCPOrg1();
+ const walletPath = path.join(__dirname, 'wallet/org1');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await queryBid(ccp,wallet,user,auctionID,bidID);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+
+ const orgMSP = 'Org2MSP';
+ const ccp = buildCCPOrg2();
+ const walletPath = path.join(__dirname, 'wallet/org2');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await queryBid(ccp,wallet,user,auctionID,bidID);
+ } else {
+ console.log("Usage: node bid.js org userID auctionID bidID");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ }
+}
+
+
+main();
diff --git a/auction/application-javascript/registerEnrollUser.js b/auction/application-javascript/registerEnrollUser.js
new file mode 100644
index 0000000..3ee97a6
--- /dev/null
+++ b/auction/application-javascript/registerEnrollUser.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Wallets } = require('fabric-network');
+const FabricCAServices = require('fabric-ca-client');
+const path = require('path');
+const { buildCAClient, registerAndEnrollUser } = require('../../test-application/javascript/CAUtil.js');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const mspOrg1 = 'Org1MSP';
+const mspOrg2 = 'Org2MSP';
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function connectToOrg1CA(UserID) {
+ console.log('\n--> Register and enrolling new user');
+ const ccpOrg1 = buildCCPOrg1();
+ const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
+
+ const walletPathOrg1 = path.join(__dirname, 'wallet/org1');
+ const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
+
+ await registerAndEnrollUser(caOrg1Client, walletOrg1, mspOrg1, UserID, 'org1.department1');
+
+}
+
+async function connectToOrg2CA(UserID) {
+ console.log('\n--> Register and enrolling new user');
+ const ccpOrg2 = buildCCPOrg2();
+ const caOrg2Client = buildCAClient(FabricCAServices, ccpOrg2, 'ca.org2.example.com');
+
+ const walletPathOrg2 = path.join(__dirname, 'wallet/org2');
+ const walletOrg2 = await buildWallet(Wallets, walletPathOrg2);
+
+ await registerAndEnrollUser(caOrg2Client, walletOrg2, mspOrg2, UserID, 'org2.department1');
+
+}
+async function main() {
+
+ if (process.argv[2] == undefined && process.argv[3] == undefined) {
+ console.log("Usage: node registerEnrollUser.js org userID");
+ process.exit(1);
+ }
+
+ const org = process.argv[2];
+ const userId = process.argv[3];
+
+ try {
+
+ if (org == 'Org1' || org == 'org1') {
+ await connectToOrg1CA(userId);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+ await connectToOrg2CA(userId);
+ } else {
+ console.log("Usage: node registerEnrollUser.js org userID");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`Error in enrolling admin: ${error}`);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/auction/application-javascript/revealBid.js b/auction/application-javascript/revealBid.js
new file mode 100644
index 0000000..8b39743
--- /dev/null
+++ b/auction/application-javascript/revealBid.js
@@ -0,0 +1,119 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'auction';
+
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function addBid(ccp,wallet,user,auctionID,bidID) {
+ try {
+
+ const gateway = new Gateway();
+ //connect using Discovery enabled
+
+ await gateway.connect(ccp,
+ { wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
+
+ const network = await gateway.getNetwork(myChannel);
+ const contract = network.getContract(myChaincodeName);
+
+ console.log('\n--> Evaluate Transaction: read your bid');
+ let bidString = await contract.evaluateTransaction('QueryBid',auctionID,bidID);
+ var bidJSON = JSON.parse(bidString);
+
+ //console.log('\n--> Evaluate Transaction: query the auction you want to join');
+ let auctionString = await contract.evaluateTransaction('QueryAuction',auctionID);
+ // console.log('*** Result: Bid: ' + prettyJSONString(auctionString.toString()));
+ var auctionJSON = JSON.parse(auctionString);
+
+ let bidData = { objectType: 'bid', price: parseInt(bidJSON.price), org: bidJSON.org, bidder: bidJSON.bidder};
+ console.log('*** Result: Bid: ' + JSON.stringify(bidData,null,2));
+
+ let statefulTxn = contract.createTransaction('RevealBid');
+ let tmapData = Buffer.from(JSON.stringify(bidData));
+ statefulTxn.setTransient({
+ bid: tmapData
+ });
+
+ if (auctionJSON.organizations.length == 2) {
+ statefulTxn.setEndorsingOrganizations(auctionJSON.organizations[0],auctionJSON.organizations[1]);
+ } else {
+ statefulTxn.setEndorsingOrganizations(auctionJSON.organizations[0]);
+ }
+
+ await statefulTxn.submit(auctionID,bidID);
+
+ console.log('\n--> Evaluate Transaction: query the auction to see that our bid was added');
+ let result = await contract.evaluateTransaction('QueryAuction',auctionID);
+ console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
+
+ gateway.disconnect();
+ } catch (error) {
+ console.error(`******** FAILED to submit bid: ${error}`);
+ process.exit(1);
+ }
+}
+
+async function main() {
+ try {
+
+ if (process.argv[2] == undefined || process.argv[3] == undefined
+ || process.argv[4] == undefined || process.argv[5] == undefined) {
+ console.log("Usage: node revealBid.js org userID auctionID bidID");
+ process.exit(1);
+ }
+
+ const org = process.argv[2]
+ const user = process.argv[3];
+ const auctionID = process.argv[4];
+ const bidID = process.argv[5];
+
+ if (org == 'Org1' || org == 'org1') {
+
+ const orgMSP = 'Org1MSP';
+ const ccp = buildCCPOrg1();
+ const walletPath = path.join(__dirname, 'wallet/org1');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await addBid(ccp,wallet,user,auctionID,bidID);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+
+ const orgMSP = 'Org2MSP';
+ const ccp = buildCCPOrg2();
+ const walletPath = path.join(__dirname, 'wallet/org2');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await addBid(ccp,wallet,user,auctionID,bidID);
+ }
+ else {
+ console.log("Usage: node revealBid.js org userID auctionID bidID");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+}
+
+
+main();
diff --git a/auction/application-javascript/submitBid.js b/auction/application-javascript/submitBid.js
new file mode 100644
index 0000000..ef6cf2a
--- /dev/null
+++ b/auction/application-javascript/submitBid.js
@@ -0,0 +1,108 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
+
+const myChannel = 'mychannel';
+const myChaincodeName = 'auction';
+
+
+function prettyJSONString(inputString) {
+ if (inputString) {
+ return JSON.stringify(JSON.parse(inputString), null, 2);
+ }
+ else {
+ return inputString;
+ }
+}
+
+async function submitBid(ccp,wallet,user,auctionID,bidID) {
+ try {
+
+ const gateway = new Gateway();
+ //connect using Discovery enabled
+
+ await gateway.connect(ccp,
+ { wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
+
+ const network = await gateway.getNetwork(myChannel);
+ const contract = network.getContract(myChaincodeName);
+
+ console.log('\n--> Evaluate Transaction: query the auction you want to join');
+ let auctionString = await contract.evaluateTransaction('QueryAuction',auctionID);
+ var auctionJSON = JSON.parse(auctionString);
+
+ let statefulTxn = contract.createTransaction('SubmitBid');
+
+ if (auctionJSON.organizations.length == 2) {
+ statefulTxn.setEndorsingOrganizations(auctionJSON.organizations[0],auctionJSON.organizations[1]);
+ } else {
+ statefulTxn.setEndorsingOrganizations(auctionJSON.organizations[0]);
+ }
+
+ console.log('\n--> Submit Transaction: add bid to the auction');
+ await statefulTxn.submit(auctionID,bidID);
+
+ console.log('\n--> Evaluate Transaction: query the auction to see that our bid was added');
+ let result = await contract.evaluateTransaction('QueryAuction',auctionID);
+ console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
+
+ gateway.disconnect();
+ } catch (error) {
+ console.error(`******** FAILED to submit bid: ${error}`);
+ process.exit(1);
+ }
+}
+
+async function main() {
+ try {
+
+ if (process.argv[2] == undefined || process.argv[3] == undefined
+ || process.argv[4] == undefined || process.argv[5] == undefined) {
+ console.log("Usage: node submitBid.js org userID auctionID bidID");
+ process.exit(1);
+ }
+
+ const org = process.argv[2]
+ const user = process.argv[3];
+ const auctionID = process.argv[4];
+ const bidID = process.argv[5];
+
+ if (org == 'Org1' || org == 'org1') {
+
+ const orgMSP = 'Org1MSP';
+ const ccp = buildCCPOrg1();
+ const walletPath = path.join(__dirname, 'wallet/org1');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await submitBid(ccp,wallet,user,auctionID,bidID);
+ }
+ else if (org == 'Org2' || org == 'org2') {
+
+ const orgMSP = 'Org2MSP';
+ const ccp = buildCCPOrg2();
+ const walletPath = path.join(__dirname, 'wallet/org2');
+ const wallet = await buildWallet(Wallets, walletPath);
+ await submitBid(ccp,wallet,user,auctionID,bidID);
+ }
+ else {
+ console.log("Usage: node submitBid.js org userID auctionID bidID");
+ console.log("Org must be Org1 or Org2");
+ }
+ } catch (error) {
+ console.error(`******** FAILED to run the application: ${error}`);
+ if (error.stack) {
+ console.error(error.stack);
+ }
+ process.exit(1);
+ }
+}
+
+
+main();
diff --git a/auction/chaincode-go/go.mod b/auction/chaincode-go/go.mod
new file mode 100644
index 0000000..26f43af
--- /dev/null
+++ b/auction/chaincode-go/go.mod
@@ -0,0 +1,8 @@
+module github.com/hyperledger/fabric-samples/auction/chaincode-go
+
+go 1.15
+
+require (
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20200728190242-9b3ae92d8664
+ github.com/hyperledger/fabric-contract-api-go v1.1.0
+)
diff --git a/auction/chaincode-go/go.sum b/auction/chaincode-go/go.sum
new file mode 100644
index 0000000..f052c52
--- /dev/null
+++ b/auction/chaincode-go/go.sum
@@ -0,0 +1,139 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200728190242-9b3ae92d8664 h1:Pu/9SNpo71SJj5DGehCXOKD9QGQ3MsuWjpsLM9Mkdwg=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200728190242-9b3ae92d8664/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/auction/chaincode-go/smart-contract/auction.go b/auction/chaincode-go/smart-contract/auction.go
new file mode 100644
index 0000000..c197e7e
--- /dev/null
+++ b/auction/chaincode-go/smart-contract/auction.go
@@ -0,0 +1,487 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package auction
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "encoding/json"
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+type SmartContract struct {
+ contractapi.Contract
+}
+
+// Auction data
+type Auction struct {
+ Type string `json:"objectType"`
+ ItemSold string `json:"item"`
+ Seller string `json:"seller"`
+ Orgs []string `json:"organizations"`
+ PrivateBids map[string]BidHash `json:"privateBids"`
+ RevealedBids map[string]FullBid `json:"revealedBids"`
+ Winner string `json:"winner"`
+ Price int `json:"price"`
+ Status string `json:"status"`
+}
+
+// FullBid is the structure of a revealed bid
+type FullBid struct {
+ Type string `json:"objectType"`
+ Price int `json:"price"`
+ Org string `json:"org"`
+ Bidder string `json:"bidder"`
+}
+
+// BidHash is the structure of a private bid
+type BidHash struct {
+ Org string `json:"org"`
+ Hash string `json:"hash"`
+}
+
+const bidKeyType = "bid"
+
+// CreateAuction creates on auction on the public channel. The identity that
+// submits the transacion becomes the seller of the auction
+func (s *SmartContract) CreateAuction(ctx contractapi.TransactionContextInterface, auctionID string, itemsold string) error {
+
+ // get ID of submitting client
+ clientID, err := ctx.GetClientIdentity().GetID()
+ if err != nil {
+ return fmt.Errorf("failed to get client identity %v", err)
+ }
+
+ // get org of submitting client
+ clientOrgID, err := ctx.GetClientIdentity().GetMSPID()
+ if err != nil {
+ return fmt.Errorf("failed to get client identity %v", err)
+ }
+
+ // Create auction
+ bidders := make(map[string]BidHash)
+ revealedBids := make(map[string]FullBid)
+
+ auction := Auction{
+ Type: "auction",
+ ItemSold: itemsold,
+ Price: 0,
+ Seller: clientID,
+ Orgs: []string{clientOrgID},
+ PrivateBids: bidders,
+ RevealedBids: revealedBids,
+ Winner: "",
+ Status: "open",
+ }
+
+ auctionBytes, err := json.Marshal(auction)
+ if err != nil {
+ return err
+ }
+
+ // put auction into state
+ err = ctx.GetStub().PutState(auctionID, auctionBytes)
+ if err != nil {
+ return fmt.Errorf("failed to put auction in public data: %v", err)
+ }
+
+ // set the seller of the auction as an endorser
+ err = setAssetStateBasedEndorsement(ctx, auctionID, clientOrgID)
+ if err != nil {
+ return fmt.Errorf("failed setting state based endorsement for new organization: %v", err)
+ }
+
+ return nil
+}
+
+// Bid is used to add a user's bid to the auction. The bid is stored in the private
+// data collection on the peer of the bidder's organization. The function returns
+// the transaction ID so that users can identify and query their bid
+func (s *SmartContract) Bid(ctx contractapi.TransactionContextInterface, auctionID string) (string, error) {
+
+ // get bid from transient map
+ transientMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return "", fmt.Errorf("error getting transient: %v", err)
+ }
+
+ BidJSON, ok := transientMap["bid"]
+ if !ok {
+ return "", fmt.Errorf("bid key not found in the transient map")
+ }
+
+ // get the implicit collection name using the bidder's organization ID
+ collection, err := getCollectionName(ctx)
+ if err != nil {
+ return "", fmt.Errorf("failed to get implicit collection name: %v", err)
+ }
+
+ // the bidder has to target their peer to store the bid
+ err = verifyClientOrgMatchesPeerOrg(ctx)
+ if err != nil {
+ return "", fmt.Errorf("Cannot store bid on this peer, not a member of this org: Error %v", err)
+ }
+
+ // the transaction ID is used as a unique index for the bid
+ txID := ctx.GetStub().GetTxID()
+
+ // create a composite key using the transaction ID
+ bidKey, err := ctx.GetStub().CreateCompositeKey(bidKeyType, []string{auctionID, txID})
+ if err != nil {
+ return "", fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ // put the bid into the organization's implicit data collection
+ err = ctx.GetStub().PutPrivateData(collection, bidKey, BidJSON)
+ if err != nil {
+ return "", fmt.Errorf("failed to input price into collection: %v", err)
+ }
+
+ // return the trannsaction ID so that the uset can identify their bid
+ return txID, nil
+}
+
+// SubmitBid is used by the bidder to add the hash of that bid stored in private data to the
+// auction. Note that this function alters the auction in private state, and needs
+// to meet the auction endorsement policy. Transaction ID is used identify the bid
+func (s *SmartContract) SubmitBid(ctx contractapi.TransactionContextInterface, auctionID string, txID string) error {
+
+ // get the MSP ID of the bidder's org
+ clientOrgID, err := ctx.GetClientIdentity().GetMSPID()
+ if err != nil {
+ return fmt.Errorf("failed to get client MSP ID: %v", err)
+ }
+
+ // get the auction from state
+ auctionBytes, err := ctx.GetStub().GetState(auctionID)
+ var auctionJSON Auction
+
+ if auctionBytes == nil {
+ return fmt.Errorf("Auction not found: %v", auctionID)
+ }
+ err = json.Unmarshal(auctionBytes, &auctionJSON)
+ if err != nil {
+ return fmt.Errorf("failed to create auction object JSON: %v", err)
+ }
+
+ // the auction needs to be open for users to add their bid
+ Status := auctionJSON.Status
+ if Status != "open" {
+ return fmt.Errorf("cannot join closed or ended auction")
+ }
+
+ // get the inplicit collection name of bidder's org
+ collection, err := getCollectionName(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to get implicit collection name: %v", err)
+ }
+
+ // use the transaction ID passed as a parameter to create composite bid key
+ bidKey, err := ctx.GetStub().CreateCompositeKey(bidKeyType, []string{auctionID, txID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ // get the hash of the bid stored in private data collection
+ bidHash, err := ctx.GetStub().GetPrivateDataHash(collection, bidKey)
+ if err != nil {
+ return fmt.Errorf("failed to read bid bash from collection: %v", err)
+ }
+ if bidHash == nil {
+ return fmt.Errorf("bid hash does not exist: %s", bidKey)
+ }
+
+ // store the hash along with the bidder's organization
+ NewHash := BidHash{
+ Org: clientOrgID,
+ Hash: fmt.Sprintf("%x", bidHash),
+ }
+
+ bidders := make(map[string]BidHash)
+ bidders = auctionJSON.PrivateBids
+ bidders[bidKey] = NewHash
+ auctionJSON.PrivateBids = bidders
+
+ // Add the bidding organization to the list of participating organizations if it is not already
+ Orgs := auctionJSON.Orgs
+ if !(contains(Orgs, clientOrgID)) {
+ newOrgs := append(Orgs, clientOrgID)
+ auctionJSON.Orgs = newOrgs
+
+ err = addAssetStateBasedEndorsement(ctx, auctionID, clientOrgID)
+ if err != nil {
+ return fmt.Errorf("failed setting state based endorsement for new organization: %v", err)
+ }
+ }
+
+ newAuctionBytes, _ := json.Marshal(auctionJSON)
+
+ err = ctx.GetStub().PutState(auctionID, newAuctionBytes)
+ if err != nil {
+ return fmt.Errorf("failed to update auction: %v", err)
+ }
+
+ return nil
+}
+
+// RevealBid is used by a bidder to reveal their bid after the auction is closed
+func (s *SmartContract) RevealBid(ctx contractapi.TransactionContextInterface, auctionID string, txID string) error {
+
+ // get bid from transient map
+ transientMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("error getting transient: %v", err)
+ }
+
+ transientBidJSON, ok := transientMap["bid"]
+ if !ok {
+ return fmt.Errorf("bid key not found in the transient map")
+ }
+
+ // get implicit collection name of organization ID
+ collection, err := getCollectionName(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to get implicit collection name: %v", err)
+ }
+
+ // use transaction ID to create composit bid key
+ bidKey, err := ctx.GetStub().CreateCompositeKey(bidKeyType, []string{auctionID, txID})
+ if err != nil {
+ return fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ // get bid hash of bid if private bid on the public ledger
+ bidHash, err := ctx.GetStub().GetPrivateDataHash(collection, bidKey)
+ if err != nil {
+ return fmt.Errorf("failed to read bid bash from collection: %v", err)
+ }
+ if bidHash == nil {
+ return fmt.Errorf("bid hash does not exist: %s", bidKey)
+ }
+
+ // get auction from public state
+ auctionBytes, err := ctx.GetStub().GetState(auctionID)
+ if err != nil {
+ return fmt.Errorf("failed to get auction %v: %v", auctionID, err)
+ }
+ if auctionBytes == nil {
+ return fmt.Errorf("Auction interest object %v not found", auctionID)
+ }
+
+ var auctionJSON Auction
+ err = json.Unmarshal(auctionBytes, &auctionJSON)
+ if err != nil {
+ return fmt.Errorf("failed to create auction object JSON: %v", err)
+ }
+
+ // Complete a series of three checks before we add the bid to the auction
+
+ // check 1: check that the auction is closed. We cannot reveal a
+ // bid to an open auction
+ Status := auctionJSON.Status
+ if Status != "closed" {
+ return fmt.Errorf("cannot reveal bid for open or ended auction")
+ }
+
+ // check 2: check that hash of revealed bid matches hash of private bid
+ // on the public ledger. This checks that the bidder is telling the truth
+ // about the value of their bid
+
+ hash := sha256.New()
+ hash.Write(transientBidJSON)
+ calculatedBidJSONHash := hash.Sum(nil)
+
+ // verify that the hash of the passed immutable properties matches the on-chain hash
+ if !bytes.Equal(calculatedBidJSONHash, bidHash) {
+ return fmt.Errorf("hash %x for bid JSON %s does not match hash in auction: %x",
+ calculatedBidJSONHash,
+ transientBidJSON,
+ bidHash,
+ )
+ }
+
+ // check 3; check hash of relealed bid matches hash of private bid that was
+ // added earlier. This ensures that the bid has not changed since it
+ // was added to the auction
+
+ bidders := auctionJSON.PrivateBids
+ privateBidHashString := bidders[bidKey].Hash
+
+ onChainBidHashString := fmt.Sprintf("%x", bidHash)
+ if privateBidHashString != onChainBidHashString {
+ return fmt.Errorf("hash %s for bid JSON %s does not match hash in auction: %s, bidder must have changed bid",
+ privateBidHashString,
+ transientBidJSON,
+ onChainBidHashString,
+ )
+ }
+
+ // we can add the bid to the auction if all checks have passed
+ type transientBidInput struct {
+ Price int `json:"price"`
+ Org string `json:"org"`
+ Bidder string `json:"bidder"`
+ }
+
+ // unmarshal bid imput
+ var bidInput transientBidInput
+ err = json.Unmarshal(transientBidJSON, &bidInput)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %v", err)
+ }
+
+ // Get ID of submitting client identity
+ clientID, err := ctx.GetClientIdentity().GetID()
+ if err != nil {
+ return fmt.Errorf("failed to get client identity %v", err)
+ }
+
+ // marshal transient parameters and ID and MSPID into bid object
+ NewBid := FullBid{
+ Type: bidKeyType,
+ Price: bidInput.Price,
+ Org: bidInput.Org,
+ Bidder: bidInput.Bidder,
+ }
+
+ // check 4: make sure that the transaction is being submitted is the bidder
+ if bidInput.Bidder != clientID {
+ return fmt.Errorf("Permission denied, client id %v is not the owner of the bid", clientID)
+ }
+
+ revealedBids := make(map[string]FullBid)
+ revealedBids = auctionJSON.RevealedBids
+ revealedBids[bidKey] = NewBid
+ auctionJSON.RevealedBids = revealedBids
+
+ newAuctionBytes, _ := json.Marshal(auctionJSON)
+
+ // put auction with bid added back into state
+ err = ctx.GetStub().PutState(auctionID, newAuctionBytes)
+ if err != nil {
+ return fmt.Errorf("failed to update auction: %v", err)
+ }
+
+ return nil
+}
+
+// CloseAuction can be used by the seller to close the auction. This prevents
+// bids from being added to the auction, and allows users to reveal their bid
+func (s *SmartContract) CloseAuction(ctx contractapi.TransactionContextInterface, auctionID string) error {
+
+ auctionBytes, err := ctx.GetStub().GetState(auctionID)
+ if err != nil {
+ return fmt.Errorf("failed to get auction %v: %v", auctionID, err)
+ }
+
+ if auctionBytes == nil {
+ return fmt.Errorf("Auction interest object %v not found", auctionID)
+ }
+
+ var auctionJSON Auction
+ err = json.Unmarshal(auctionBytes, &auctionJSON)
+ if err != nil {
+ return fmt.Errorf("failed to create auction object JSON: %v", err)
+ }
+
+ // the auction can only be closed by the seller
+
+ // get ID of submitting client
+ clientID, err := ctx.GetClientIdentity().GetID()
+ if err != nil {
+ return fmt.Errorf("failed to get client identity %v", err)
+ }
+
+ Seller := auctionJSON.Seller
+ if Seller != clientID {
+ return fmt.Errorf("auction can only be closed by seller: %v", err)
+ }
+
+ Status := auctionJSON.Status
+ if Status != "open" {
+ return fmt.Errorf("cannot close auction that is not open")
+ }
+
+ auctionJSON.Status = string("closed")
+
+ closedAuction, _ := json.Marshal(auctionJSON)
+
+ err = ctx.GetStub().PutState(auctionID, closedAuction)
+ if err != nil {
+ return fmt.Errorf("failed to close auction: %v", err)
+ }
+
+ return nil
+}
+
+// EndAuction both changes the auction status to closed and calculates the winners
+// of the auction
+func (s *SmartContract) EndAuction(ctx contractapi.TransactionContextInterface, auctionID string) error {
+
+ auctionBytes, err := ctx.GetStub().GetState(auctionID)
+ if err != nil {
+ return fmt.Errorf("failed to get auction %v: %v", auctionID, err)
+ }
+
+ if auctionBytes == nil {
+ return fmt.Errorf("Auction interest object %v not found", auctionID)
+ }
+
+ var auctionJSON Auction
+ err = json.Unmarshal(auctionBytes, &auctionJSON)
+ if err != nil {
+ return fmt.Errorf("failed to create auction object JSON: %v", err)
+ }
+
+ // Check that the auction is being ended by the seller
+
+ // get ID of submitting client
+ clientID, err := ctx.GetClientIdentity().GetID()
+ if err != nil {
+ return fmt.Errorf("failed to get client identity %v", err)
+ }
+
+ Seller := auctionJSON.Seller
+ if Seller != clientID {
+ return fmt.Errorf("auction can only be ended by seller: %v", err)
+ }
+
+ Status := auctionJSON.Status
+ if Status != "closed" {
+ return fmt.Errorf("Can only end a closed auction")
+ }
+
+ // get the list of revealed bids
+ revealedBidMap := auctionJSON.RevealedBids
+ if len(auctionJSON.RevealedBids) == 0 {
+ return fmt.Errorf("No bids have been revealed, cannot end auction: %v", err)
+ }
+
+ // determine the highest bid
+ for _, bid := range revealedBidMap {
+ if bid.Price > auctionJSON.Price {
+ auctionJSON.Winner = bid.Bidder
+ auctionJSON.Price = bid.Price
+ }
+ }
+
+ // check if there is a winning bid that has yet to be revealed
+ err = queryAllBids(ctx, auctionJSON.Price, auctionJSON.RevealedBids, auctionJSON.PrivateBids)
+ if err != nil {
+ return fmt.Errorf("Cannot close auction: %v", err)
+ }
+
+ auctionJSON.Status = string("ended")
+
+ closedAuction, _ := json.Marshal(auctionJSON)
+
+ err = ctx.GetStub().PutState(auctionID, closedAuction)
+ if err != nil {
+ return fmt.Errorf("failed to close auction: %v", err)
+ }
+ return nil
+}
diff --git a/auction/chaincode-go/smart-contract/auctionQueries.go b/auction/chaincode-go/smart-contract/auctionQueries.go
new file mode 100644
index 0000000..64eb45b
--- /dev/null
+++ b/auction/chaincode-go/smart-contract/auctionQueries.go
@@ -0,0 +1,148 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package auction
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// QueryAuction allows all members of the channel to read a public auction
+func (s *SmartContract) QueryAuction(ctx contractapi.TransactionContextInterface, auctionID string) (*Auction, error) {
+
+ auctionJSON, err := ctx.GetStub().GetState(auctionID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get auction object %v: %v", auctionID, err)
+ }
+ if auctionJSON == nil {
+ return nil, fmt.Errorf("auction does not exist")
+ }
+
+ var auction *Auction
+ err = json.Unmarshal(auctionJSON, &auction)
+ if err != nil {
+ return nil, err
+ }
+
+ return auction, nil
+}
+
+// QueryBid allows the submitter of the bid to read their bid from public state
+func (s *SmartContract) QueryBid(ctx contractapi.TransactionContextInterface, auctionID string, txID string) (*FullBid, error) {
+
+ err := verifyClientOrgMatchesPeerOrg(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get implicit collection name: %v", err)
+ }
+
+ clientID, err := ctx.GetClientIdentity().GetID()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get client identity %v", err)
+ }
+
+ collection, err := getCollectionName(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get implicit collection name: %v", err)
+ }
+
+ bidKey, err := ctx.GetStub().CreateCompositeKey(bidKeyType, []string{auctionID, txID})
+ if err != nil {
+ return nil, fmt.Errorf("failed to create composite key: %v", err)
+ }
+
+ bidJSON, err := ctx.GetStub().GetPrivateData(collection, bidKey)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get bid %v: %v", bidKey, err)
+ }
+ if bidJSON == nil {
+ return nil, fmt.Errorf("bid %v does not exist", bidKey)
+ }
+
+ var bid *FullBid
+ err = json.Unmarshal(bidJSON, &bid)
+ if err != nil {
+ return nil, err
+ }
+
+ // check that the client querying the bid is the bid owner
+ if bid.Bidder != clientID {
+ return nil, fmt.Errorf("Permission denied, client id %v is not the owner of the bid", clientID)
+ }
+
+ return bid, nil
+}
+
+// GetID is an internal helper function to allow users to get their identity
+func (s *SmartContract) GetID(ctx contractapi.TransactionContextInterface) (string, error) {
+
+ // Get the MSP ID of submitting client identity
+ clientID, err := ctx.GetClientIdentity().GetID()
+ if err != nil {
+ return "", fmt.Errorf("failed to get verified MSPID: %v", err)
+ }
+
+ return clientID, nil
+}
+
+// queryAllBids is an internal function that is used to determine if a winning bid has yet to be revealed
+func queryAllBids(ctx contractapi.TransactionContextInterface, auctionPrice int, revealedBidders map[string]FullBid, bidders map[string]BidHash) error {
+
+ // Get MSP ID of peer org
+ peerMSPID, err := shim.GetMSPID()
+ if err != nil {
+ return fmt.Errorf("failed getting the peer's MSPID: %v", err)
+ }
+
+ var error error
+ error = nil
+
+ for bidKey, privateBid := range bidders {
+
+ if _, bidInAuction := revealedBidders[bidKey]; bidInAuction {
+
+ //bid is already revealed, no action to take
+
+ } else {
+
+ collection := "_implicit_org_" + privateBid.Org
+
+ if privateBid.Org == peerMSPID {
+
+ bidJSON, err := ctx.GetStub().GetPrivateData(collection, bidKey)
+ if err != nil {
+ return fmt.Errorf("failed to get bid %v: %v", bidKey, err)
+ }
+ if bidJSON == nil {
+ return fmt.Errorf("bid %v does not exist", bidKey)
+ }
+
+ var bid *FullBid
+ err = json.Unmarshal(bidJSON, &bid)
+ if err != nil {
+ return err
+ }
+
+ if bid.Price > auctionPrice {
+ error = fmt.Errorf("Cannot close auction, bidder has a higher price: %v", err)
+ }
+
+ } else {
+
+ Hash, err := ctx.GetStub().GetPrivateDataHash(collection, bidKey)
+ if err != nil {
+ return fmt.Errorf("failed to read bid hash from collection: %v", err)
+ }
+ if Hash == nil {
+ return fmt.Errorf("bid hash does not exist: %s", bidKey)
+ }
+ }
+ }
+ }
+
+ return error
+}
diff --git a/auction/chaincode-go/smart-contract/utils.go b/auction/chaincode-go/smart-contract/utils.go
new file mode 100644
index 0000000..8c30524
--- /dev/null
+++ b/auction/chaincode-go/smart-contract/utils.go
@@ -0,0 +1,107 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package auction
+
+import (
+ "fmt"
+
+ "github.com/hyperledger/fabric-chaincode-go/pkg/statebased"
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// setAssetStateBasedEndorsement sets the endorsement policy of a new auction
+func setAssetStateBasedEndorsement(ctx contractapi.TransactionContextInterface, auctionID string, orgToEndorse string) error {
+
+ endorsementPolicy, err := statebased.NewStateEP(nil)
+ if err != nil {
+ return err
+ }
+ err = endorsementPolicy.AddOrgs(statebased.RoleTypePeer, orgToEndorse)
+ if err != nil {
+ return fmt.Errorf("failed to add org to endorsement policy: %v", err)
+ }
+ policy, err := endorsementPolicy.Policy()
+ if err != nil {
+ return fmt.Errorf("failed to create endorsement policy bytes from org: %v", err)
+ }
+ err = ctx.GetStub().SetStateValidationParameter(auctionID, policy)
+ if err != nil {
+ return fmt.Errorf("failed to set validation parameter on auction: %v", err)
+ }
+
+ return nil
+}
+
+// addAssetStateBasedEndorsement adds a new organization as an endorser of the auction
+func addAssetStateBasedEndorsement(ctx contractapi.TransactionContextInterface, auctionID string, orgToEndorse string) error {
+
+ endorsementPolicy, err := ctx.GetStub().GetStateValidationParameter(auctionID)
+ if err != nil {
+ return err
+ }
+
+ newEndorsementPolicy, err := statebased.NewStateEP(endorsementPolicy)
+ if err != nil {
+ return err
+ }
+
+ err = newEndorsementPolicy.AddOrgs(statebased.RoleTypePeer, orgToEndorse)
+ if err != nil {
+ return fmt.Errorf("failed to add org to endorsement policy: %v", err)
+ }
+ policy, err := newEndorsementPolicy.Policy()
+ if err != nil {
+ return fmt.Errorf("failed to create endorsement policy bytes from org: %v", err)
+ }
+ err = ctx.GetStub().SetStateValidationParameter(auctionID, policy)
+ if err != nil {
+ return fmt.Errorf("failed to set validation parameter on auction: %v", err)
+ }
+
+ return nil
+}
+
+// getCollectionName is an internal helper function to get collection of submitting client identity.
+func getCollectionName(ctx contractapi.TransactionContextInterface) (string, error) {
+
+ // Get the MSP ID of submitting client identity
+ clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
+ if err != nil {
+ return "", fmt.Errorf("failed to get verified MSPID: %v", err)
+ }
+
+ // Create the collection name
+ orgCollection := "_implicit_org_" + clientMSPID
+
+ return orgCollection, nil
+}
+
+// verifyClientOrgMatchesPeerOrg is an internal function used to verify that client org id matches peer org id.
+func verifyClientOrgMatchesPeerOrg(ctx contractapi.TransactionContextInterface) error {
+ clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
+ if err != nil {
+ return fmt.Errorf("failed getting the client's MSPID: %v", err)
+ }
+ peerMSPID, err := shim.GetMSPID()
+ if err != nil {
+ return fmt.Errorf("failed getting the peer's MSPID: %v", err)
+ }
+
+ if clientMSPID != peerMSPID {
+ return fmt.Errorf("client from org %v is not authorized to read or write private data from an org %v peer", clientMSPID, peerMSPID)
+ }
+
+ return nil
+}
+
+func contains(sli []string, str string) bool {
+ for _, a := range sli {
+ if a == str {
+ return true
+ }
+ }
+ return false
+}
diff --git a/auction/chaincode-go/smartContract.go b/auction/chaincode-go/smartContract.go
new file mode 100644
index 0000000..8aa5637
--- /dev/null
+++ b/auction/chaincode-go/smartContract.go
@@ -0,0 +1,23 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "log"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ "github.com/hyperledger/fabric-samples/auction/chaincode-go/smart-contract"
+)
+
+func main() {
+ auctionSmartContract, err := contractapi.NewChaincode(&auction.SmartContract{})
+ if err != nil {
+ log.Panicf("Error creating auction chaincode: %v", err)
+ }
+
+ if err := auctionSmartContract.Start(); err != nil {
+ log.Panicf("Error starting auction chaincode: %v", err)
+ }
+}
diff --git a/chaincode/README.md b/chaincode/README.md
new file mode 100644
index 0000000..510528c
--- /dev/null
+++ b/chaincode/README.md
@@ -0,0 +1,20 @@
+[//]: # (SPDX-License-Identifier: CC-BY-4.0)
+
+# Sample smart contracts
+
+This folder contains example smart contracts. It is recommended that users start with the Asset transfer samples and tutorials series for the most recent example smart contracts.
+
+| **Smart Contract** | **Description** | **Languages** |
+| -----------|------------------------------|---------|
+| [fabcar](fabcar) | Basic smart contract that allows you to add and change data on the ledger using the Fabric contract API. Also contains an example on how to run chaincode as an external service. | Go, Java, JavaScript, Typescript |
+| [marbles02](marbles02) | Sample that demonstrates how to deploy an index and use rich queries when you are using CouchDB as your state database. | Go |
+| [marbles02_private](marbles02_private) | Sample that demonstrates the use of private data collections. | Go |
+| [sacc](sacc) | Simple asset chaincode that interacts with the ledger using the low-level APIs provided by the Fabric Chaincode Shim API. | Go |
+| [abstore](abstore) | Basic smart contract that allows you to transfer data (from A to B) using the Fabric contract API. | Go, Java, JavaScript |
+
+## License
+
+Hyperledger Project source code files are made available under the Apache
+License, Version 2.0 (Apache-2.0), located in the [LICENSE](LICENSE) file.
+Hyperledger Project documentation files are made available under the Creative
+Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/.
diff --git a/chaincode/abstore/go/abstore.go b/chaincode/abstore/go/abstore.go
new file mode 100644
index 0000000..9b35a22
--- /dev/null
+++ b/chaincode/abstore/go/abstore.go
@@ -0,0 +1,135 @@
+/*
+Copyright IBM Corp. 2016 All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// ABstore Chaincode implementation
+type ABstore struct {
+ contractapi.Contract
+}
+
+func (t *ABstore) Init(ctx contractapi.TransactionContextInterface, A string, Aval int, B string, Bval int) error {
+ fmt.Println("ABstore Init")
+ var err error
+ // Initialize the chaincode
+ fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
+ // Write the state to the ledger
+ err = ctx.GetStub().PutState(A, []byte(strconv.Itoa(Aval)))
+ if err != nil {
+ return err
+ }
+
+ err = ctx.GetStub().PutState(B, []byte(strconv.Itoa(Bval)))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Transaction makes payment of X units from A to B
+func (t *ABstore) Invoke(ctx contractapi.TransactionContextInterface, A, B string, X int) error {
+ var err error
+ var Aval int
+ var Bval int
+ // Get the state from the ledger
+ // TODO: will be nice to have a GetAllState call to ledger
+ Avalbytes, err := ctx.GetStub().GetState(A)
+ if err != nil {
+ return fmt.Errorf("Failed to get state")
+ }
+ if Avalbytes == nil {
+ return fmt.Errorf("Entity not found")
+ }
+ Aval, _ = strconv.Atoi(string(Avalbytes))
+
+ Bvalbytes, err := ctx.GetStub().GetState(B)
+ if err != nil {
+ return fmt.Errorf("Failed to get state")
+ }
+ if Bvalbytes == nil {
+ return fmt.Errorf("Entity not found")
+ }
+ Bval, _ = strconv.Atoi(string(Bvalbytes))
+
+ // Perform the execution
+ Aval = Aval - X
+ Bval = Bval + X
+ fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
+
+ // Write the state back to the ledger
+ err = ctx.GetStub().PutState(A, []byte(strconv.Itoa(Aval)))
+ if err != nil {
+ return err
+ }
+
+ err = ctx.GetStub().PutState(B, []byte(strconv.Itoa(Bval)))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Delete an entity from state
+func (t *ABstore) Delete(ctx contractapi.TransactionContextInterface, A string) error {
+
+ // Delete the key from the state in ledger
+ err := ctx.GetStub().DelState(A)
+ if err != nil {
+ return fmt.Errorf("Failed to delete state")
+ }
+
+ return nil
+}
+
+// Query callback representing the query of a chaincode
+func (t *ABstore) Query(ctx contractapi.TransactionContextInterface, A string) (string, error) {
+ var err error
+ // Get the state from the ledger
+ Avalbytes, err := ctx.GetStub().GetState(A)
+ if err != nil {
+ jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
+ return "", errors.New(jsonResp)
+ }
+
+ if Avalbytes == nil {
+ jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
+ return "", errors.New(jsonResp)
+ }
+
+ jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
+ fmt.Printf("Query Response:%s\n", jsonResp)
+ return string(Avalbytes), nil
+}
+
+func main() {
+ cc, err := contractapi.NewChaincode(new(ABstore))
+ if err != nil {
+ panic(err.Error())
+ }
+ if err := cc.Start(); err != nil {
+ fmt.Printf("Error starting ABstore chaincode: %s", err)
+ }
+}
diff --git a/chaincode/abstore/go/go.mod b/chaincode/abstore/go/go.mod
new file mode 100644
index 0000000..3891885
--- /dev/null
+++ b/chaincode/abstore/go/go.mod
@@ -0,0 +1,5 @@
+module github.com/hyperledger/fabric-samples/chaincode/abstore/go
+
+go 1.13
+
+require github.com/hyperledger/fabric-contract-api-go v1.1.0
diff --git a/chaincode/abstore/go/go.sum b/chaincode/abstore/go/go.sum
new file mode 100644
index 0000000..12dd961
--- /dev/null
+++ b/chaincode/abstore/go/go.sum
@@ -0,0 +1,149 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/chaincode/abstore/java/.gitignore b/chaincode/abstore/java/.gitignore
new file mode 100644
index 0000000..7005557
--- /dev/null
+++ b/chaincode/abstore/java/.gitignore
@@ -0,0 +1,61 @@
+
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# Gradle
+.gradle
+/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
+
+# Eclipse files
+.project
+.classpath
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+.externalToolBuilders/
+*.launch
diff --git a/chaincode/abstore/java/build.gradle b/chaincode/abstore/java/build.gradle
new file mode 100644
index 0000000..aec70af
--- /dev/null
+++ b/chaincode/abstore/java/build.gradle
@@ -0,0 +1,39 @@
+/*
+ * Copyright IBM Corp. 2018 All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+plugins {
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+ id 'java'
+}
+
+group 'org.hyperledger.fabric-chaincode-java'
+version '1.0-SNAPSHOT'
+
+sourceCompatibility = 1.8
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven {
+ url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
+ }
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+dependencies {
+ implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+}
+
+shadowJar {
+ baseName = 'chaincode'
+ version = null
+ classifier = null
+
+ manifest {
+ attributes 'Main-Class': 'org.hyperledger.fabric_samples.ABstore'
+ }
+}
diff --git a/chaincode/abstore/java/gradle/wrapper/gradle-wrapper.jar b/chaincode/abstore/java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
Binary files /dev/null and b/chaincode/abstore/java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/chaincode/abstore/java/gradle/wrapper/gradle-wrapper.properties b/chaincode/abstore/java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..7c4388a
--- /dev/null
+++ b/chaincode/abstore/java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/chaincode/abstore/java/gradlew b/chaincode/abstore/java/gradlew
new file mode 100755
index 0000000..83f2acf
--- /dev/null
+++ b/chaincode/abstore/java/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/chaincode/abstore/java/gradlew.bat b/chaincode/abstore/java/gradlew.bat
new file mode 100644
index 0000000..24467a1
--- /dev/null
+++ b/chaincode/abstore/java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/chaincode/abstore/java/settings.gradle b/chaincode/abstore/java/settings.gradle
new file mode 100644
index 0000000..80cbe43
--- /dev/null
+++ b/chaincode/abstore/java/settings.gradle
@@ -0,0 +1,7 @@
+/*
+ * Copyright IBM Corp. 2017 All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+rootProject.name = 'abstore'
+
diff --git a/chaincode/abstore/java/src/main/java/org/hyperledger/fabric-samples/ABstore.java b/chaincode/abstore/java/src/main/java/org/hyperledger/fabric-samples/ABstore.java
new file mode 100644
index 0000000..634895d
--- /dev/null
+++ b/chaincode/abstore/java/src/main/java/org/hyperledger/fabric-samples/ABstore.java
@@ -0,0 +1,136 @@
+/*
+Copyright IBM Corp., DTCC All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+package org.hyperledger.fabric_samples;
+
+import java.util.List;
+
+import com.google.protobuf.ByteString;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hyperledger.fabric.shim.ChaincodeBase;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class ABstore extends ChaincodeBase {
+
+ private static Log _logger = LogFactory.getLog(ABstore.class);
+
+ @Override
+ public Response init(ChaincodeStub stub) {
+ try {
+ _logger.info("Init java simple chaincode");
+ List args = stub.getParameters();
+ if (args.size() != 4) {
+ newErrorResponse("Incorrect number of arguments. Expecting 4");
+ }
+ // Initialize the chaincode
+ String account1Key = args.get(0);
+ int account1Value = Integer.parseInt(args.get(1));
+ String account2Key = args.get(2);
+ int account2Value = Integer.parseInt(args.get(3));
+
+ _logger.info(String.format("account %s, value = %s; account %s, value %s", account1Key, account1Value, account2Key, account2Value));
+ stub.putStringState(account1Key, args.get(1));
+ stub.putStringState(account2Key, args.get(3));
+
+ return newSuccessResponse();
+ } catch (Throwable e) {
+ return newErrorResponse(e);
+ }
+ }
+
+ @Override
+ public Response invoke(ChaincodeStub stub) {
+ try {
+ _logger.info("Invoke java simple chaincode");
+ String func = stub.getFunction();
+ List params = stub.getParameters();
+ if (func.equals("invoke")) {
+ return invoke(stub, params);
+ }
+ if (func.equals("delete")) {
+ return delete(stub, params);
+ }
+ if (func.equals("query")) {
+ return query(stub, params);
+ }
+ return newErrorResponse("Invalid invoke function name. Expecting one of: [\"invoke\", \"delete\", \"query\"]");
+ } catch (Throwable e) {
+ return newErrorResponse(e);
+ }
+ }
+
+ private Response invoke(ChaincodeStub stub, List args) {
+ if (args.size() != 3) {
+ return newErrorResponse("Incorrect number of arguments. Expecting 3");
+ }
+ String accountFromKey = args.get(0);
+ String accountToKey = args.get(1);
+
+ String accountFromValueStr = stub.getStringState(accountFromKey);
+ if (accountFromValueStr == null) {
+ return newErrorResponse(String.format("Entity %s not found", accountFromKey));
+ }
+ int accountFromValue = Integer.parseInt(accountFromValueStr);
+
+ String accountToValueStr = stub.getStringState(accountToKey);
+ if (accountToValueStr == null) {
+ return newErrorResponse(String.format("Entity %s not found", accountToKey));
+ }
+ int accountToValue = Integer.parseInt(accountToValueStr);
+
+ int amount = Integer.parseInt(args.get(2));
+
+ if (amount > accountFromValue) {
+ return newErrorResponse(String.format("not enough money in account %s", accountFromKey));
+ }
+
+ accountFromValue -= amount;
+ accountToValue += amount;
+
+ _logger.info(String.format("new value of A: %s", accountFromValue));
+ _logger.info(String.format("new value of B: %s", accountToValue));
+
+ stub.putStringState(accountFromKey, Integer.toString(accountFromValue));
+ stub.putStringState(accountToKey, Integer.toString(accountToValue));
+
+ _logger.info("Transfer complete");
+
+ return newSuccessResponse("invoke finished successfully", ByteString.copyFrom(accountFromKey + ": " + accountFromValue + " " + accountToKey + ": " + accountToValue, UTF_8).toByteArray());
+ }
+
+ // Deletes an entity from state
+ private Response delete(ChaincodeStub stub, List args) {
+ if (args.size() != 1) {
+ return newErrorResponse("Incorrect number of arguments. Expecting 1");
+ }
+ String key = args.get(0);
+ // Delete the key from the state in ledger
+ stub.delState(key);
+ return newSuccessResponse();
+ }
+
+ // query callback representing the query of a chaincode
+ private Response query(ChaincodeStub stub, List args) {
+ if (args.size() != 1) {
+ return newErrorResponse("Incorrect number of arguments. Expecting name of the person to query");
+ }
+ String key = args.get(0);
+ //byte[] stateBytes
+ String val = stub.getStringState(key);
+ if (val == null) {
+ return newErrorResponse(String.format("Error: state for %s is null", key));
+ }
+ _logger.info(String.format("Query Response:\nName: %s, Amount: %s\n", key, val));
+ return newSuccessResponse(val, ByteString.copyFrom(val, UTF_8).toByteArray());
+ }
+
+ public static void main(String[] args) {
+ new ABstore().start(args);
+ }
+
+}
diff --git a/chaincode/abstore/javascript/.gitignore b/chaincode/abstore/javascript/.gitignore
new file mode 100644
index 0000000..a00ca94
--- /dev/null
+++ b/chaincode/abstore/javascript/.gitignore
@@ -0,0 +1,77 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
diff --git a/chaincode/abstore/javascript/abstore.js b/chaincode/abstore/javascript/abstore.js
new file mode 100644
index 0000000..adcfdd9
--- /dev/null
+++ b/chaincode/abstore/javascript/abstore.js
@@ -0,0 +1,138 @@
+/*
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+*/
+
+const shim = require('fabric-shim');
+const util = require('util');
+
+var ABstore = class {
+
+ // Initialize the chaincode
+ async Init(stub) {
+ console.info('========= ABstore Init =========');
+ let ret = stub.getFunctionAndParameters();
+ console.info(ret);
+ let args = ret.params;
+ // initialise only if 4 parameters passed.
+ if (args.length != 4) {
+ return shim.error('Incorrect number of arguments. Expecting 4');
+ }
+
+ let A = args[0];
+ let B = args[2];
+ let Aval = args[1];
+ let Bval = args[3];
+
+ if (typeof parseInt(Aval) !== 'number' || typeof parseInt(Bval) !== 'number') {
+ return shim.error('Expecting integer value for asset holding');
+ }
+
+ try {
+ await stub.putState(A, Buffer.from(Aval));
+ try {
+ await stub.putState(B, Buffer.from(Bval));
+ return shim.success();
+ } catch (err) {
+ return shim.error(err);
+ }
+ } catch (err) {
+ return shim.error(err);
+ }
+ }
+
+ async Invoke(stub) {
+ let ret = stub.getFunctionAndParameters();
+ console.info(ret);
+ let method = this[ret.fcn];
+ if (!method) {
+ console.log('no method of name:' + ret.fcn + ' found');
+ return shim.success();
+ }
+ try {
+ let payload = await method(stub, ret.params);
+ return shim.success(payload);
+ } catch (err) {
+ console.log(err);
+ return shim.error(err);
+ }
+ }
+
+ async invoke(stub, args) {
+ if (args.length != 3) {
+ throw new Error('Incorrect number of arguments. Expecting 3');
+ }
+
+ let A = args[0];
+ let B = args[1];
+ if (!A || !B) {
+ throw new Error('asset holding must not be empty');
+ }
+
+ // Get the state from the ledger
+ let Avalbytes = await stub.getState(A);
+ if (!Avalbytes) {
+ throw new Error('Failed to get state of asset holder A');
+ }
+ let Aval = parseInt(Avalbytes.toString());
+
+ let Bvalbytes = await stub.getState(B);
+ if (!Bvalbytes) {
+ throw new Error('Failed to get state of asset holder B');
+ }
+
+ let Bval = parseInt(Bvalbytes.toString());
+ // Perform the execution
+ let amount = parseInt(args[2]);
+ if (typeof amount !== 'number') {
+ throw new Error('Expecting integer value for amount to be transaferred');
+ }
+
+ Aval = Aval - amount;
+ Bval = Bval + amount;
+ console.info(util.format('Aval = %d, Bval = %d\n', Aval, Bval));
+
+ // Write the states back to the ledger
+ await stub.putState(A, Buffer.from(Aval.toString()));
+ await stub.putState(B, Buffer.from(Bval.toString()));
+
+ }
+
+ // Deletes an entity from state
+ async delete(stub, args) {
+ if (args.length != 1) {
+ throw new Error('Incorrect number of arguments. Expecting 1');
+ }
+
+ let A = args[0];
+
+ // Delete the key from the state in ledger
+ await stub.deleteState(A);
+ }
+
+ // query callback representing the query of a chaincode
+ async query(stub, args) {
+ if (args.length != 1) {
+ throw new Error('Incorrect number of arguments. Expecting name of the person to query')
+ }
+
+ let jsonResp = {};
+ let A = args[0];
+
+ // Get the state from the ledger
+ let Avalbytes = await stub.getState(A);
+ if (!Avalbytes) {
+ jsonResp.error = 'Failed to get state for ' + A;
+ throw new Error(JSON.stringify(jsonResp));
+ }
+
+ jsonResp.name = A;
+ jsonResp.amount = Avalbytes.toString();
+ console.info('Query Response:');
+ console.info(jsonResp);
+ return Avalbytes;
+ }
+};
+
+shim.start(new ABstore());
diff --git a/chaincode/abstore/javascript/package.json b/chaincode/abstore/javascript/package.json
new file mode 100644
index 0000000..74dc19c
--- /dev/null
+++ b/chaincode/abstore/javascript/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "abstore",
+ "version": "1.0.0",
+ "description": "ABstore chaincode implemented in node.js",
+ "engines": {
+ "node": ">=8.4.0",
+ "npm": ">=5.3.0"
+ },
+ "scripts": {
+ "start": "node abstore.js"
+ },
+ "engine-strict": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-shim": "^2.0.0"
+ }
+}
diff --git a/chaincode/fabcar/external/.dockerignore b/chaincode/fabcar/external/.dockerignore
new file mode 100644
index 0000000..61260e5
--- /dev/null
+++ b/chaincode/fabcar/external/.dockerignore
@@ -0,0 +1,5 @@
+chaincode.env*
+*.json
+*.md
+*.tar.gz
+*.tgz
diff --git a/chaincode/fabcar/external/.gitignore b/chaincode/fabcar/external/.gitignore
new file mode 100644
index 0000000..3f4b4e1
--- /dev/null
+++ b/chaincode/fabcar/external/.gitignore
@@ -0,0 +1,4 @@
+chaincode.env
+connection.json
+*.tar.gz
+*.tgz
\ No newline at end of file
diff --git a/chaincode/fabcar/external/Dockerfile b/chaincode/fabcar/external/Dockerfile
new file mode 100644
index 0000000..ef427a8
--- /dev/null
+++ b/chaincode/fabcar/external/Dockerfile
@@ -0,0 +1,17 @@
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+ARG GO_VER=1.13.8
+ARG ALPINE_VER=3.10
+
+FROM golang:${GO_VER}-alpine${ALPINE_VER}
+
+WORKDIR /go/src/github.com/hyperledger/fabric-samples/chaincode/fabcar/external
+COPY . .
+
+RUN go get -d -v ./...
+RUN go install -v ./...
+
+EXPOSE 9999
+CMD ["external"]
diff --git a/chaincode/fabcar/external/README.md b/chaincode/fabcar/external/README.md
new file mode 100644
index 0000000..6d2954e
--- /dev/null
+++ b/chaincode/fabcar/external/README.md
@@ -0,0 +1,55 @@
+# FabCar as an external service
+
+See the "Chaincode as an external service" documentation for running chaincode as an external service.
+This includes details of the external builder and launcher scripts which will peers in your Fabric network will require.
+
+The FabCar chaincode requires two environment variables to run, `CHAINCODE_SERVER_ADDRESS` and `CHAINCODE_ID`, which are described in the `chaincode.env.example` file. Copy this file to `chaincode.env` before continuing.
+
+**Note:** each organization in a Fabric network will need to follow the instructions below to host their own instance of the FabCar external service.
+
+## Packaging and installing
+
+Make sure the value of `CHAINCODE_SERVER_ADDRESS` in `chaincode.env` is correct for the FabCar external service you will be running.
+
+The peer needs a `connection.json` configuration file so that it can connect to the external FabCar service.
+Use the `CHAINCODE_SERVER_ADDRESS` value in `chaincode.env` to create the `connection.json` file with the following command (requires [jq](https://stedolan.github.io/jq/)):
+
+```
+env $(cat chaincode.env | grep -v "#" | xargs) jq -n '{"address":env.CHAINCODE_SERVER_ADDRESS,"dial_timeout": "10s","tls_required": false}' > connection.json
+```
+
+Add this file to a `code.tar.gz` archive ready for adding to a FabCar external service package:
+
+```
+tar cfz code.tar.gz connection.json
+```
+
+Package the FabCar external service using the supplied `metadata.json` file:
+
+```
+tar cfz fabcar-pkg.tgz metadata.json code.tar.gz
+```
+
+Install the `fabcar-pkg.tgz` chaincode as usual, for example:
+
+```
+peer lifecycle chaincode install ./fabcar-pkg.tgz
+```
+
+## Running the FabCar external service
+
+To run the service in a container, build a FabCar docker image:
+
+```
+docker build -t hyperledger/fabcar-sample .
+```
+
+Edit the `chaincode.env` file to configure the `CHAINCODE_ID` variable before starting a FabCar container using the following command:
+
+```
+docker run -it --rm --name fabcar.org1.example.com --hostname fabcar.org1.example.com --env-file chaincode.env --network=net_test hyperledger/fabcar-sample
+```
+
+## Starting the FabCar external service
+
+Complete the remaining lifecycle steps to start the FabCar chaincode!
diff --git a/chaincode/fabcar/external/chaincode.env.example b/chaincode/fabcar/external/chaincode.env.example
new file mode 100644
index 0000000..ea3ba52
--- /dev/null
+++ b/chaincode/fabcar/external/chaincode.env.example
@@ -0,0 +1,8 @@
+# CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can
+# connect to the chaincode server
+CHAINCODE_SERVER_ADDRESS=fabcar.org1.example.com:9999
+
+# CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode
+# on install. The `peer lifecycle chaincode queryinstalled` command can be
+# used to get the ID after install if required
+CHAINCODE_ID=fabcar:...
diff --git a/chaincode/fabcar/external/fabcar.go b/chaincode/fabcar/external/fabcar.go
new file mode 100644
index 0000000..c48211d
--- /dev/null
+++ b/chaincode/fabcar/external/fabcar.go
@@ -0,0 +1,172 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strconv"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+type ServerConfig struct {
+ CCID string
+ Address string
+}
+
+// SmartContract provides functions for managing a car
+type SmartContract struct {
+ contractapi.Contract
+}
+
+// Car describes basic details of what makes up a car
+type Car struct {
+ Make string `json:"make"`
+ Model string `json:"model"`
+ Colour string `json:"colour"`
+ Owner string `json:"owner"`
+}
+
+// QueryResult structure used for handling result of query
+type QueryResult struct {
+ Key string `json:"Key"`
+ Record *Car
+}
+
+// InitLedger adds a base set of cars to the ledger
+func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
+ cars := []Car{
+ Car{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
+ Car{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
+ Car{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
+ Car{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
+ Car{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
+ Car{Make: "Peugeot", Model: "205", Colour: "purple", Owner: "Michel"},
+ Car{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
+ Car{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
+ Car{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
+ Car{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
+ }
+
+ for i, car := range cars {
+ carAsBytes, _ := json.Marshal(car)
+ err := ctx.GetStub().PutState("CAR"+strconv.Itoa(i), carAsBytes)
+
+ if err != nil {
+ return fmt.Errorf("Failed to put to world state. %s", err.Error())
+ }
+ }
+
+ return nil
+}
+
+// CreateCar adds a new car to the world state with given details
+func (s *SmartContract) CreateCar(ctx contractapi.TransactionContextInterface, carNumber string, make string, model string, colour string, owner string) error {
+ car := Car{
+ Make: make,
+ Model: model,
+ Colour: colour,
+ Owner: owner,
+ }
+
+ carAsBytes, _ := json.Marshal(car)
+
+ return ctx.GetStub().PutState(carNumber, carAsBytes)
+}
+
+// QueryCar returns the car stored in the world state with given id
+func (s *SmartContract) QueryCar(ctx contractapi.TransactionContextInterface, carNumber string) (*Car, error) {
+ carAsBytes, err := ctx.GetStub().GetState(carNumber)
+
+ if err != nil {
+ return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
+ }
+
+ if carAsBytes == nil {
+ return nil, fmt.Errorf("%s does not exist", carNumber)
+ }
+
+ car := new(Car)
+ _ = json.Unmarshal(carAsBytes, car)
+
+ return car, nil
+}
+
+// QueryAllCars returns all cars found in world state
+func (s *SmartContract) QueryAllCars(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
+ startKey := ""
+ endKey := ""
+
+ resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)
+
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ results := []QueryResult{}
+
+ for resultsIterator.HasNext() {
+ queryResponse, err := resultsIterator.Next()
+
+ if err != nil {
+ return nil, err
+ }
+
+ car := new(Car)
+ _ = json.Unmarshal(queryResponse.Value, car)
+
+ queryResult := QueryResult{Key: queryResponse.Key, Record: car}
+ results = append(results, queryResult)
+ }
+
+ return results, nil
+}
+
+// ChangeCarOwner updates the owner field of car with given id in world state
+func (s *SmartContract) ChangeCarOwner(ctx contractapi.TransactionContextInterface, carNumber string, newOwner string) error {
+ car, err := s.QueryCar(ctx, carNumber)
+
+ if err != nil {
+ return err
+ }
+
+ car.Owner = newOwner
+
+ carAsBytes, _ := json.Marshal(car)
+
+ return ctx.GetStub().PutState(carNumber, carAsBytes)
+}
+
+func main() {
+ // See chaincode.env.example
+ config := ServerConfig{
+ CCID: os.Getenv("CHAINCODE_ID"),
+ Address: os.Getenv("CHAINCODE_SERVER_ADDRESS"),
+ }
+
+ chaincode, err := contractapi.NewChaincode(new(SmartContract))
+
+ if err != nil {
+ fmt.Printf("Error create fabcar chaincode: %s", err.Error())
+ return
+ }
+
+ server := &shim.ChaincodeServer{
+ CCID: config.CCID,
+ Address: config.Address,
+ CC: chaincode,
+ TLSProps: shim.TLSProperties{
+ Disabled: true,
+ },
+ }
+
+ if err := server.Start(); err != nil {
+ fmt.Printf("Error starting fabcar chaincode: %s", err.Error())
+ }
+}
diff --git a/chaincode/fabcar/external/go.mod b/chaincode/fabcar/external/go.mod
new file mode 100644
index 0000000..f76f401
--- /dev/null
+++ b/chaincode/fabcar/external/go.mod
@@ -0,0 +1,8 @@
+module github.com/hyperledger/fabric-samples/chaincode/fabcar/external
+
+go 1.13
+
+require (
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212
+ github.com/hyperledger/fabric-contract-api-go v1.1.0
+)
diff --git a/chaincode/fabcar/external/go.sum b/chaincode/fabcar/external/go.sum
new file mode 100644
index 0000000..a159a45
--- /dev/null
+++ b/chaincode/fabcar/external/go.sum
@@ -0,0 +1,146 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/chaincode/fabcar/external/metadata.json b/chaincode/fabcar/external/metadata.json
new file mode 100644
index 0000000..43bcdc0
--- /dev/null
+++ b/chaincode/fabcar/external/metadata.json
@@ -0,0 +1,4 @@
+{
+ "type": "external",
+ "label": "fabcar"
+}
diff --git a/chaincode/fabcar/go/fabcar.go b/chaincode/fabcar/go/fabcar.go
new file mode 100644
index 0000000..935b0cd
--- /dev/null
+++ b/chaincode/fabcar/go/fabcar.go
@@ -0,0 +1,396 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "strconv"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+// utils "github.com/cd1/utils-golang"
+
+// SmartContract provides functions for managing a car
+type SmartContract struct {
+ contractapi.Contract
+}
+
+// Car describes basic details of what makes up a car
+type Car struct {
+ Make string `json:"make"`
+ Model string `json:"model"`
+ Colour string `json:"colour"`
+ Owner string `json:"owner"`
+}
+
+// =========================================================================================
+// SSE WEB MODULE DEPLOY STARTS HERE: SCHEMA
+// =========================================================================================
+
+type User struct {
+ Id string `json:"id"`
+ Doctype string `json:"doctype"`
+ Name string `json:"name"`
+ Email string `json:"email"`
+}
+
+type File struct {
+ Id string `json:"id"`
+ Doctype string `json:"doctype"`
+ FilePath string `json:"filePath"`
+ Keyword string `json:"keyword"`
+ NumberOfKeyword int `json:"numberOfKeyword"`
+}
+
+//here doctype will be "sentRequest"
+type RequestFile struct {
+ Id string `json:"id"`
+ Doctype string `json:"doctype"`
+ FileId string `json:"fileid"`
+ OwnerId string `json:"ownerid"`
+ RequesterId string `json:"requesterid"`
+ ResponseType string `json:"responseType"`
+}
+
+type QueryResultFile struct {
+ Key string `json:"Key"`
+ Record *File
+}
+
+// =========================================================================================
+// SSE WEB MODULE DEPLOY ENDS HERE: SCHEMA
+// =========================================================================================
+
+// QueryResult structure used for handling result of query
+type QueryResult struct {
+ Key string `json:"Key"`
+ Record *Car
+}
+
+// InitLedger adds a base set of cars to the ledger
+func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
+ cars := []Car{
+ Car{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
+ Car{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
+ Car{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
+ Car{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
+ Car{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
+ Car{Make: "Peugeot", Model: "205", Colour: "purple", Owner: "Michel"},
+ Car{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
+ Car{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
+ Car{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
+ Car{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
+ }
+
+ for i, car := range cars {
+ carAsBytes, _ := json.Marshal(car)
+ err := ctx.GetStub().PutState("CAR"+strconv.Itoa(i), carAsBytes)
+
+ if err != nil {
+ return fmt.Errorf("Failed to put to world state. %s", err.Error())
+ }
+ }
+
+ return nil
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// =========================================================================================
+// SSE WEB MODULE DEPLOY STARTS HERE: FUNCTION
+// =========================================================================================
+
+func (s *SmartContract) Register(ctx contractapi.TransactionContextInterface, id string, email string, name string) error {
+ //id := utils.RandomString()
+
+ user := User{
+ Id: id,
+ Doctype: "User",
+ Name: name,
+ Email: email,
+ }
+
+ fmt.Println("User created in register function:", user)
+
+ userAsBytes, _ := json.Marshal(user)
+
+ fmt.Println("User created in register function:", userAsBytes)
+ return ctx.GetStub().PutState(id, userAsBytes)
+}
+
+func (s *SmartContract) QueryUserById(ctx contractapi.TransactionContextInterface, userId string) (*User, error) {
+ userAsBytes, err := ctx.GetStub().GetState(userId)
+
+ if err != nil {
+ return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
+ }
+
+ if userAsBytes == nil {
+ return nil, fmt.Errorf("%s does not exist", userId)
+ }
+
+ user := new(User)
+ _ = json.Unmarshal(userAsBytes, user)
+
+ return user, nil
+}
+
+func (s *SmartContract) FileUpload(ctx contractapi.TransactionContextInterface, id string, filePath, string, keyword string, numberOfKeyword int) error {
+ //id := utils.RandomString()
+
+ file := File{
+ Id: id,
+ Doctype: "File",
+ FilePath: filePath,
+ Keyword: keyword,
+ NumberOfKeyword: numberOfKeyword,
+ }
+
+ fmt.Println("File has been uploaded", file)
+
+ fileAsBytes, _ := json.Marshal(file)
+
+ fmt.Println("File uploaded successfully", fileAsBytes)
+ return ctx.GetStub().PutState(id, fileAsBytes)
+}
+
+func (s *SmartContract) QueryFileById(ctx contractapi.TransactionContextInterface, fileId string) (*File, error) {
+ fileAsBytes, err := ctx.GetStub().GetState(fileId)
+
+ if err != nil {
+ return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
+ }
+
+ if fileAsBytes == nil {
+ return nil, fmt.Errorf("%s does not exist", fileId)
+ }
+
+ file := new(File)
+ _ = json.Unmarshal(fileAsBytes, file)
+
+ return file, nil
+}
+
+
+
+func (s *SmartContract) FileRequestNotification(ctx contractapi.TransactionContextInterface, fileId string, ownerId string, requesterId string, responseType string) error {
+ id := fileId + ownerId + requesterId
+ //ownerid, _ := QueryUserByName(ctx, ownerName) //if shows error try s.QueryUserByName(ctx, ownerName) [also for follwing two lines]
+ //requesterid, _ := QueryUserByName(ctx, requesterName)
+ //fileid, _ := QueryFileByName(ctx, fileName)
+
+ requestfile := RequestFile{
+ Id: id,
+ Doctype: "Request",
+ FileId: fileId,
+ OwnerId: ownerId,
+ RequesterId: requesterId,
+ ResponseType: responseType,
+ }
+
+ fmt.Println("Transaction regarding file requsest occur", requestfile)
+
+ requestfileAsBytes, _ := json.Marshal(requestfile)
+
+ fmt.Println("Transaction created for file request sent", requestfileAsBytes)
+ return ctx.GetStub().PutState(id, requestfileAsBytes)
+}
+
+func (s *SmartContract) FileRequestAccept(ctx contractapi.TransactionContextInterface, fileId string, ownerId string, requesterId string, responseType string) error {
+ //id := utils.RandomString()
+ id := fileId + ownerId + requesterId
+
+ requestfile := RequestFile{
+ Id: id,
+ Doctype: "acceptRequest",
+ FileId: fileId,
+ OwnerId: ownerId,
+ RequesterId: requesterId,
+ ResponseType: responseType,
+ }
+
+ fmt.Println("Transaction regarding file requsest occur", requestfile)
+
+ requestfileAsBytes, _ := json.Marshal(requestfile)
+
+ fmt.Println("Transaction created for file request sent", requestfileAsBytes)
+ return ctx.GetStub().PutState(id, requestfileAsBytes)
+
+}
+
+// =========================================================================================
+// SSE WEB MODULE DEPLOY ENDS HERE: FUNCTION
+// =========================================================================================
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// CreateCar adds a new car to the world state with given details
+func (s *SmartContract) CreateCar(ctx contractapi.TransactionContextInterface, carNumber string, make string, model string, colour string, owner string) error {
+ car := Car{
+ Make: make,
+ Model: model,
+ Colour: colour,
+ Owner: owner,
+ }
+
+ carAsBytes, _ := json.Marshal(car)
+
+ return ctx.GetStub().PutState(carNumber, carAsBytes)
+}
+
+// QueryCar returns the car stored in the world state with given id
+func (s *SmartContract) QueryCar(ctx contractapi.TransactionContextInterface, carNumber string) (*Car, error) {
+ carAsBytes, err := ctx.GetStub().GetState(carNumber)
+
+ if err != nil {
+ return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
+ }
+
+ if carAsBytes == nil {
+ return nil, fmt.Errorf("%s does not exist", carNumber)
+ }
+
+ car := new(Car)
+ _ = json.Unmarshal(carAsBytes, car)
+
+ return car, nil
+}
+
+// QueryAllCars returns all cars found in world state
+func (s *SmartContract) QueryAllCars(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
+ startKey := ""
+ endKey := ""
+
+ resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)
+
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ results := []QueryResult{}
+
+ for resultsIterator.HasNext() {
+ queryResponse, err := resultsIterator.Next()
+
+ if err != nil {
+ return nil, err
+ }
+
+ car := new(Car)
+ _ = json.Unmarshal(queryResponse.Value, car)
+
+ queryResult := QueryResult{Key: queryResponse.Key, Record: car}
+ results = append(results, queryResult)
+ }
+
+ return results, nil
+}
+
+// ChangeCarOwner updates the owner field of car with given id in world state
+func (s *SmartContract) ChangeCarOwner(ctx contractapi.TransactionContextInterface, carNumber string, newOwner string) error {
+ car, err := s.QueryCar(ctx, carNumber)
+
+ if err != nil {
+ return err
+ }
+
+ car.Owner = newOwner
+
+ carAsBytes, _ := json.Marshal(car)
+
+ return ctx.GetStub().PutState(carNumber, carAsBytes)
+}
+
+func main() {
+
+ chaincode, err := contractapi.NewChaincode(new(SmartContract))
+
+ if err != nil {
+ fmt.Printf("Error create fabcar chaincode: %s", err.Error())
+ return
+ }
+
+ if err := chaincode.Start(); err != nil {
+ fmt.Printf("Error starting fabcar chaincode: %s", err.Error())
+ }
+}
diff --git a/chaincode/fabcar/go/go.mod b/chaincode/fabcar/go/go.mod
new file mode 100644
index 0000000..6ae88ed
--- /dev/null
+++ b/chaincode/fabcar/go/go.mod
@@ -0,0 +1,5 @@
+module github.com/hyperledger/fabric-samples/chaincode/fabcar/go
+
+go 1.13
+
+require github.com/hyperledger/fabric-contract-api-go v1.1.0
diff --git a/chaincode/fabcar/go/go.sum b/chaincode/fabcar/go/go.sum
new file mode 100644
index 0000000..a159a45
--- /dev/null
+++ b/chaincode/fabcar/go/go.sum
@@ -0,0 +1,146 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/chaincode/fabcar/java/.gitignore b/chaincode/fabcar/java/.gitignore
new file mode 100644
index 0000000..7005557
--- /dev/null
+++ b/chaincode/fabcar/java/.gitignore
@@ -0,0 +1,61 @@
+
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# Gradle
+.gradle
+/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
+
+# Eclipse files
+.project
+.classpath
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+.externalToolBuilders/
+*.launch
diff --git a/chaincode/fabcar/java/README.md b/chaincode/fabcar/java/README.md
new file mode 100644
index 0000000..581c0a4
--- /dev/null
+++ b/chaincode/fabcar/java/README.md
@@ -0,0 +1,14 @@
+# Java FabCar contract sample
+
+The directions for using this sample are documented in the Hyperledger Fabric
+[Writing Your First Application](https://hyperledger-fabric.readthedocs.io/en/latest/write_first_app.html) tutorial.
+
+The tutorial is based on JavaScript, however the same concepts are applicable when using Java.
+
+To install and instantiate the Java version of `FabCar`, use the following command instead of the command shown in the [Launch the network](https://hyperledger-fabric.readthedocs.io/en/release-1.4/write_first_app.html#launch-the-network) section of the tutorial:
+
+```
+./startFabric.sh javascript
+```
+
+*NOTE:* After navigating to the documentation, choose the documentation version that matches your version of Fabric
diff --git a/chaincode/fabcar/java/build.gradle b/chaincode/fabcar/java/build.gradle
new file mode 100644
index 0000000..32a6525
--- /dev/null
+++ b/chaincode/fabcar/java/build.gradle
@@ -0,0 +1,74 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+plugins {
+ id 'application'
+ id 'checkstyle'
+ id 'jacoco'
+}
+
+group 'org.hyperledger.fabric.samples'
+version '1.0-SNAPSHOT'
+
+dependencies {
+ compileOnly 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+ implementation 'com.owlike:genson:1.5'
+ testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
+ testImplementation 'org.assertj:assertj-core:3.11.1'
+ testImplementation 'org.mockito:mockito-core:2.+'
+}
+
+repositories {
+ maven {
+ url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
+ }
+ jcenter()
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+application {
+ mainClass = 'org.hyperledger.fabric.contract.ContractRouter'
+}
+
+checkstyle {
+ toolVersion '8.21'
+ configFile file("config/checkstyle/checkstyle.xml")
+}
+
+checkstyleMain {
+ source ='src/main/java'
+}
+
+checkstyleTest {
+ source ='src/test/java'
+}
+
+jacocoTestReport {
+ dependsOn test
+}
+
+jacocoTestCoverageVerification {
+ violationRules {
+ rule {
+ limit {
+ minimum = 1.0
+ }
+ }
+ }
+
+ finalizedBy jacocoTestReport
+}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
+
+check.dependsOn jacocoTestCoverageVerification
+installDist.dependsOn check
diff --git a/chaincode/fabcar/java/config/checkstyle/checkstyle.xml b/chaincode/fabcar/java/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..29fbde9
--- /dev/null
+++ b/chaincode/fabcar/java/config/checkstyle/checkstyle.xml
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/chaincode/fabcar/java/config/checkstyle/suppressions.xml b/chaincode/fabcar/java/config/checkstyle/suppressions.xml
new file mode 100644
index 0000000..8c44b0a
--- /dev/null
+++ b/chaincode/fabcar/java/config/checkstyle/suppressions.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/chaincode/fabcar/java/gradle/wrapper/gradle-wrapper.jar b/chaincode/fabcar/java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
Binary files /dev/null and b/chaincode/fabcar/java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/chaincode/fabcar/java/gradle/wrapper/gradle-wrapper.properties b/chaincode/fabcar/java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bb8b2fc
--- /dev/null
+++ b/chaincode/fabcar/java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/chaincode/fabcar/java/gradlew b/chaincode/fabcar/java/gradlew
new file mode 100755
index 0000000..83f2acf
--- /dev/null
+++ b/chaincode/fabcar/java/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/chaincode/fabcar/java/gradlew.bat b/chaincode/fabcar/java/gradlew.bat
new file mode 100644
index 0000000..24467a1
--- /dev/null
+++ b/chaincode/fabcar/java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/chaincode/fabcar/java/settings.gradle b/chaincode/fabcar/java/settings.gradle
new file mode 100644
index 0000000..8ee6422
--- /dev/null
+++ b/chaincode/fabcar/java/settings.gradle
@@ -0,0 +1,5 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+rootProject.name = 'fabcar'
diff --git a/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/Car.java b/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/Car.java
new file mode 100644
index 0000000..a67204a
--- /dev/null
+++ b/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/Car.java
@@ -0,0 +1,79 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.fabcar;
+
+import java.util.Objects;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+
+import com.owlike.genson.annotation.JsonProperty;
+
+@DataType()
+public final class Car {
+
+ @Property()
+ private final String make;
+
+ @Property()
+ private final String model;
+
+ @Property()
+ private final String color;
+
+ @Property()
+ private final String owner;
+
+ public String getMake() {
+ return make;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public Car(@JsonProperty("make") final String make, @JsonProperty("model") final String model,
+ @JsonProperty("color") final String color, @JsonProperty("owner") final String owner) {
+ this.make = make;
+ this.model = model;
+ this.color = color;
+ this.owner = owner;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if ((obj == null) || (getClass() != obj.getClass())) {
+ return false;
+ }
+
+ Car other = (Car) obj;
+
+ return Objects.deepEquals(new String[] {getMake(), getModel(), getColor(), getOwner()},
+ new String[] {other.getMake(), other.getModel(), other.getColor(), other.getOwner()});
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getMake(), getModel(), getColor(), getOwner());
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + " [make=" + make + ", model="
+ + model + ", color=" + color + ", owner=" + owner + "]";
+ }
+}
diff --git a/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/CarQueryResult.java b/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/CarQueryResult.java
new file mode 100644
index 0000000..934115a
--- /dev/null
+++ b/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/CarQueryResult.java
@@ -0,0 +1,67 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.fabcar;
+
+import java.util.Objects;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+
+import com.owlike.genson.annotation.JsonProperty;
+
+/**
+ * CarQueryResult structure used for handling result of query
+ *
+ */
+@DataType()
+public final class CarQueryResult {
+ @Property()
+ private final String key;
+
+ @Property()
+ private final Car record;
+
+ public CarQueryResult(@JsonProperty("Key") final String key, @JsonProperty("Record") final Car record) {
+ this.key = key;
+ this.record = record;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public Car getRecord() {
+ return record;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if ((obj == null) || (getClass() != obj.getClass())) {
+ return false;
+ }
+
+ CarQueryResult other = (CarQueryResult) obj;
+
+ Boolean recordsAreEquals = this.getRecord().equals(other.getRecord());
+ Boolean keysAreEquals = this.getKey().equals(other.getKey());
+
+ return recordsAreEquals && keysAreEquals;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.getKey(), this.getRecord());
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + " [key=" + key + ", record="
+ + record + "]";
+ }
+}
diff --git a/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/FabCar.java b/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/FabCar.java
new file mode 100644
index 0000000..89975c8
--- /dev/null
+++ b/chaincode/fabcar/java/src/main/java/org/hyperledger/fabric/samples/fabcar/FabCar.java
@@ -0,0 +1,190 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.fabcar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.contract.ContractInterface;
+import org.hyperledger.fabric.contract.annotation.Contact;
+import org.hyperledger.fabric.contract.annotation.Contract;
+import org.hyperledger.fabric.contract.annotation.Default;
+import org.hyperledger.fabric.contract.annotation.Info;
+import org.hyperledger.fabric.contract.annotation.License;
+import org.hyperledger.fabric.contract.annotation.Transaction;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ledger.KeyValue;
+import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
+
+import com.owlike.genson.Genson;
+
+/**
+ * Java implementation of the Fabric Car Contract described in the Writing Your
+ * First Application tutorial
+ */
+@Contract(
+ name = "FabCar",
+ info = @Info(
+ title = "FabCar contract",
+ description = "The hyperlegendary car contract",
+ version = "0.0.1-SNAPSHOT",
+ license = @License(
+ name = "Apache 2.0 License",
+ url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
+ contact = @Contact(
+ email = "f.carr@example.com",
+ name = "F Carr",
+ url = "https://hyperledger.example.com")))
+@Default
+public final class FabCar implements ContractInterface {
+
+ private final Genson genson = new Genson();
+
+ private enum FabCarErrors {
+ CAR_NOT_FOUND,
+ CAR_ALREADY_EXISTS
+ }
+
+ /**
+ * Retrieves a car with the specified key from the ledger.
+ *
+ * @param ctx the transaction context
+ * @param key the key
+ * @return the Car found on the ledger if there was one
+ */
+ @Transaction()
+ public Car queryCar(final Context ctx, final String key) {
+ ChaincodeStub stub = ctx.getStub();
+ String carState = stub.getStringState(key);
+
+ if (carState.isEmpty()) {
+ String errorMessage = String.format("Car %s does not exist", key);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, FabCarErrors.CAR_NOT_FOUND.toString());
+ }
+
+ Car car = genson.deserialize(carState, Car.class);
+
+ return car;
+ }
+
+ /**
+ * Creates some initial Cars on the ledger.
+ *
+ * @param ctx the transaction context
+ */
+ @Transaction()
+ public void initLedger(final Context ctx) {
+ ChaincodeStub stub = ctx.getStub();
+
+ String[] carData = {
+ "{ \"make\": \"Toyota\", \"model\": \"Prius\", \"color\": \"blue\", \"owner\": \"Tomoko\" }",
+ "{ \"make\": \"Ford\", \"model\": \"Mustang\", \"color\": \"red\", \"owner\": \"Brad\" }",
+ "{ \"make\": \"Hyundai\", \"model\": \"Tucson\", \"color\": \"green\", \"owner\": \"Jin Soo\" }",
+ "{ \"make\": \"Volkswagen\", \"model\": \"Passat\", \"color\": \"yellow\", \"owner\": \"Max\" }",
+ "{ \"make\": \"Tesla\", \"model\": \"S\", \"color\": \"black\", \"owner\": \"Adrian\" }",
+ "{ \"make\": \"Peugeot\", \"model\": \"205\", \"color\": \"purple\", \"owner\": \"Michel\" }",
+ "{ \"make\": \"Chery\", \"model\": \"S22L\", \"color\": \"white\", \"owner\": \"Aarav\" }",
+ "{ \"make\": \"Fiat\", \"model\": \"Punto\", \"color\": \"violet\", \"owner\": \"Pari\" }",
+ "{ \"make\": \"Tata\", \"model\": \"nano\", \"color\": \"indigo\", \"owner\": \"Valeria\" }",
+ "{ \"make\": \"Holden\", \"model\": \"Barina\", \"color\": \"brown\", \"owner\": \"Shotaro\" }"
+ };
+
+ for (int i = 0; i < carData.length; i++) {
+ String key = String.format("CAR%d", i);
+
+ Car car = genson.deserialize(carData[i], Car.class);
+ String carState = genson.serialize(car);
+ stub.putStringState(key, carState);
+ }
+ }
+
+ /**
+ * Creates a new car on the ledger.
+ *
+ * @param ctx the transaction context
+ * @param key the key for the new car
+ * @param make the make of the new car
+ * @param model the model of the new car
+ * @param color the color of the new car
+ * @param owner the owner of the new car
+ * @return the created Car
+ */
+ @Transaction()
+ public Car createCar(final Context ctx, final String key, final String make, final String model,
+ final String color, final String owner) {
+ ChaincodeStub stub = ctx.getStub();
+
+ String carState = stub.getStringState(key);
+ if (!carState.isEmpty()) {
+ String errorMessage = String.format("Car %s already exists", key);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, FabCarErrors.CAR_ALREADY_EXISTS.toString());
+ }
+
+ Car car = new Car(make, model, color, owner);
+ carState = genson.serialize(car);
+ stub.putStringState(key, carState);
+
+ return car;
+ }
+
+ /**
+ * Retrieves all cars from the ledger.
+ *
+ * @param ctx the transaction context
+ * @return array of Cars found on the ledger
+ */
+ @Transaction()
+ public String queryAllCars(final Context ctx) {
+ ChaincodeStub stub = ctx.getStub();
+
+ final String startKey = "CAR1";
+ final String endKey = "CAR99";
+ List queryResults = new ArrayList();
+
+ QueryResultsIterator results = stub.getStateByRange(startKey, endKey);
+
+ for (KeyValue result: results) {
+ Car car = genson.deserialize(result.getStringValue(), Car.class);
+ queryResults.add(new CarQueryResult(result.getKey(), car));
+ }
+
+ final String response = genson.serialize(queryResults);
+
+ return response;
+ }
+
+ /**
+ * Changes the owner of a car on the ledger.
+ *
+ * @param ctx the transaction context
+ * @param key the key
+ * @param newOwner the new owner
+ * @return the updated Car
+ */
+ @Transaction()
+ public Car changeCarOwner(final Context ctx, final String key, final String newOwner) {
+ ChaincodeStub stub = ctx.getStub();
+
+ String carState = stub.getStringState(key);
+
+ if (carState.isEmpty()) {
+ String errorMessage = String.format("Car %s does not exist", key);
+ System.out.println(errorMessage);
+ throw new ChaincodeException(errorMessage, FabCarErrors.CAR_NOT_FOUND.toString());
+ }
+
+ Car car = genson.deserialize(carState, Car.class);
+
+ Car newCar = new Car(car.getMake(), car.getModel(), car.getColor(), newOwner);
+ String newCarState = genson.serialize(newCar);
+ stub.putStringState(key, newCarState);
+
+ return newCar;
+ }
+}
diff --git a/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/CarQueryResultTest.java b/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/CarQueryResultTest.java
new file mode 100644
index 0000000..1bcacd6
--- /dev/null
+++ b/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/CarQueryResultTest.java
@@ -0,0 +1,93 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.fabcar;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+public final class CarQueryResultTest {
+
+ @Nested
+ class Equality {
+
+ @Test
+ public void isReflexive() {
+ CarQueryResult cqr = new CarQueryResult("CAR1", new Car("Toyota", "Prius", "blue", "Tomoko"));
+
+ assertThat(cqr).isEqualTo(cqr);
+ }
+
+ @Test
+ public void isSymmetric() {
+ Car car = new Car("Toyota", "Prius", "blue", "Tomoko");
+ CarQueryResult cqrA = new CarQueryResult("CAR1", car);
+ CarQueryResult cqrB = new CarQueryResult("CAR1", car);
+
+ assertThat(cqrA).isEqualTo(cqrB);
+ assertThat(cqrB).isEqualTo(cqrA);
+ }
+
+ @Test
+ public void isTransitive() {
+ Car car = new Car("Toyota", "Prius", "blue", "Tomoko");
+ CarQueryResult cqrA = new CarQueryResult("CAR1", car);
+ CarQueryResult cqrB = new CarQueryResult("CAR1", car);
+ CarQueryResult cqrC = new CarQueryResult("CAR1", car);
+
+ assertThat(cqrA).isEqualTo(cqrB);
+ assertThat(cqrB).isEqualTo(cqrC);
+ assertThat(cqrA).isEqualTo(cqrC);
+ }
+
+ @Test
+ public void handlesKeyInequality() {
+ CarQueryResult cqrA = new CarQueryResult("CAR1", new Car("Toyota", "Prius", "blue", "Tomoko"));
+ CarQueryResult cqrB = new CarQueryResult("CAR2", new Car("Toyota", "Prius", "blue", "Tomoko"));
+
+ assertThat(cqrA).isNotEqualTo(cqrB);
+ }
+
+ @Test
+ public void handlesRecordInequality() {
+ CarQueryResult cqrA = new CarQueryResult("CAR1", new Car("Toyota", "Prius", "blue", "Tomoko"));
+ CarQueryResult cqrB = new CarQueryResult("CAR1", new Car("Ford", "Mustang", "red", "Brad"));
+
+ assertThat(cqrA).isNotEqualTo(cqrB);
+ }
+
+ @Test
+ public void handlesKeyRecordInequality() {
+ CarQueryResult cqrA = new CarQueryResult("CAR1", new Car("Toyota", "Prius", "blue", "Tomoko"));
+ CarQueryResult cqrB = new CarQueryResult("CAR2", new Car("Ford", "Mustang", "red", "Brad"));
+
+ assertThat(cqrA).isNotEqualTo(cqrB);
+ }
+
+ @Test
+ public void handlesOtherObjects() {
+ CarQueryResult cqrA = new CarQueryResult("CAR1", new Car("Toyota", "Prius", "blue", "Tomoko"));
+ String cqrB = "not a car";
+
+ assertThat(cqrA).isNotEqualTo(cqrB);
+ }
+
+ @Test
+ public void handlesNull() {
+ CarQueryResult cqr = new CarQueryResult("CAR1", new Car("Toyota", "Prius", "blue", "Tomoko"));
+
+ assertThat(cqr).isNotEqualTo(null);
+ }
+ }
+
+ @Test
+ public void toStringIdentifiesCarQueryResult() {
+ CarQueryResult cqr = new CarQueryResult("CAR1", new Car("Toyota", "Prius", "blue", "Tomoko"));
+
+ assertThat(cqr.toString()).isEqualTo("CarQueryResult@65766eb3 [key=CAR1, "
+ + "record=Car@61a77e4f [make=Toyota, model=Prius, color=blue, owner=Tomoko]]");
+ }
+}
diff --git a/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/CarTest.java b/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/CarTest.java
new file mode 100644
index 0000000..5c7b4fc
--- /dev/null
+++ b/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/CarTest.java
@@ -0,0 +1,74 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.fabcar;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+public final class CarTest {
+
+ @Nested
+ class Equality {
+
+ @Test
+ public void isReflexive() {
+ Car car = new Car("Toyota", "Prius", "blue", "Tomoko");
+
+ assertThat(car).isEqualTo(car);
+ }
+
+ @Test
+ public void isSymmetric() {
+ Car carA = new Car("Toyota", "Prius", "blue", "Tomoko");
+ Car carB = new Car("Toyota", "Prius", "blue", "Tomoko");
+
+ assertThat(carA).isEqualTo(carB);
+ assertThat(carB).isEqualTo(carA);
+ }
+
+ @Test
+ public void isTransitive() {
+ Car carA = new Car("Toyota", "Prius", "blue", "Tomoko");
+ Car carB = new Car("Toyota", "Prius", "blue", "Tomoko");
+ Car carC = new Car("Toyota", "Prius", "blue", "Tomoko");
+
+ assertThat(carA).isEqualTo(carB);
+ assertThat(carB).isEqualTo(carC);
+ assertThat(carA).isEqualTo(carC);
+ }
+
+ @Test
+ public void handlesInequality() {
+ Car carA = new Car("Toyota", "Prius", "blue", "Tomoko");
+ Car carB = new Car("Ford", "Mustang", "red", "Brad");
+
+ assertThat(carA).isNotEqualTo(carB);
+ }
+
+ @Test
+ public void handlesOtherObjects() {
+ Car carA = new Car("Toyota", "Prius", "blue", "Tomoko");
+ String carB = "not a car";
+
+ assertThat(carA).isNotEqualTo(carB);
+ }
+
+ @Test
+ public void handlesNull() {
+ Car car = new Car("Toyota", "Prius", "blue", "Tomoko");
+
+ assertThat(car).isNotEqualTo(null);
+ }
+ }
+
+ @Test
+ public void toStringIdentifiesCar() {
+ Car car = new Car("Toyota", "Prius", "blue", "Tomoko");
+
+ assertThat(car.toString()).isEqualTo("Car@61a77e4f [make=Toyota, model=Prius, color=blue, owner=Tomoko]");
+ }
+}
diff --git a/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/FabCarTest.java b/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/FabCarTest.java
new file mode 100644
index 0000000..ea27f47
--- /dev/null
+++ b/chaincode/fabcar/java/src/test/java/org/hyperledger/fabric/samples/fabcar/FabCarTest.java
@@ -0,0 +1,264 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.hyperledger.fabric.samples.fabcar;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.ThrowableAssert.catchThrowable;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.shim.ChaincodeException;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ledger.KeyValue;
+import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InOrder;
+
+public final class FabCarTest {
+
+ private final class MockKeyValue implements KeyValue {
+
+ private final String key;
+ private final String value;
+
+ MockKeyValue(final String key, final String value) {
+ super();
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String getKey() {
+ return this.key;
+ }
+
+ @Override
+ public String getStringValue() {
+ return this.value;
+ }
+
+ @Override
+ public byte[] getValue() {
+ return this.value.getBytes();
+ }
+
+ }
+
+ private final class MockCarResultsIterator implements QueryResultsIterator {
+
+ private final List carList;
+
+ MockCarResultsIterator() {
+ super();
+
+ carList = new ArrayList();
+
+ carList.add(new MockKeyValue("CAR0",
+ "{\"color\":\"blue\",\"make\":\"Toyota\",\"model\":\"Prius\",\"owner\":\"Tomoko\"}"));
+ carList.add(new MockKeyValue("CAR1",
+ "{\"color\":\"red\",\"make\":\"Ford\",\"model\":\"Mustang\",\"owner\":\"Brad\"}"));
+ carList.add(new MockKeyValue("CAR2",
+ "{\"color\":\"green\",\"make\":\"Hyundai\",\"model\":\"Tucson\",\"owner\":\"Jin Soo\"}"));
+ carList.add(new MockKeyValue("CAR7",
+ "{\"color\":\"violet\",\"make\":\"Fiat\",\"model\":\"Punto\",\"owner\":\"Pari\"}"));
+ carList.add(new MockKeyValue("CAR9",
+ "{\"color\":\"brown\",\"make\":\"Holden\",\"model\":\"Barina\",\"owner\":\"Shotaro\"}"));
+ }
+
+ @Override
+ public Iterator iterator() {
+ return carList.iterator();
+ }
+
+ @Override
+ public void close() throws Exception {
+ // do nothing
+ }
+
+ }
+
+ @Test
+ public void invokeUnknownTransaction() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.unknownTransaction(ctx);
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Undefined contract method called");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo(null);
+
+ verifyZeroInteractions(ctx);
+ }
+
+ @Nested
+ class InvokeQueryCarTransaction {
+
+ @Test
+ public void whenCarExists() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("CAR0"))
+ .thenReturn("{\"color\":\"blue\",\"make\":\"Toyota\",\"model\":\"Prius\",\"owner\":\"Tomoko\"}");
+
+ Car car = contract.queryCar(ctx, "CAR0");
+
+ assertThat(car).isEqualTo(new Car("Toyota", "Prius", "blue", "Tomoko"));
+ }
+
+ @Test
+ public void whenCarDoesNotExist() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("CAR0")).thenReturn("");
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.queryCar(ctx, "CAR0");
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Car CAR0 does not exist");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("CAR_NOT_FOUND".getBytes());
+ }
+ }
+
+ @Test
+ void invokeInitLedgerTransaction() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+
+ contract.initLedger(ctx);
+
+ InOrder inOrder = inOrder(stub);
+ inOrder.verify(stub).putStringState("CAR0",
+ "{\"color\":\"blue\",\"make\":\"Toyota\",\"model\":\"Prius\",\"owner\":\"Tomoko\"}");
+ inOrder.verify(stub).putStringState("CAR1",
+ "{\"color\":\"red\",\"make\":\"Ford\",\"model\":\"Mustang\",\"owner\":\"Brad\"}");
+ inOrder.verify(stub).putStringState("CAR2",
+ "{\"color\":\"green\",\"make\":\"Hyundai\",\"model\":\"Tucson\",\"owner\":\"Jin Soo\"}");
+ inOrder.verify(stub).putStringState("CAR3",
+ "{\"color\":\"yellow\",\"make\":\"Volkswagen\",\"model\":\"Passat\",\"owner\":\"Max\"}");
+ inOrder.verify(stub).putStringState("CAR4",
+ "{\"color\":\"black\",\"make\":\"Tesla\",\"model\":\"S\",\"owner\":\"Adrian\"}");
+ inOrder.verify(stub).putStringState("CAR5",
+ "{\"color\":\"purple\",\"make\":\"Peugeot\",\"model\":\"205\",\"owner\":\"Michel\"}");
+ inOrder.verify(stub).putStringState("CAR6",
+ "{\"color\":\"white\",\"make\":\"Chery\",\"model\":\"S22L\",\"owner\":\"Aarav\"}");
+ inOrder.verify(stub).putStringState("CAR7",
+ "{\"color\":\"violet\",\"make\":\"Fiat\",\"model\":\"Punto\",\"owner\":\"Pari\"}");
+ inOrder.verify(stub).putStringState("CAR8",
+ "{\"color\":\"indigo\",\"make\":\"Tata\",\"model\":\"nano\",\"owner\":\"Valeria\"}");
+ inOrder.verify(stub).putStringState("CAR9",
+ "{\"color\":\"brown\",\"make\":\"Holden\",\"model\":\"Barina\",\"owner\":\"Shotaro\"}");
+ }
+
+ @Nested
+ class InvokeCreateCarTransaction {
+
+ @Test
+ public void whenCarExists() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("CAR0"))
+ .thenReturn("{\"color\":\"blue\",\"make\":\"Toyota\",\"model\":\"Prius\",\"owner\":\"Tomoko\"}");
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.createCar(ctx, "CAR0", "Nissan", "Leaf", "green", "Siobhán");
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Car CAR0 already exists");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("CAR_ALREADY_EXISTS".getBytes());
+ }
+
+ @Test
+ public void whenCarDoesNotExist() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("CAR0")).thenReturn("");
+
+ Car car = contract.createCar(ctx, "CAR0", "Nissan", "Leaf", "green", "Siobhán");
+
+ assertThat(car).isEqualTo(new Car("Nissan", "Leaf", "green", "Siobhán"));
+ }
+ }
+
+ @Test
+ void invokeQueryAllCarsTransaction() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStateByRange("CAR1", "CAR99")).thenReturn(new MockCarResultsIterator());
+
+ String cars = contract.queryAllCars(ctx);
+
+ assertThat(cars).isEqualTo("[{\"key\":\"CAR0\","
+ + "\"record\":{\"color\":\"blue\",\"make\":\"Toyota\",\"model\":\"Prius\",\"owner\":\"Tomoko\"}},"
+ + "{\"key\":\"CAR1\","
+ + "\"record\":{\"color\":\"red\",\"make\":\"Ford\",\"model\":\"Mustang\",\"owner\":\"Brad\"}},"
+ + "{\"key\":\"CAR2\","
+ + "\"record\":{\"color\":\"green\",\"make\":\"Hyundai\",\"model\":\"Tucson\",\"owner\":\"Jin Soo\"}},"
+ + "{\"key\":\"CAR7\","
+ + "\"record\":{\"color\":\"violet\",\"make\":\"Fiat\",\"model\":\"Punto\",\"owner\":\"Pari\"}},"
+ + "{\"key\":\"CAR9\","
+ + "\"record\":{\"color\":\"brown\",\"make\":\"Holden\",\"model\":\"Barina\",\"owner\":\"Shotaro\"}}]");
+ }
+
+ @Nested
+ class ChangeCarOwnerTransaction {
+
+ @Test
+ public void whenCarExists() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("CAR0"))
+ .thenReturn("{\"color\":\"blue\",\"make\":\"Toyota\",\"model\":\"Prius\",\"owner\":\"Tomoko\"}");
+
+ Car car = contract.changeCarOwner(ctx, "CAR0", "Dr Evil");
+
+ assertThat(car).isEqualTo(new Car("Toyota", "Prius", "blue", "Dr Evil"));
+ }
+
+ @Test
+ public void whenCarDoesNotExist() {
+ FabCar contract = new FabCar();
+ Context ctx = mock(Context.class);
+ ChaincodeStub stub = mock(ChaincodeStub.class);
+ when(ctx.getStub()).thenReturn(stub);
+ when(stub.getStringState("CAR0")).thenReturn("");
+
+ Throwable thrown = catchThrowable(() -> {
+ contract.changeCarOwner(ctx, "CAR0", "Dr Evil");
+ });
+
+ assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
+ .hasMessage("Car CAR0 does not exist");
+ assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("CAR_NOT_FOUND".getBytes());
+ }
+ }
+}
diff --git a/chaincode/fabcar/javascript/.editorconfig b/chaincode/fabcar/javascript/.editorconfig
new file mode 100755
index 0000000..75a13be
--- /dev/null
+++ b/chaincode/fabcar/javascript/.editorconfig
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/chaincode/fabcar/javascript/.eslintignore b/chaincode/fabcar/javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/chaincode/fabcar/javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/chaincode/fabcar/javascript/.eslintrc.js b/chaincode/fabcar/javascript/.eslintrc.js
new file mode 100644
index 0000000..6d5751a
--- /dev/null
+++ b/chaincode/fabcar/javascript/.eslintrc.js
@@ -0,0 +1,38 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 4],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-tabs': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed'],
+ 'no-constant-condition': ["error", { "checkLoops": false }]
+ }
+};
diff --git a/chaincode/fabcar/javascript/.gitignore b/chaincode/fabcar/javascript/.gitignore
new file mode 100644
index 0000000..a00ca94
--- /dev/null
+++ b/chaincode/fabcar/javascript/.gitignore
@@ -0,0 +1,77 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
diff --git a/chaincode/fabcar/javascript/index.js b/chaincode/fabcar/javascript/index.js
new file mode 100644
index 0000000..493f4a1
--- /dev/null
+++ b/chaincode/fabcar/javascript/index.js
@@ -0,0 +1,12 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const FabCar = require('./lib/fabcar');
+
+module.exports.FabCar = FabCar;
+module.exports.contracts = [ FabCar ];
diff --git a/chaincode/fabcar/javascript/lib/fabcar.js b/chaincode/fabcar/javascript/lib/fabcar.js
new file mode 100644
index 0000000..1220e5f
--- /dev/null
+++ b/chaincode/fabcar/javascript/lib/fabcar.js
@@ -0,0 +1,145 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Contract } = require('fabric-contract-api');
+
+class FabCar extends Contract {
+
+ async initLedger(ctx) {
+ console.info('============= START : Initialize Ledger ===========');
+ const cars = [
+ {
+ color: 'blue',
+ make: 'Toyota',
+ model: 'Prius',
+ owner: 'Tomoko',
+ },
+ {
+ color: 'red',
+ make: 'Ford',
+ model: 'Mustang',
+ owner: 'Brad',
+ },
+ {
+ color: 'green',
+ make: 'Hyundai',
+ model: 'Tucson',
+ owner: 'Jin Soo',
+ },
+ {
+ color: 'yellow',
+ make: 'Volkswagen',
+ model: 'Passat',
+ owner: 'Max',
+ },
+ {
+ color: 'black',
+ make: 'Tesla',
+ model: 'S',
+ owner: 'Adriana',
+ },
+ {
+ color: 'purple',
+ make: 'Peugeot',
+ model: '205',
+ owner: 'Michel',
+ },
+ {
+ color: 'white',
+ make: 'Chery',
+ model: 'S22L',
+ owner: 'Aarav',
+ },
+ {
+ color: 'violet',
+ make: 'Fiat',
+ model: 'Punto',
+ owner: 'Pari',
+ },
+ {
+ color: 'indigo',
+ make: 'Tata',
+ model: 'Nano',
+ owner: 'Valeria',
+ },
+ {
+ color: 'brown',
+ make: 'Holden',
+ model: 'Barina',
+ owner: 'Shotaro',
+ },
+ ];
+
+ for (let i = 0; i < cars.length; i++) {
+ cars[i].docType = 'car';
+ await ctx.stub.putState('CAR' + i, Buffer.from(JSON.stringify(cars[i])));
+ console.info('Added <--> ', cars[i]);
+ }
+ console.info('============= END : Initialize Ledger ===========');
+ }
+
+ async queryCar(ctx, carNumber) {
+ const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state
+ if (!carAsBytes || carAsBytes.length === 0) {
+ throw new Error(`${carNumber} does not exist`);
+ }
+ console.log(carAsBytes.toString());
+ return carAsBytes.toString();
+ }
+
+ async createCar(ctx, carNumber, make, model, color, owner) {
+ console.info('============= START : Create Car ===========');
+
+ const car = {
+ color,
+ docType: 'car',
+ make,
+ model,
+ owner,
+ };
+
+ await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car)));
+ console.info('============= END : Create Car ===========');
+ }
+
+ async queryAllCars(ctx) {
+ const startKey = '';
+ const endKey = '';
+ const allResults = [];
+ for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) {
+ const strValue = Buffer.from(value).toString('utf8');
+ let record;
+ try {
+ record = JSON.parse(strValue);
+ } catch (err) {
+ console.log(err);
+ record = strValue;
+ }
+ allResults.push({ Key: key, Record: record });
+ }
+ console.info(allResults);
+ return JSON.stringify(allResults);
+ }
+
+ async changeCarOwner(ctx, carNumber, newOwner) {
+ console.info('============= START : changeCarOwner ===========');
+
+ const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state
+ if (!carAsBytes || carAsBytes.length === 0) {
+ throw new Error(`${carNumber} does not exist`);
+ }
+ const car = JSON.parse(carAsBytes.toString());
+ car.owner = newOwner;
+
+ await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car)));
+ console.info('============= END : changeCarOwner ===========');
+ }
+
+}
+
+module.exports = FabCar;
diff --git a/chaincode/fabcar/javascript/package.json b/chaincode/fabcar/javascript/package.json
new file mode 100644
index 0000000..1adb28b
--- /dev/null
+++ b/chaincode/fabcar/javascript/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "fabcar",
+ "version": "1.0.0",
+ "description": "FabCar contract implemented in JavaScript",
+ "main": "index.js",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6.9"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "test": "nyc mocha --recursive",
+ "start": "fabric-chaincode-node start"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ },
+ "devDependencies": {
+ "chai": "^4.1.2",
+ "eslint": "^4.19.1",
+ "mocha": "^5.2.0",
+ "nyc": "^14.1.1",
+ "sinon": "^6.0.0",
+ "sinon-chai": "^3.2.0"
+ },
+ "nyc": {
+ "exclude": [
+ "coverage/**",
+ "test/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/chaincode/fabcar/typescript/.editorconfig b/chaincode/fabcar/typescript/.editorconfig
new file mode 100755
index 0000000..75a13be
--- /dev/null
+++ b/chaincode/fabcar/typescript/.editorconfig
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/chaincode/fabcar/typescript/.gitignore b/chaincode/fabcar/typescript/.gitignore
new file mode 100644
index 0000000..69d6a33
--- /dev/null
+++ b/chaincode/fabcar/typescript/.gitignore
@@ -0,0 +1,81 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
+
+# Compiled TypeScript files
+dist
+
diff --git a/chaincode/fabcar/typescript/package.json b/chaincode/fabcar/typescript/package.json
new file mode 100644
index 0000000..7cd3837
--- /dev/null
+++ b/chaincode/fabcar/typescript/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "fabcar",
+ "version": "1.0.0",
+ "description": "FabCar contract implemented in TypeScript",
+ "main": "dist/index.js",
+ "typings": "dist/index.d.ts",
+ "engines": {
+ "node": ">=8",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "tslint -c tslint.json 'src/**/*.ts'",
+ "pretest": "npm run lint",
+ "test": "nyc mocha -r ts-node/register src/**/*.spec.ts",
+ "start": "fabric-chaincode-node start",
+ "build": "tsc",
+ "build:watch": "tsc -w",
+ "prepublishOnly": "npm run build"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ },
+ "devDependencies": {
+ "@types/chai": "^4.1.7",
+ "@types/mocha": "^5.2.5",
+ "@types/node": "^10.12.10",
+ "@types/sinon": "^5.0.7",
+ "@types/sinon-chai": "^3.2.1",
+ "chai": "^4.2.0",
+ "mocha": "^5.2.0",
+ "nyc": "^14.1.1",
+ "sinon": "^7.1.1",
+ "sinon-chai": "^3.3.0",
+ "ts-node": "^7.0.1",
+ "tslint": "^5.11.0",
+ "typescript": "^3.1.6"
+ },
+ "nyc": {
+ "extension": [
+ ".ts",
+ ".tsx"
+ ],
+ "exclude": [
+ "coverage/**",
+ "dist/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/chaincode/fabcar/typescript/src/car.ts b/chaincode/fabcar/typescript/src/car.ts
new file mode 100644
index 0000000..ba10162
--- /dev/null
+++ b/chaincode/fabcar/typescript/src/car.ts
@@ -0,0 +1,11 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export class Car {
+ public docType?: string;
+ public color: string;
+ public make: string;
+ public model: string;
+ public owner: string;
+}
diff --git a/chaincode/fabcar/typescript/src/fabcar.ts b/chaincode/fabcar/typescript/src/fabcar.ts
new file mode 100644
index 0000000..597da7e
--- /dev/null
+++ b/chaincode/fabcar/typescript/src/fabcar.ts
@@ -0,0 +1,140 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Context, Contract } from 'fabric-contract-api';
+import { Car } from './car';
+
+export class FabCar extends Contract {
+
+ public async initLedger(ctx: Context) {
+ console.info('============= START : Initialize Ledger ===========');
+ const cars: Car[] = [
+ {
+ color: 'blue',
+ make: 'Toyota',
+ model: 'Prius',
+ owner: 'Tomoko',
+ },
+ {
+ color: 'red',
+ make: 'Ford',
+ model: 'Mustang',
+ owner: 'Brad',
+ },
+ {
+ color: 'green',
+ make: 'Hyundai',
+ model: 'Tucson',
+ owner: 'Jin Soo',
+ },
+ {
+ color: 'yellow',
+ make: 'Volkswagen',
+ model: 'Passat',
+ owner: 'Max',
+ },
+ {
+ color: 'black',
+ make: 'Tesla',
+ model: 'S',
+ owner: 'Adriana',
+ },
+ {
+ color: 'purple',
+ make: 'Peugeot',
+ model: '205',
+ owner: 'Michel',
+ },
+ {
+ color: 'white',
+ make: 'Chery',
+ model: 'S22L',
+ owner: 'Aarav',
+ },
+ {
+ color: 'violet',
+ make: 'Fiat',
+ model: 'Punto',
+ owner: 'Pari',
+ },
+ {
+ color: 'indigo',
+ make: 'Tata',
+ model: 'Nano',
+ owner: 'Valeria',
+ },
+ {
+ color: 'brown',
+ make: 'Holden',
+ model: 'Barina',
+ owner: 'Shotaro',
+ },
+ ];
+
+ for (let i = 0; i < cars.length; i++) {
+ cars[i].docType = 'car';
+ await ctx.stub.putState('CAR' + i, Buffer.from(JSON.stringify(cars[i])));
+ console.info('Added <--> ', cars[i]);
+ }
+ console.info('============= END : Initialize Ledger ===========');
+ }
+
+ public async queryCar(ctx: Context, carNumber: string): Promise {
+ const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state
+ if (!carAsBytes || carAsBytes.length === 0) {
+ throw new Error(`${carNumber} does not exist`);
+ }
+ console.log(carAsBytes.toString());
+ return carAsBytes.toString();
+ }
+
+ public async createCar(ctx: Context, carNumber: string, make: string, model: string, color: string, owner: string) {
+ console.info('============= START : Create Car ===========');
+
+ const car: Car = {
+ color,
+ docType: 'car',
+ make,
+ model,
+ owner,
+ };
+
+ await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car)));
+ console.info('============= END : Create Car ===========');
+ }
+
+ public async queryAllCars(ctx: Context): Promise {
+ const startKey = '';
+ const endKey = '';
+ const allResults = [];
+ for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) {
+ const strValue = Buffer.from(value).toString('utf8');
+ let record;
+ try {
+ record = JSON.parse(strValue);
+ } catch (err) {
+ console.log(err);
+ record = strValue;
+ }
+ allResults.push({ Key: key, Record: record });
+ }
+ console.info(allResults);
+ return JSON.stringify(allResults);
+ }
+
+ public async changeCarOwner(ctx: Context, carNumber: string, newOwner: string) {
+ console.info('============= START : changeCarOwner ===========');
+
+ const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state
+ if (!carAsBytes || carAsBytes.length === 0) {
+ throw new Error(`${carNumber} does not exist`);
+ }
+ const car: Car = JSON.parse(carAsBytes.toString());
+ car.owner = newOwner;
+
+ await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car)));
+ console.info('============= END : changeCarOwner ===========');
+ }
+
+}
diff --git a/chaincode/fabcar/typescript/src/index.ts b/chaincode/fabcar/typescript/src/index.ts
new file mode 100644
index 0000000..c0a2fcf
--- /dev/null
+++ b/chaincode/fabcar/typescript/src/index.ts
@@ -0,0 +1,8 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { FabCar } from './fabcar';
+export { FabCar } from './fabcar';
+
+export const contracts: any[] = [ FabCar ];
diff --git a/chaincode/fabcar/typescript/tsconfig.json b/chaincode/fabcar/typescript/tsconfig.json
new file mode 100644
index 0000000..8c96ea0
--- /dev/null
+++ b/chaincode/fabcar/typescript/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "compilerOptions": {
+ "outDir": "dist",
+ "target": "es2017",
+ "moduleResolution": "node",
+ "module": "commonjs",
+ "declaration": true,
+ "sourceMap": true
+ },
+ "include": [
+ "./src/**/*"
+ ],
+ "exclude": [
+ "./src/**/*.spec.ts"
+ ]
+}
diff --git a/chaincode/fabcar/typescript/tslint.json b/chaincode/fabcar/typescript/tslint.json
new file mode 100644
index 0000000..33ccbf3
--- /dev/null
+++ b/chaincode/fabcar/typescript/tslint.json
@@ -0,0 +1,21 @@
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint:recommended"
+ ],
+ "jsRules": {},
+ "rules": {
+ "indent": [true, "spaces", 4],
+ "linebreak-style": [true, "LF"],
+ "quotemark": [true, "single"],
+ "semicolon": [true, "always"],
+ "no-console": false,
+ "curly": true,
+ "triple-equals": true,
+ "no-string-throw": true,
+ "no-var-keyword": true,
+ "no-trailing-whitespace": true,
+ "object-literal-key-quotes": [true, "as-needed"]
+ },
+ "rulesDirectory": []
+}
diff --git a/chaincode/marbles02/go/META-INF/statedb/couchdb/indexes/indexOwner.json b/chaincode/marbles02/go/META-INF/statedb/couchdb/indexes/indexOwner.json
new file mode 100644
index 0000000..305f090
--- /dev/null
+++ b/chaincode/marbles02/go/META-INF/statedb/couchdb/indexes/indexOwner.json
@@ -0,0 +1 @@
+{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
diff --git a/chaincode/marbles02/go/go.mod b/chaincode/marbles02/go/go.mod
new file mode 100644
index 0000000..d61ab3e
--- /dev/null
+++ b/chaincode/marbles02/go/go.mod
@@ -0,0 +1,12 @@
+module github.com/hyperledger/fabric-samples/chaincode/marbles02/go
+
+go 1.12
+
+require (
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20190823162523-04390e015b85
+ github.com/hyperledger/fabric-protos-go v0.0.0-20190821214336-621b908d5022
+ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
+ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
+ golang.org/x/text v0.3.2 // indirect
+ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect
+)
diff --git a/chaincode/marbles02/go/go.sum b/chaincode/marbles02/go/go.sum
new file mode 100644
index 0000000..66ba37b
--- /dev/null
+++ b/chaincode/marbles02/go/go.sum
@@ -0,0 +1,85 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20190823162523-04390e015b85 h1:VEm3tPRTCzq3J/1XpVERh1PbOSnshUVwx2G5s3cLiTw=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20190823162523-04390e015b85/go.mod h1:HZK6PKLWrvdD/t0oSLiyaRaUM6fZ7qjJuOlb0zrn0mo=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190821214336-621b908d5022 h1:WzttYAPO5xkQ87ZrxzEhvDZknfarSNu1PZt3NPMTE3Y=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190821214336-621b908d5022/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/chaincode/marbles02/go/marbles_chaincode.go b/chaincode/marbles02/go/marbles_chaincode.go
new file mode 100644
index 0000000..d47fe92
--- /dev/null
+++ b/chaincode/marbles02/go/marbles_chaincode.go
@@ -0,0 +1,755 @@
+/*
+ SPDX-License-Identifier: Apache-2.0
+*/
+
+// ====CHAINCODE EXECUTION SAMPLES (CLI) ==================
+
+// ==== Invoke marbles ====
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble1","blue","35","tom"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble2","red","50","tom"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble3","blue","70","tom"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarble","marble2","jerry"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarblesBasedOnColor","blue","jerry"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["delete","marble1"]}'
+
+// ==== Query marbles ====
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["readMarble","marble1"]}'
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["getMarblesByRange","marble1","marble3"]}'
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'
+
+// Rich Query (Only supported if CouchDB is used as state database):
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesByOwner","tom"]}'
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}'
+
+// Rich Query with Pagination (Only supported if CouchDB is used as state database):
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}'
+
+// INDEXES TO SUPPORT COUCHDB RICH QUERIES
+//
+// Indexes in CouchDB are required in order to make JSON queries efficient and are required for
+// any JSON query with a sort. As of Hyperledger Fabric 1.1, indexes may be packaged alongside
+// chaincode in a META-INF/statedb/couchdb/indexes directory. Each index must be defined in its own
+// text file with extension *.json with the index definition formatted in JSON following the
+// CouchDB index JSON syntax as documented at:
+// http://docs.couchdb.org/en/2.1.1/api/database/find.html#db-index
+//
+// This marbles02 example chaincode demonstrates a packaged
+// index which you can find in META-INF/statedb/couchdb/indexes/indexOwner.json.
+// For deployment of chaincode to production environments, it is recommended
+// to define any indexes alongside chaincode so that the chaincode and supporting indexes
+// are deployed automatically as a unit, once the chaincode has been installed on a peer and
+// instantiated on a channel. See Hyperledger Fabric documentation for more details.
+//
+// If you have access to the your peer's CouchDB state database in a development environment,
+// you may want to iteratively test various indexes in support of your chaincode queries. You
+// can use the CouchDB Fauxton interface or a command line curl utility to create and update
+// indexes. Then once you finalize an index, include the index definition alongside your
+// chaincode in the META-INF/statedb/couchdb/indexes directory, for packaging and deployment
+// to managed environments.
+//
+// In the examples below you can find index definitions that support marbles02
+// chaincode queries, along with the syntax that you can use in development environments
+// to create the indexes in the CouchDB Fauxton interface or a curl command line utility.
+//
+
+//Example hostname:port configurations to access CouchDB.
+//
+//To access CouchDB docker container from within another docker container or from vagrant environments:
+// http://couchdb:5984/
+//
+//Inside couchdb docker container
+// http://127.0.0.1:5984/
+
+// Index for docType, owner.
+//
+// Example curl command line to define index in the CouchDB channel_chaincode database
+// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"docType\",\"owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc1_marbles/_index
+//
+
+// Index for docType, owner, size (descending order).
+//
+// Example curl command line to define index in the CouchDB channel_chaincode database
+// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_marbles/_index
+
+// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
+
+// Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"docType\":{\"$eq\":\"marble\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'
+
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ pb "github.com/hyperledger/fabric-protos-go/peer"
+)
+
+// SimpleChaincode example simple Chaincode implementation
+type SimpleChaincode struct {
+}
+
+type marble struct {
+ ObjectType string `json:"docType"` //docType is used to distinguish the various types of objects in state database
+ Name string `json:"name"` //the fieldtags are needed to keep case from bouncing around
+ Color string `json:"color"`
+ Size int `json:"size"`
+ Owner string `json:"owner"`
+}
+
+// ===================================================================================
+// Main
+// ===================================================================================
+func main() {
+ err := shim.Start(new(SimpleChaincode))
+ if err != nil {
+ fmt.Printf("Error starting Simple chaincode: %s", err)
+ }
+}
+
+// Init initializes chaincode
+// ===========================
+func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
+ return shim.Success(nil)
+}
+
+// Invoke - Our entry point for Invocations
+// ========================================
+func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
+ function, args := stub.GetFunctionAndParameters()
+ fmt.Println("invoke is running " + function)
+
+ // Handle different functions
+ if function == "initMarble" { //create a new marble
+ return t.initMarble(stub, args)
+ } else if function == "transferMarble" { //change owner of a specific marble
+ return t.transferMarble(stub, args)
+ } else if function == "transferMarblesBasedOnColor" { //transfer all marbles of a certain color
+ return t.transferMarblesBasedOnColor(stub, args)
+ } else if function == "delete" { //delete a marble
+ return t.delete(stub, args)
+ } else if function == "readMarble" { //read a marble
+ return t.readMarble(stub, args)
+ } else if function == "queryMarblesByOwner" { //find marbles for owner X using rich query
+ return t.queryMarblesByOwner(stub, args)
+ } else if function == "queryMarbles" { //find marbles based on an ad hoc rich query
+ return t.queryMarbles(stub, args)
+ } else if function == "getHistoryForMarble" { //get history of values for a marble
+ return t.getHistoryForMarble(stub, args)
+ } else if function == "getMarblesByRange" { //get marbles based on range query
+ return t.getMarblesByRange(stub, args)
+ } else if function == "getMarblesByRangeWithPagination" {
+ return t.getMarblesByRangeWithPagination(stub, args)
+ } else if function == "queryMarblesWithPagination" {
+ return t.queryMarblesWithPagination(stub, args)
+ }
+
+ fmt.Println("invoke did not find func: " + function) //error
+ return shim.Error("Received unknown function invocation")
+}
+
+// ============================================================
+// initMarble - create a new marble, store into chaincode state
+// ============================================================
+func (t *SimpleChaincode) initMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+ var err error
+
+ // 0 1 2 3
+ // "asdf", "blue", "35", "bob"
+ if len(args) != 4 {
+ return shim.Error("Incorrect number of arguments. Expecting 4")
+ }
+
+ // ==== Input sanitation ====
+ fmt.Println("- start init marble")
+ if len(args[0]) <= 0 {
+ return shim.Error("1st argument must be a non-empty string")
+ }
+ if len(args[1]) <= 0 {
+ return shim.Error("2nd argument must be a non-empty string")
+ }
+ if len(args[2]) <= 0 {
+ return shim.Error("3rd argument must be a non-empty string")
+ }
+ if len(args[3]) <= 0 {
+ return shim.Error("4th argument must be a non-empty string")
+ }
+ marbleName := args[0]
+ color := strings.ToLower(args[1])
+ owner := strings.ToLower(args[3])
+ size, err := strconv.Atoi(args[2])
+ if err != nil {
+ return shim.Error("3rd argument must be a numeric string")
+ }
+
+ // ==== Check if marble already exists ====
+ marbleAsBytes, err := stub.GetState(marbleName)
+ if err != nil {
+ return shim.Error("Failed to get marble: " + err.Error())
+ } else if marbleAsBytes != nil {
+ fmt.Println("This marble already exists: " + marbleName)
+ return shim.Error("This marble already exists: " + marbleName)
+ }
+
+ // ==== Create marble object and marshal to JSON ====
+ objectType := "marble"
+ marble := &marble{objectType, marbleName, color, size, owner}
+ marbleJSONasBytes, err := json.Marshal(marble)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ //Alternatively, build the marble json string manually if you don't want to use struct marshalling
+ //marbleJSONasString := `{"docType":"Marble", "name": "` + marbleName + `", "color": "` + color + `", "size": ` + strconv.Itoa(size) + `, "owner": "` + owner + `"}`
+ //marbleJSONasBytes := []byte(str)
+
+ // === Save marble to state ===
+ err = stub.PutState(marbleName, marbleJSONasBytes)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+
+ // ==== Index the marble to enable color-based range queries, e.g. return all blue marbles ====
+ // An 'index' is a normal key/value entry in state.
+ // The key is a composite key, with the elements that you want to range query on listed first.
+ // In our case, the composite key is based on indexName~color~name.
+ // This will enable very efficient state range queries based on composite keys matching indexName~color~*
+ indexName := "color~name"
+ colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marble.Color, marble.Name})
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble.
+ // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value
+ value := []byte{0x00}
+ stub.PutState(colorNameIndexKey, value)
+
+ // ==== Marble saved and indexed. Return success ====
+ fmt.Println("- end init marble")
+ return shim.Success(nil)
+}
+
+// ===============================================
+// readMarble - read a marble from chaincode state
+// ===============================================
+func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+ var name, jsonResp string
+ var err error
+
+ if len(args) != 1 {
+ return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
+ }
+
+ name = args[0]
+ valAsbytes, err := stub.GetState(name) //get the marble from chaincode state
+ if err != nil {
+ jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
+ return shim.Error(jsonResp)
+ } else if valAsbytes == nil {
+ jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}"
+ return shim.Error(jsonResp)
+ }
+
+ return shim.Success(valAsbytes)
+}
+
+// ==================================================
+// delete - remove a marble key/value pair from state
+// ==================================================
+func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+ var jsonResp string
+ var marbleJSON marble
+ if len(args) != 1 {
+ return shim.Error("Incorrect number of arguments. Expecting 1")
+ }
+ marbleName := args[0]
+
+ // to maintain the color~name index, we need to read the marble first and get its color
+ valAsbytes, err := stub.GetState(marbleName) //get the marble from chaincode state
+ if err != nil {
+ jsonResp = "{\"Error\":\"Failed to get state for " + marbleName + "\"}"
+ return shim.Error(jsonResp)
+ } else if valAsbytes == nil {
+ jsonResp = "{\"Error\":\"Marble does not exist: " + marbleName + "\"}"
+ return shim.Error(jsonResp)
+ }
+
+ err = json.Unmarshal([]byte(valAsbytes), &marbleJSON)
+ if err != nil {
+ jsonResp = "{\"Error\":\"Failed to decode JSON of: " + marbleName + "\"}"
+ return shim.Error(jsonResp)
+ }
+
+ err = stub.DelState(marbleName) //remove the marble from chaincode state
+ if err != nil {
+ return shim.Error("Failed to delete state:" + err.Error())
+ }
+
+ // maintain the index
+ indexName := "color~name"
+ colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marbleJSON.Color, marbleJSON.Name})
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+
+ // Delete index entry to state.
+ err = stub.DelState(colorNameIndexKey)
+ if err != nil {
+ return shim.Error("Failed to delete state:" + err.Error())
+ }
+ return shim.Success(nil)
+}
+
+// ===========================================================
+// transfer a marble by setting a new owner name on the marble
+// ===========================================================
+func (t *SimpleChaincode) transferMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+
+ // 0 1
+ // "name", "bob"
+ if len(args) < 2 {
+ return shim.Error("Incorrect number of arguments. Expecting 2")
+ }
+
+ marbleName := args[0]
+ newOwner := strings.ToLower(args[1])
+ fmt.Println("- start transferMarble ", marbleName, newOwner)
+
+ marbleAsBytes, err := stub.GetState(marbleName)
+ if err != nil {
+ return shim.Error("Failed to get marble:" + err.Error())
+ } else if marbleAsBytes == nil {
+ return shim.Error("Marble does not exist")
+ }
+
+ marbleToTransfer := marble{}
+ err = json.Unmarshal(marbleAsBytes, &marbleToTransfer) //unmarshal it aka JSON.parse()
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ marbleToTransfer.Owner = newOwner //change the owner
+
+ marbleJSONasBytes, _ := json.Marshal(marbleToTransfer)
+ err = stub.PutState(marbleName, marbleJSONasBytes) //rewrite the marble
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+
+ fmt.Println("- end transferMarble (success)")
+ return shim.Success(nil)
+}
+
+// ===========================================================================================
+// constructQueryResponseFromIterator constructs a JSON array containing query results from
+// a given result iterator
+// ===========================================================================================
+func constructQueryResponseFromIterator(resultsIterator shim.StateQueryIteratorInterface) (*bytes.Buffer, error) {
+ // buffer is a JSON array containing QueryResults
+ var buffer bytes.Buffer
+ buffer.WriteString("[")
+
+ bArrayMemberAlreadyWritten := false
+ for resultsIterator.HasNext() {
+ queryResponse, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+ // Add a comma before array members, suppress it for the first array member
+ if bArrayMemberAlreadyWritten == true {
+ buffer.WriteString(",")
+ }
+ buffer.WriteString("{\"Key\":")
+ buffer.WriteString("\"")
+ buffer.WriteString(queryResponse.Key)
+ buffer.WriteString("\"")
+
+ buffer.WriteString(", \"Record\":")
+ // Record is a JSON object, so we write as-is
+ buffer.WriteString(string(queryResponse.Value))
+ buffer.WriteString("}")
+ bArrayMemberAlreadyWritten = true
+ }
+ buffer.WriteString("]")
+
+ return &buffer, nil
+}
+
+// ===========================================================================================
+// addPaginationMetadataToQueryResults adds QueryResponseMetadata, which contains pagination
+// info, to the constructed query results
+// ===========================================================================================
+func addPaginationMetadataToQueryResults(buffer *bytes.Buffer, responseMetadata *pb.QueryResponseMetadata) *bytes.Buffer {
+
+ buffer.WriteString("[{\"ResponseMetadata\":{\"RecordsCount\":")
+ buffer.WriteString("\"")
+ buffer.WriteString(fmt.Sprintf("%v", responseMetadata.FetchedRecordsCount))
+ buffer.WriteString("\"")
+ buffer.WriteString(", \"Bookmark\":")
+ buffer.WriteString("\"")
+ buffer.WriteString(responseMetadata.Bookmark)
+ buffer.WriteString("\"}}]")
+
+ return buffer
+}
+
+// ===========================================================================================
+// getMarblesByRange performs a range query based on the start and end keys provided.
+
+// Read-only function results are not typically submitted to ordering. If the read-only
+// results are submitted to ordering, or if the query is used in an update transaction
+// and submitted to ordering, then the committing peers will re-execute to guarantee that
+// result sets are stable between endorsement time and commit time. The transaction is
+// invalidated by the committing peers if the result set has changed between endorsement
+// time and commit time.
+// Therefore, range queries are a safe option for performing update transactions based on query results.
+// ===========================================================================================
+func (t *SimpleChaincode) getMarblesByRange(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+
+ if len(args) < 2 {
+ return shim.Error("Incorrect number of arguments. Expecting 2")
+ }
+
+ startKey := args[0]
+ endKey := args[1]
+
+ resultsIterator, err := stub.GetStateByRange(startKey, endKey)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ defer resultsIterator.Close()
+
+ buffer, err := constructQueryResponseFromIterator(resultsIterator)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+
+ fmt.Printf("- getMarblesByRange queryResult:\n%s\n", buffer.String())
+
+ return shim.Success(buffer.Bytes())
+}
+
+// ==== Example: GetStateByPartialCompositeKey/RangeQuery =========================================
+// transferMarblesBasedOnColor will transfer marbles of a given color to a certain new owner.
+// Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'.
+// Committing peers will re-execute range queries to guarantee that result sets are stable
+// between endorsement time and commit time. The transaction is invalidated by the
+// committing peers if the result set has changed between endorsement time and commit time.
+// Therefore, range queries are a safe option for performing update transactions based on query results.
+// ===========================================================================================
+func (t *SimpleChaincode) transferMarblesBasedOnColor(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+
+ // 0 1
+ // "color", "bob"
+ if len(args) < 2 {
+ return shim.Error("Incorrect number of arguments. Expecting 2")
+ }
+
+ color := args[0]
+ newOwner := strings.ToLower(args[1])
+ fmt.Println("- start transferMarblesBasedOnColor ", color, newOwner)
+
+ // Query the color~name index by color
+ // This will execute a key range query on all keys starting with 'color'
+ coloredMarbleResultsIterator, err := stub.GetStateByPartialCompositeKey("color~name", []string{color})
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ defer coloredMarbleResultsIterator.Close()
+
+ // Iterate through result set and for each marble found, transfer to newOwner
+ var i int
+ for i = 0; coloredMarbleResultsIterator.HasNext(); i++ {
+ // Note that we don't get the value (2nd return variable), we'll just get the marble name from the composite key
+ responseRange, err := coloredMarbleResultsIterator.Next()
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+
+ // get the color and name from color~name composite key
+ objectType, compositeKeyParts, err := stub.SplitCompositeKey(responseRange.Key)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ returnedColor := compositeKeyParts[0]
+ returnedMarbleName := compositeKeyParts[1]
+ fmt.Printf("- found a marble from index:%s color:%s name:%s\n", objectType, returnedColor, returnedMarbleName)
+
+ // Now call the transfer function for the found marble.
+ // Re-use the same function that is used to transfer individual marbles
+ response := t.transferMarble(stub, []string{returnedMarbleName, newOwner})
+ // if the transfer failed break out of loop and return error
+ if response.Status != shim.OK {
+ return shim.Error("Transfer failed: " + response.Message)
+ }
+ }
+
+ responsePayload := fmt.Sprintf("Transferred %d %s marbles to %s", i, color, newOwner)
+ fmt.Println("- end transferMarblesBasedOnColor: " + responsePayload)
+ return shim.Success([]byte(responsePayload))
+}
+
+// =======Rich queries =========================================================================
+// Two examples of rich queries are provided below (parameterized query and ad hoc query).
+// Rich queries pass a query string to the state database.
+// Rich queries are only supported by state database implementations
+// that support rich query (e.g. CouchDB).
+// The query string is in the syntax of the underlying state database.
+// With rich queries there is no guarantee that the result set hasn't changed between
+// endorsement time and commit time, aka 'phantom reads'.
+// Therefore, rich queries should not be used in update transactions, unless the
+// application handles the possibility of result set changes between endorsement and commit time.
+// Rich queries can be used for point-in-time queries against a peer.
+// ============================================================================================
+
+// ===== Example: Parameterized rich query =================================================
+// queryMarblesByOwner queries for marbles based on a passed in owner.
+// This is an example of a parameterized query where the query logic is baked into the chaincode,
+// and accepting a single query parameter (owner).
+// Only available on state databases that support rich query (e.g. CouchDB)
+// =========================================================================================
+func (t *SimpleChaincode) queryMarblesByOwner(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+
+ // 0
+ // "bob"
+ if len(args) < 1 {
+ return shim.Error("Incorrect number of arguments. Expecting 1")
+ }
+
+ owner := strings.ToLower(args[0])
+
+ queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"marble\",\"owner\":\"%s\"}}", owner)
+
+ queryResults, err := getQueryResultForQueryString(stub, queryString)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ return shim.Success(queryResults)
+}
+
+// ===== Example: Ad hoc rich query ========================================================
+// queryMarbles uses a query string to perform a query for marbles.
+// Query string matching state database syntax is passed in and executed as is.
+// Supports ad hoc queries that can be defined at runtime by the client.
+// If this is not desired, follow the queryMarblesForOwner example for parameterized queries.
+// Only available on state databases that support rich query (e.g. CouchDB)
+// =========================================================================================
+func (t *SimpleChaincode) queryMarbles(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+
+ // 0
+ // "queryString"
+ if len(args) < 1 {
+ return shim.Error("Incorrect number of arguments. Expecting 1")
+ }
+
+ queryString := args[0]
+
+ queryResults, err := getQueryResultForQueryString(stub, queryString)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ return shim.Success(queryResults)
+}
+
+// =========================================================================================
+// getQueryResultForQueryString executes the passed in query string.
+// Result set is built and returned as a byte array containing the JSON results.
+// =========================================================================================
+func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {
+
+ fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)
+
+ resultsIterator, err := stub.GetQueryResult(queryString)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ buffer, err := constructQueryResponseFromIterator(resultsIterator)
+ if err != nil {
+ return nil, err
+ }
+
+ fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())
+
+ return buffer.Bytes(), nil
+}
+
+// ====== Pagination =========================================================================
+// Pagination provides a method to retrieve records with a defined pagesize and
+// start point (bookmark). An empty string bookmark defines the first "page" of a query
+// result. Paginated queries return a bookmark that can be used in
+// the next query to retrieve the next page of results. Paginated queries extend
+// rich queries and range queries to include a pagesize and bookmark.
+//
+// Two examples are provided in this example. The first is getMarblesByRangeWithPagination
+// which executes a paginated range query.
+// The second example is a paginated query for rich ad-hoc queries.
+// =========================================================================================
+
+// ====== Example: Pagination with Range Query ===============================================
+// getMarblesByRangeWithPagination performs a range query based on the start & end key,
+// page size and a bookmark.
+
+// The number of fetched records will be equal to or lesser than the page size.
+// Paginated range queries are only valid for read only transactions.
+// ===========================================================================================
+func (t *SimpleChaincode) getMarblesByRangeWithPagination(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+
+ if len(args) < 4 {
+ return shim.Error("Incorrect number of arguments. Expecting 4")
+ }
+
+ startKey := args[0]
+ endKey := args[1]
+ //return type of ParseInt is int64
+ pageSize, err := strconv.ParseInt(args[2], 10, 32)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ bookmark := args[3]
+
+ resultsIterator, responseMetadata, err := stub.GetStateByRangeWithPagination(startKey, endKey, int32(pageSize), bookmark)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ defer resultsIterator.Close()
+
+ buffer, err := constructQueryResponseFromIterator(resultsIterator)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+
+ bufferWithPaginationInfo := addPaginationMetadataToQueryResults(buffer, responseMetadata)
+
+ fmt.Printf("- getMarblesByRange queryResult:\n%s\n", bufferWithPaginationInfo.String())
+
+ return shim.Success(buffer.Bytes())
+}
+
+// ===== Example: Pagination with Ad hoc Rich Query ========================================================
+// queryMarblesWithPagination uses a query string, page size and a bookmark to perform a query
+// for marbles. Query string matching state database syntax is passed in and executed as is.
+// The number of fetched records would be equal to or lesser than the specified page size.
+// Supports ad hoc queries that can be defined at runtime by the client.
+// If this is not desired, follow the queryMarblesForOwner example for parameterized queries.
+// Only available on state databases that support rich query (e.g. CouchDB)
+// Paginated queries are only valid for read only transactions.
+// =========================================================================================
+func (t *SimpleChaincode) queryMarblesWithPagination(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+
+ // 0
+ // "queryString"
+ if len(args) < 3 {
+ return shim.Error("Incorrect number of arguments. Expecting 3")
+ }
+
+ queryString := args[0]
+ //return type of ParseInt is int64
+ pageSize, err := strconv.ParseInt(args[1], 10, 32)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ bookmark := args[2]
+
+ queryResults, err := getQueryResultForQueryStringWithPagination(stub, queryString, int32(pageSize), bookmark)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ return shim.Success(queryResults)
+}
+
+// =========================================================================================
+// getQueryResultForQueryStringWithPagination executes the passed in query string with
+// pagination info. Result set is built and returned as a byte array containing the JSON results.
+// =========================================================================================
+func getQueryResultForQueryStringWithPagination(stub shim.ChaincodeStubInterface, queryString string, pageSize int32, bookmark string) ([]byte, error) {
+
+ fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)
+
+ resultsIterator, responseMetadata, err := stub.GetQueryResultWithPagination(queryString, pageSize, bookmark)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ buffer, err := constructQueryResponseFromIterator(resultsIterator)
+ if err != nil {
+ return nil, err
+ }
+
+ bufferWithPaginationInfo := addPaginationMetadataToQueryResults(buffer, responseMetadata)
+
+ fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", bufferWithPaginationInfo.String())
+
+ return buffer.Bytes(), nil
+}
+
+func (t *SimpleChaincode) getHistoryForMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
+
+ if len(args) < 1 {
+ return shim.Error("Incorrect number of arguments. Expecting 1")
+ }
+
+ marbleName := args[0]
+
+ fmt.Printf("- start getHistoryForMarble: %s\n", marbleName)
+
+ resultsIterator, err := stub.GetHistoryForKey(marbleName)
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ defer resultsIterator.Close()
+
+ // buffer is a JSON array containing historic values for the marble
+ var buffer bytes.Buffer
+ buffer.WriteString("[")
+
+ bArrayMemberAlreadyWritten := false
+ for resultsIterator.HasNext() {
+ response, err := resultsIterator.Next()
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+ // Add a comma before array members, suppress it for the first array member
+ if bArrayMemberAlreadyWritten == true {
+ buffer.WriteString(",")
+ }
+ buffer.WriteString("{\"TxId\":")
+ buffer.WriteString("\"")
+ buffer.WriteString(response.TxId)
+ buffer.WriteString("\"")
+
+ buffer.WriteString(", \"Value\":")
+ // if it was a delete operation on given key, then we need to set the
+ //corresponding value null. Else, we will write the response.Value
+ //as-is (as the Value itself a JSON marble)
+ if response.IsDelete {
+ buffer.WriteString("null")
+ } else {
+ buffer.WriteString(string(response.Value))
+ }
+
+ buffer.WriteString(", \"Timestamp\":")
+ buffer.WriteString("\"")
+ buffer.WriteString(time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).String())
+ buffer.WriteString("\"")
+
+ buffer.WriteString(", \"IsDelete\":")
+ buffer.WriteString("\"")
+ buffer.WriteString(strconv.FormatBool(response.IsDelete))
+ buffer.WriteString("\"")
+
+ buffer.WriteString("}")
+ bArrayMemberAlreadyWritten = true
+ }
+ buffer.WriteString("]")
+
+ fmt.Printf("- getHistoryForMarble returning:\n%s\n", buffer.String())
+
+ return shim.Success(buffer.Bytes())
+}
diff --git a/chaincode/marbles02/javascript/.gitignore b/chaincode/marbles02/javascript/.gitignore
new file mode 100644
index 0000000..a00ca94
--- /dev/null
+++ b/chaincode/marbles02/javascript/.gitignore
@@ -0,0 +1,77 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
diff --git a/chaincode/marbles02/javascript/META-INF/statedb/couchdb/indexes/indexOwner.json b/chaincode/marbles02/javascript/META-INF/statedb/couchdb/indexes/indexOwner.json
new file mode 100644
index 0000000..305f090
--- /dev/null
+++ b/chaincode/marbles02/javascript/META-INF/statedb/couchdb/indexes/indexOwner.json
@@ -0,0 +1 @@
+{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
diff --git a/chaincode/marbles02/javascript/marbles_chaincode.js b/chaincode/marbles02/javascript/marbles_chaincode.js
new file mode 100644
index 0000000..7536178
--- /dev/null
+++ b/chaincode/marbles02/javascript/marbles_chaincode.js
@@ -0,0 +1,481 @@
+/*
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+*/
+
+// ====CHAINCODE EXECUTION SAMPLES (CLI) ==================
+
+// ==== Invoke marbles ====
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble1","blue","35","tom"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble2","red","50","tom"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["initMarble","marble3","blue","70","tom"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarble","marble2","jerry"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["transferMarblesBasedOnColor","blue","jerry"]}'
+// peer chaincode invoke -C myc1 -n marbles -c '{"Args":["delete","marble1"]}'
+
+// ==== Query marbles ====
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["readMarble","marble1"]}'
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["getMarblesByRange","marble1","marble3"]}'
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["getMarblesByRangeWithPagination","marble1","marble3","3",""]}'
+
+// Rich Query (Only supported if CouchDB is used as state database):
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesByOwner","tom"]}'
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}'
+
+// Rich Query with Pagination (Only supported if CouchDB is used as state database):
+// peer chaincode query -C myc1 -n marbles -c '{"Args":["queryMarblesWithPagination","{\"selector\":{\"owner\":\"tom\"}}","3",""]}'
+
+'use strict';
+const shim = require('fabric-shim');
+const util = require('util');
+
+let Chaincode = class {
+ async Init(stub) {
+ let ret = stub.getFunctionAndParameters();
+ console.info(ret);
+ console.info('=========== Instantiated Marbles Chaincode ===========');
+ return shim.success();
+ }
+
+ async Invoke(stub) {
+ console.info('Transaction ID: ' + stub.getTxID());
+ console.info(util.format('Args: %j', stub.getArgs()));
+
+ let ret = stub.getFunctionAndParameters();
+ console.info(ret);
+
+ let method = this[ret.fcn];
+ if (!method) {
+ console.log('no function of name:' + ret.fcn + ' found');
+ throw new Error('Received unknown function ' + ret.fcn + ' invocation');
+ }
+ try {
+ let payload = await method(stub, ret.params, this);
+ return shim.success(payload);
+ } catch (err) {
+ console.log(err);
+ return shim.error(err);
+ }
+ }
+
+ // ===============================================
+ // initMarble - create a new marble
+ // ===============================================
+ async initMarble(stub, args, thisClass) {
+ if (args.length != 4) {
+ throw new Error('Incorrect number of arguments. Expecting 4');
+ }
+ // ==== Input sanitation ====
+ console.info('--- start init marble ---')
+ if (args[0].lenth <= 0) {
+ throw new Error('1st argument must be a non-empty string');
+ }
+ if (args[1].lenth <= 0) {
+ throw new Error('2nd argument must be a non-empty string');
+ }
+ if (args[2].lenth <= 0) {
+ throw new Error('3rd argument must be a non-empty string');
+ }
+ if (args[3].lenth <= 0) {
+ throw new Error('4th argument must be a non-empty string');
+ }
+ let marbleName = args[0];
+ let color = args[1].toLowerCase();
+ let owner = args[3].toLowerCase();
+ let size = parseInt(args[2]);
+ if (typeof size !== 'number') {
+ throw new Error('3rd argument must be a numeric string');
+ }
+
+ // ==== Check if marble already exists ====
+ let marbleState = await stub.getState(marbleName);
+ if (marbleState.toString()) {
+ throw new Error('This marble already exists: ' + marbleName);
+ }
+
+ // ==== Create marble object and marshal to JSON ====
+ let marble = {};
+ marble.docType = 'marble';
+ marble.name = marbleName;
+ marble.color = color;
+ marble.size = size;
+ marble.owner = owner;
+
+ // === Save marble to state ===
+ await stub.putState(marbleName, Buffer.from(JSON.stringify(marble)));
+ let indexName = 'color~name'
+ let colorNameIndexKey = await stub.createCompositeKey(indexName, [marble.color, marble.name]);
+ console.info(colorNameIndexKey);
+ // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble.
+ // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value
+ await stub.putState(colorNameIndexKey, Buffer.from('\u0000'));
+ // ==== Marble saved and indexed. Return success ====
+ console.info('- end init marble');
+ }
+
+ // ===============================================
+ // readMarble - read a marble from chaincode state
+ // ===============================================
+ async readMarble(stub, args, thisClass) {
+ if (args.length != 1) {
+ throw new Error('Incorrect number of arguments. Expecting name of the marble to query');
+ }
+
+ let name = args[0];
+ if (!name) {
+ throw new Error(' marble name must not be empty');
+ }
+ let marbleAsbytes = await stub.getState(name); //get the marble from chaincode state
+ if (!marbleAsbytes.toString()) {
+ let jsonResp = {};
+ jsonResp.Error = 'Marble does not exist: ' + name;
+ throw new Error(JSON.stringify(jsonResp));
+ }
+ console.info('=======================================');
+ console.log(marbleAsbytes.toString());
+ console.info('=======================================');
+ return marbleAsbytes;
+ }
+
+ // ==================================================
+ // delete - remove a marble key/value pair from state
+ // ==================================================
+ async delete(stub, args, thisClass) {
+ if (args.length != 1) {
+ throw new Error('Incorrect number of arguments. Expecting name of the marble to delete');
+ }
+ let marbleName = args[0];
+ if (!marbleName) {
+ throw new Error('marble name must not be empty');
+ }
+ // to maintain the color~name index, we need to read the marble first and get its color
+ let valAsbytes = await stub.getState(marbleName); //get the marble from chaincode state
+ let jsonResp = {};
+ if (!valAsbytes) {
+ jsonResp.error = 'marble does not exist: ' + name;
+ throw new Error(jsonResp);
+ }
+ let marbleJSON = {};
+ try {
+ marbleJSON = JSON.parse(valAsbytes.toString());
+ } catch (err) {
+ jsonResp = {};
+ jsonResp.error = 'Failed to decode JSON of: ' + marbleName;
+ throw new Error(jsonResp);
+ }
+
+ await stub.deleteState(marbleName); //remove the marble from chaincode state
+
+ // delete the index
+ let indexName = 'color~name';
+ let colorNameIndexKey = stub.createCompositeKey(indexName, [marbleJSON.color, marbleJSON.name]);
+ if (!colorNameIndexKey) {
+ throw new Error(' Failed to create the createCompositeKey');
+ }
+ // Delete index entry to state.
+ await stub.deleteState(colorNameIndexKey);
+ }
+
+ // ===========================================================
+ // transfer a marble by setting a new owner name on the marble
+ // ===========================================================
+ async transferMarble(stub, args, thisClass) {
+ // 0 1
+ // 'name', 'bob'
+ if (args.length < 2) {
+ throw new Error('Incorrect number of arguments. Expecting marblename and owner')
+ }
+
+ let marbleName = args[0];
+ let newOwner = args[1].toLowerCase();
+ console.info('- start transferMarble ', marbleName, newOwner);
+
+ let marbleAsBytes = await stub.getState(marbleName);
+ if (!marbleAsBytes || !marbleAsBytes.toString()) {
+ throw new Error('marble does not exist');
+ }
+ let marbleToTransfer = {};
+ try {
+ marbleToTransfer = JSON.parse(marbleAsBytes.toString()); //unmarshal
+ } catch (err) {
+ let jsonResp = {};
+ jsonResp.error = 'Failed to decode JSON of: ' + marbleName;
+ throw new Error(jsonResp);
+ }
+ console.info(marbleToTransfer);
+ marbleToTransfer.owner = newOwner; //change the owner
+
+ let marbleJSONasBytes = Buffer.from(JSON.stringify(marbleToTransfer));
+ await stub.putState(marbleName, marbleJSONasBytes); //rewrite the marble
+
+ console.info('- end transferMarble (success)');
+ }
+
+ // ===========================================================================================
+ // getMarblesByRange performs a range query based on the start and end keys provided.
+
+ // Read-only function results are not typically submitted to ordering. If the read-only
+ // results are submitted to ordering, or if the query is used in an update transaction
+ // and submitted to ordering, then the committing peers will re-execute to guarantee that
+ // result sets are stable between endorsement time and commit time. The transaction is
+ // invalidated by the committing peers if the result set has changed between endorsement
+ // time and commit time.
+ // Therefore, range queries are a safe option for performing update transactions based on query results.
+ // ===========================================================================================
+ async getMarblesByRange(stub, args, thisClass) {
+
+ if (args.length < 2) {
+ throw new Error('Incorrect number of arguments. Expecting 2');
+ }
+
+ let startKey = args[0];
+ let endKey = args[1];
+
+ let resultsIterator = await stub.getStateByRange(startKey, endKey);
+ let method = thisClass['getAllResults'];
+ let results = await method(resultsIterator, false);
+
+ return Buffer.from(JSON.stringify(results));
+ }
+
+ // ==== Example: GetStateByPartialCompositeKey/RangeQuery =========================================
+ // transferMarblesBasedOnColor will transfer marbles of a given color to a certain new owner.
+ // Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'.
+ // Committing peers will re-execute range queries to guarantee that result sets are stable
+ // between endorsement time and commit time. The transaction is invalidated by the
+ // committing peers if the result set has changed between endorsement time and commit time.
+ // Therefore, range queries are a safe option for performing update transactions based on query results.
+ // ===========================================================================================
+ async transferMarblesBasedOnColor(stub, args, thisClass) {
+
+ // 0 1
+ // 'color', 'bob'
+ if (args.length < 2) {
+ throw new Error('Incorrect number of arguments. Expecting color and owner');
+ }
+
+ let color = args[0];
+ let newOwner = args[1].toLowerCase();
+ console.info('- start transferMarblesBasedOnColor ', color, newOwner);
+
+ // Query the color~name index by color
+ // This will execute a key range query on all keys starting with 'color'
+ let coloredMarbleResultsIterator = await stub.getStateByPartialCompositeKey('color~name', [color]);
+
+ let method = thisClass['transferMarble'];
+ // Iterate through result set and for each marble found, transfer to newOwner
+ while (true) {
+ let responseRange = await coloredMarbleResultsIterator.next();
+ if (!responseRange || !responseRange.value || !responseRange.value.key) {
+ return;
+ }
+ console.log(responseRange.value.key);
+
+ // let value = res.value.value.toString('utf8');
+ let objectType;
+ let attributes;
+ ({
+ objectType,
+ attributes
+ } = await stub.splitCompositeKey(responseRange.value.key));
+
+ let returnedColor = attributes[0];
+ let returnedMarbleName = attributes[1];
+ console.info(util.format('- found a marble from index:%s color:%s name:%s\n', objectType, returnedColor, returnedMarbleName));
+
+ // Now call the transfer function for the found marble.
+ // Re-use the same function that is used to transfer individual marbles
+ let response = await method(stub, [returnedMarbleName, newOwner]);
+ }
+
+ let responsePayload = util.format('Transferred %s marbles to %s', color, newOwner);
+ console.info('- end transferMarblesBasedOnColor: ' + responsePayload);
+ }
+
+
+ // ===== Example: Parameterized rich query =================================================
+ // queryMarblesByOwner queries for marbles based on a passed in owner.
+ // This is an example of a parameterized query where the query logic is baked into the chaincode,
+ // and accepting a single query parameter (owner).
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ // =========================================================================================
+ async queryMarblesByOwner(stub, args, thisClass) {
+ // 0
+ // 'bob'
+ if (args.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting owner name.')
+ }
+
+ let owner = args[0].toLowerCase();
+ let queryString = {};
+ queryString.selector = {};
+ queryString.selector.docType = 'marble';
+ queryString.selector.owner = owner;
+ let method = thisClass['getQueryResultForQueryString'];
+ let queryResults = await method(stub, JSON.stringify(queryString), thisClass);
+ return queryResults; //shim.success(queryResults);
+ }
+
+ // ===== Example: Ad hoc rich query ========================================================
+ // queryMarbles uses a query string to perform a query for marbles.
+ // Query string matching state database syntax is passed in and executed as is.
+ // Supports ad hoc queries that can be defined at runtime by the client.
+ // If this is not desired, follow the queryMarblesForOwner example for parameterized queries.
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ // =========================================================================================
+ async queryMarbles(stub, args, thisClass) {
+ // 0
+ // 'queryString'
+ if (args.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting queryString');
+ }
+ let queryString = args[0];
+ if (!queryString) {
+ throw new Error('queryString must not be empty');
+ }
+ let method = thisClass['getQueryResultForQueryString'];
+ let queryResults = await method(stub, queryString, thisClass);
+ return queryResults;
+ }
+
+ async getAllResults(iterator, isHistory) {
+ let allResults = [];
+ while (true) {
+ let res = await iterator.next();
+
+ if (res.value && res.value.value.toString()) {
+ let jsonRes = {};
+ console.log(res.value.value.toString('utf8'));
+
+ if (isHistory && isHistory === true) {
+ jsonRes.TxId = res.value.tx_id;
+ jsonRes.Timestamp = res.value.timestamp;
+ jsonRes.IsDelete = res.value.is_delete.toString();
+ try {
+ jsonRes.Value = JSON.parse(res.value.value.toString('utf8'));
+ } catch (err) {
+ console.log(err);
+ jsonRes.Value = res.value.value.toString('utf8');
+ }
+ } else {
+ jsonRes.Key = res.value.key;
+ try {
+ jsonRes.Record = JSON.parse(res.value.value.toString('utf8'));
+ } catch (err) {
+ console.log(err);
+ jsonRes.Record = res.value.value.toString('utf8');
+ }
+ }
+ allResults.push(jsonRes);
+ }
+ if (res.done) {
+ console.log('end of data');
+ await iterator.close();
+ console.info(allResults);
+ return allResults;
+ }
+ }
+ }
+
+ // =========================================================================================
+ // getQueryResultForQueryString executes the passed in query string.
+ // Result set is built and returned as a byte array containing the JSON results.
+ // =========================================================================================
+ async getQueryResultForQueryString(stub, queryString, thisClass) {
+
+ console.info('- getQueryResultForQueryString queryString:\n' + queryString)
+ let resultsIterator = await stub.getQueryResult(queryString);
+ let method = thisClass['getAllResults'];
+
+ let results = await method(resultsIterator, false);
+
+ return Buffer.from(JSON.stringify(results));
+ }
+
+ async getHistoryForMarble(stub, args, thisClass) {
+
+ if (args.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting 1')
+ }
+ let marbleName = args[0];
+ console.info('- start getHistoryForMarble: %s\n', marbleName);
+
+ let resultsIterator = await stub.getHistoryForKey(marbleName);
+ let method = thisClass['getAllResults'];
+ let results = await method(resultsIterator, true);
+
+ return Buffer.from(JSON.stringify(results));
+ }
+
+ // ====== Pagination =========================================================================
+ // Pagination provides a method to retrieve records with a defined pagesize and
+ // start point (bookmark). An empty string bookmark defines the first "page" of a query
+ // result. Paginated queries return a bookmark that can be used in
+ // the next query to retrieve the next page of results. Paginated queries extend
+ // rich queries and range queries to include a pagesize and bookmark.
+ //
+ // Two examples are provided in this example. The first is getMarblesByRangeWithPagination
+ // which executes a paginated range query.
+ // The second example is a paginated query for rich ad-hoc queries.
+ // =========================================================================================
+
+ // ====== Example: Pagination with Range Query ===============================================
+ // getMarblesByRangeWithPagination performs a range query based on the start & end key,
+ // page size and a bookmark.
+ //
+ // The number of fetched records will be equal to or lesser than the page size.
+ // Paginated range queries are only valid for read only transactions.
+ // ===========================================================================================
+ async getMarblesByRangeWithPagination(stub, args, thisClass) {
+ if (args.length < 2) {
+ throw new Error('Incorrect number of arguments. Expecting 2');
+ }
+ const startKey = args[0];
+ const endKey = args[1];
+
+ const pageSize = parseInt(args[2], 10);
+ const bookmark = args[3];
+
+ const { iterator, metadata } = await stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark);
+ const getAllResults = thisClass['getAllResults'];
+ const results = await getAllResults(iterator, false);
+ // use RecordsCount and Bookmark to keep consistency with the go sample
+ results.ResponseMetadata = {
+ RecordsCount: metadata.fetched_records_count,
+ Bookmark: metadata.bookmark,
+ };
+ return Buffer.from(JSON.stringify(results));
+ }
+
+ // =========================================================================================
+ // getQueryResultForQueryStringWithPagination executes the passed in query string with
+ // pagination info. Result set is built and returned as a byte array containing the JSON results.
+ // =========================================================================================
+ async queryMarblesWithPagination(stub, args, thisClass) {
+
+ // 0
+ // "queryString"
+ if (args.length < 3) {
+ return shim.Error("Incorrect number of arguments. Expecting 3")
+ }
+
+ const queryString = args[0];
+ const pageSize = parseInt(args[1], 10);
+ const bookmark = args[2];
+
+ const { iterator, metadata } = await stub.getQueryResultWithPagination(queryString, pageSize, bookmark);
+ const getAllResults = thisClass['getAllResults'];
+ const results = await getAllResults(iterator, false);
+ // use RecordsCount and Bookmark to keep consistency with the go sample
+ results.ResponseMetadata = {
+ RecordsCount: metadata.fetched_records_count,
+ Bookmark: metadata.bookmark,
+ };
+
+ return Buffer.from(JSON.stringify(results));
+ }
+};
+
+shim.start(new Chaincode());
diff --git a/chaincode/marbles02/javascript/package.json b/chaincode/marbles02/javascript/package.json
new file mode 100644
index 0000000..18e1744
--- /dev/null
+++ b/chaincode/marbles02/javascript/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "marbles",
+ "version": "1.0.0",
+ "description": "marbles chaincode implemented in node.js",
+ "engines": {
+ "node": ">=8.4.0",
+ "npm": ">=5.3.0"
+ },
+ "scripts": {
+ "start": "node marbles_chaincode.js"
+ },
+ "engine-strict": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-shim": "^2.0.0"
+ }
+}
diff --git a/chaincode/marbles02_private/collections_config.json b/chaincode/marbles02_private/collections_config.json
new file mode 100644
index 0000000..82af88d
--- /dev/null
+++ b/chaincode/marbles02_private/collections_config.json
@@ -0,0 +1,18 @@
+[
+ {
+ "name": "collectionMarbles",
+ "policy": "OR('Org1MSP.member', 'Org2MSP.member')",
+ "requiredPeerCount": 0,
+ "maxPeerCount": 3,
+ "blockToLive":1000000,
+ "memberOnlyRead": true
+},
+ {
+ "name": "collectionMarblePrivateDetails",
+ "policy": "OR('Org1MSP.member')",
+ "requiredPeerCount": 0,
+ "maxPeerCount": 3,
+ "blockToLive":3,
+ "memberOnlyRead": true
+ }
+]
diff --git a/chaincode/marbles02_private/go/META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexOwner.json b/chaincode/marbles02_private/go/META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexOwner.json
new file mode 100644
index 0000000..305f090
--- /dev/null
+++ b/chaincode/marbles02_private/go/META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexOwner.json
@@ -0,0 +1 @@
+{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
diff --git a/chaincode/marbles02_private/go/go.mod b/chaincode/marbles02_private/go/go.mod
new file mode 100644
index 0000000..8674539
--- /dev/null
+++ b/chaincode/marbles02_private/go/go.mod
@@ -0,0 +1,5 @@
+module github.com/hyperledger/fabric-samples/chaincode/marbles02_private/go
+
+go 1.13
+
+require github.com/hyperledger/fabric-contract-api-go v1.1.0
diff --git a/chaincode/marbles02_private/go/go.sum b/chaincode/marbles02_private/go/go.sum
new file mode 100644
index 0000000..653dbb0
--- /dev/null
+++ b/chaincode/marbles02_private/go/go.sum
@@ -0,0 +1,137 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/chaincode/marbles02_private/go/marbles_chaincode_private.go b/chaincode/marbles02_private/go/marbles_chaincode_private.go
new file mode 100644
index 0000000..34d5e1b
--- /dev/null
+++ b/chaincode/marbles02_private/go/marbles_chaincode_private.go
@@ -0,0 +1,556 @@
+/*
+Copyright IBM Corp. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+// ====CHAINCODE EXECUTION SAMPLES (CLI) ==================
+
+// ==== Invoke marbles, pass private data as base64 encoded bytes in transient map ====
+//
+// export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 | tr -d \\n)
+// peer chaincode invoke -C mychannel -n marblesp -c '{"Args":["InitMarble"]}' --transient "{\"marble\":\"$MARBLE\"}"
+//
+// export MARBLE=$(echo -n "{\"name\":\"marble2\",\"color\":\"red\",\"size\":50,\"owner\":\"tom\",\"price\":102}" | base64 | tr -d \\n)
+// peer chaincode invoke -C mychannel -n marblesp -c '{"Args":["InitMarble"]}' --transient "{\"marble\":\"$MARBLE\"}"
+//
+// export MARBLE=$(echo -n "{\"name\":\"marble3\",\"color\":\"blue\",\"size\":70,\"owner\":\"tom\",\"price\":103}" | base64 | tr -d \\n)
+// peer chaincode invoke -C mychannel -n marblesp -c '{"Args":["InitMarble"]}' --transient "{\"marble\":\"$MARBLE\"}"
+//
+// export MARBLE_OWNER=$(echo -n "{\"name\":\"marble2\",\"owner\":\"jerry\"}" | base64 | tr -d \\n)
+// peer chaincode invoke -C mychannel -n marblesp -c '{"Args":["TransferMarble"]}' --transient "{\"marble_owner\":\"$MARBLE_OWNER\"}"
+//
+// export MARBLE_DELETE=$(echo -n "{\"name\":\"marble1\"}" | base64 | tr -d \\n)
+// peer chaincode invoke -C mychannel -n marblesp -c '{"Args":["Delete"]}' --transient "{\"marble_delete\":\"$MARBLE_DELETE\"}"
+
+// ==== Query marbles, since queries are not recorded on chain we don't need to hide private data in transient map ====
+// peer chaincode query -C mychannel -n marblesp -c '{"Args":["ReadMarble","marble1"]}'
+// peer chaincode query -C mychannel -n marblesp -c '{"Args":["ReadMarblePrivateDetails","marble1"]}'
+// peer chaincode query -C mychannel -n marblesp -c '{"Args":["GetMarblesByRange","marble1","marble4"]}'
+
+// Query a marble's public data hash
+// peer chaincode query -C mychannel -n marblesp -c '{"Args":["GetMarbleHash","collectionMarbles","marble1"]}'
+
+// Rich Query (Only supported if CouchDB is used as state database):
+// peer chaincode query -C mychannel -n marblesp -c '{"Args":["QueryMarblesByOwner","tom"]}'
+// peer chaincode query -C mychannel -n marblesp -c '{"Args":["QueryMarbles","{\"selector\":{\"owner\":\"tom\"}}"]}'
+
+// INDEXES TO SUPPORT COUCHDB RICH QUERIES
+//
+// Indexes in CouchDB are required in order to make JSON queries efficient and are required for
+// any JSON query with a sort. As of Hyperledger Fabric 1.1, indexes may be packaged alongside
+// chaincode in a META-INF/statedb/couchdb/indexes directory. Or for indexes on private data
+// collections, in a META-INF/statedb/couchdb/collections//indexes directory.
+// Each index must be defined in its own text file with extension *.json with the index
+// definition formatted in JSON following the CouchDB index JSON syntax as documented at:
+// http://docs.couchdb.org/en/2.1.1/api/database/find.html#db-index
+//
+// This marbles02_private example chaincode demonstrates a packaged index which you
+// can find in META-INF/statedb/couchdb/collection/collectionMarbles/indexes/indexOwner.json.
+// For deployment of chaincode to production environments, it is recommended
+// to define any indexes alongside chaincode so that the chaincode and supporting indexes
+// are deployed automatically as a unit, once the chaincode has been installed on a peer and
+// instantiated on a channel. See Hyperledger Fabric documentation for more details.
+//
+// If you have access to the your peer's CouchDB state database in a development environment,
+// you may want to iteratively test various indexes in support of your chaincode queries. You
+// can use the CouchDB Fauxton interface or a command line curl utility to create and update
+// indexes. Then once you finalize an index, include the index definition alongside your
+// chaincode in the META-INF/statedb/couchdb/indexes directory or
+// META-INF/statedb/couchdb/collections//indexes directory, for packaging
+// and deployment to managed environments.
+//
+// In the examples below you can find index definitions that support marbles02_private
+// chaincode queries, along with the syntax that you can use in development environments
+// to create the indexes in the CouchDB Fauxton interface.
+//
+
+//Example hostname:port configurations to access CouchDB.
+//
+//To access CouchDB docker container from within another docker container or from vagrant environments:
+// http://couchdb:5984/
+//
+//Inside couchdb docker container
+// http://127.0.0.1:5984/
+
+// Index for docType, owner.
+// Note that docType and owner fields must be prefixed with the "data" wrapper
+//
+// Index definition for use with Fauxton interface
+// {"index":{"fields":["data.docType","data.owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
+
+// Index for docType, owner, size (descending order).
+// Note that docType, owner and size fields must be prefixed with the "data" wrapper
+//
+// Index definition for use with Fauxton interface
+// {"index":{"fields":[{"data.size":"desc"},{"data.docType":"desc"},{"data.owner":"desc"}]},"ddoc":"indexSizeSortDoc", "name":"indexSizeSortDesc","type":"json"}
+
+// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
+// peer chaincode query -C mychannel -n marblesp -c '{"Args":["QueryMarbles","{\"selector\":{\"docType\":\"marble\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
+
+// Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
+// peer chaincode query -C mychannel -n marblesp -c '{"Args":["QueryMarbles","{\"selector\":{\"docType\":{\"$eq\":\"marble\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+type Marble struct {
+ ObjectType string `json:"docType"` //docType is used to distinguish the various types of objects in state database
+ Name string `json:"name"` //the fieldtags are needed to keep case from bouncing around
+ Color string `json:"color"`
+ Size int `json:"size"`
+ Owner string `json:"owner"`
+}
+
+type MarblePrivateDetails struct {
+ ObjectType string `json:"docType"` //docType is used to distinguish the various types of objects in state database
+ Name string `json:"name"` //the fieldtags are needed to keep case from bouncing around
+ Price int `json:"price"`
+}
+
+type SmartContract struct {
+ contractapi.Contract
+}
+
+
+// ============================================================
+// initMarble - create a new marble, store into chaincode state
+// ============================================================
+func (s *SmartContract) InitMarble(ctx contractapi.TransactionContextInterface) error {
+
+ transMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("Error getting transient: " + err.Error())
+ }
+
+ // Marble properties are private, therefore they get passed in transient field
+ transientMarbleJSON, ok := transMap["marble"]
+ if !ok {
+ return fmt.Errorf("marble not found in the transient map")
+ }
+
+ type marbleTransientInput struct {
+ Name string `json:"name"` //the fieldtags are needed to keep case from bouncing around
+ Color string `json:"color"`
+ Size int `json:"size"`
+ Owner string `json:"owner"`
+ Price int `json:"price"`
+ }
+
+ var marbleInput marbleTransientInput
+ err = json.Unmarshal(transientMarbleJSON, &marbleInput)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
+ }
+
+ if len(marbleInput.Name) == 0 {
+ return fmt.Errorf("name field must be a non-empty string")
+ }
+ if len(marbleInput.Color) == 0 {
+ return fmt.Errorf("color field must be a non-empty string")
+ }
+ if marbleInput.Size <= 0 {
+ return fmt.Errorf("size field must be a positive integer")
+ }
+ if len(marbleInput.Owner) == 0 {
+ return fmt.Errorf("owner field must be a non-empty string")
+ }
+ if marbleInput.Price <= 0 {
+ return fmt.Errorf("price field must be a positive integer")
+ }
+
+ // ==== Check if marble already exists ====
+ marbleAsBytes, err := ctx.GetStub().GetPrivateData("collectionMarbles", marbleInput.Name)
+ if err != nil {
+ return fmt.Errorf("Failed to get marble: " + err.Error())
+ } else if marbleAsBytes != nil {
+ fmt.Println("This marble already exists: " + marbleInput.Name)
+ return fmt.Errorf("This marble already exists: " + marbleInput.Name)
+ }
+
+ // ==== Create marble object, marshal to JSON, and save to state ====
+ marble := &Marble{
+ ObjectType: "Marble",
+ Name: marbleInput.Name,
+ Color: marbleInput.Color,
+ Size: marbleInput.Size,
+ Owner: marbleInput.Owner,
+ }
+ marbleJSONasBytes, err := json.Marshal(marble)
+ if err != nil {
+ return fmt.Errorf(err.Error())
+ }
+
+ // === Save marble to state ===
+ err = ctx.GetStub().PutPrivateData("collectionMarbles", marbleInput.Name, marbleJSONasBytes)
+ if err != nil {
+ return fmt.Errorf("failed to put Marble: %s", err.Error())
+ }
+
+ // ==== Create marble private details object with price, marshal to JSON, and save to state ====
+ marblePrivateDetails := &MarblePrivateDetails{
+ ObjectType: "MarblePrivateDetails",
+ Name: marbleInput.Name,
+ Price: marbleInput.Price,
+ }
+ marblePrivateDetailsAsBytes, err := json.Marshal(marblePrivateDetails)
+ if err != nil {
+ return fmt.Errorf(err.Error())
+ }
+ err = ctx.GetStub().PutPrivateData("collectionMarblePrivateDetails", marbleInput.Name, marblePrivateDetailsAsBytes)
+ if err != nil {
+ return fmt.Errorf("failed to put Marble private details: %s", err.Error())
+ }
+
+ // ==== Index the marble to enable color-based range queries, e.g. return all blue marbles ====
+ // An 'index' is a normal key/value entry in state.
+ // The key is a composite key, with the elements that you want to range query on listed first.
+ // In our case, the composite key is based on indexName=color~name.
+ // This will enable very efficient state range queries based on composite keys matching indexName=color~*
+ indexName := "color~name"
+ colorNameIndexKey, err := ctx.GetStub().CreateCompositeKey(indexName, []string{marble.Color, marble.Name})
+ if err != nil {
+ return err
+ }
+ // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble.
+ // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value
+ value := []byte{0x00}
+ err = ctx.GetStub().PutPrivateData("collectionMarbles", colorNameIndexKey, value)
+
+ // ==== Marble saved and indexed. Return success ====
+
+ return nil
+
+}
+
+// ===============================================
+// readMarble - read a marble from chaincode state
+// ===============================================
+
+func (s *SmartContract) ReadMarble(ctx contractapi.TransactionContextInterface, marbleID string) (*Marble, error) {
+
+ marbleJSON, err := ctx.GetStub().GetPrivateData("collectionMarbles", marbleID) //get the marble from chaincode state
+ if err != nil {
+ return nil, fmt.Errorf("failed to read from marble %s", err.Error())
+ }
+ if marbleJSON == nil {
+ return nil, fmt.Errorf("%s does not exist", marbleID)
+ }
+
+ marble := new(Marble)
+ _ = json.Unmarshal(marbleJSON, marble)
+
+ return marble, nil
+
+}
+
+// ===============================================
+// ReadMarblePrivateDetails - read a marble private details from chaincode state
+// ===============================================
+func (s *SmartContract) ReadMarblePrivateDetails(ctx contractapi.TransactionContextInterface, marbleID string) (*MarblePrivateDetails, error) {
+
+ marbleDetailsJSON, err := ctx.GetStub().GetPrivateData("collectionMarblePrivateDetails", marbleID) //get the marble from chaincode state
+ if err != nil {
+ return nil, fmt.Errorf("failed to read from marble details %s", err.Error())
+ }
+ if marbleDetailsJSON == nil {
+ return nil, fmt.Errorf("%s does not exist", marbleID)
+ }
+
+ marbleDetails := new(MarblePrivateDetails)
+ _ = json.Unmarshal(marbleDetailsJSON, marbleDetails)
+
+ return marbleDetails, nil
+}
+
+// ==================================================
+// delete - remove a marble key/value pair from state
+// ==================================================
+func (s *SmartContract) Delete(ctx contractapi.TransactionContextInterface) error {
+
+ transMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("Error getting transient: " + err.Error())
+ }
+
+ // Marble properties are private, therefore they get passed in transient field
+ transientDeleteMarbleJSON, ok := transMap["marble_delete"]
+ if !ok {
+ return fmt.Errorf("marble to delete not found in the transient map")
+ }
+
+ type marbleDelete struct {
+ Name string `json:"name"`
+ }
+
+ var marbleDeleteInput marbleDelete
+ err = json.Unmarshal(transientDeleteMarbleJSON, &marbleDeleteInput)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
+ }
+
+ if len(marbleDeleteInput.Name) == 0 {
+ return fmt.Errorf("name field must be a non-empty string")
+ }
+
+ // to maintain the color~name index, we need to read the marble first and get its color
+ valAsbytes, err := ctx.GetStub().GetPrivateData("collectionMarbles", marbleDeleteInput.Name) //get the marble from chaincode state
+ if err != nil {
+ return fmt.Errorf("failed to read marble: %s", err.Error())
+ }
+ if valAsbytes == nil {
+ return fmt.Errorf("marble private details does not exist: %s", marbleDeleteInput.Name)
+ }
+
+ var marbleToDelete Marble
+ err = json.Unmarshal([]byte(valAsbytes), &marbleToDelete)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
+ }
+
+ // delete the marble from state
+ err = ctx.GetStub().DelPrivateData("collectionMarbles", marbleDeleteInput.Name)
+ if err != nil {
+ return fmt.Errorf("Failed to delete state:" + err.Error())
+ }
+
+ // Also delete the marble from the color~name index
+ indexName := "color~name"
+ colorNameIndexKey, err := ctx.GetStub().CreateCompositeKey(indexName, []string{marbleToDelete.Color, marbleToDelete.Name})
+ if err != nil {
+ return err
+ }
+ err = ctx.GetStub().DelPrivateData("collectionMarbles", colorNameIndexKey)
+ if err != nil {
+ return fmt.Errorf("Failed to delete marble:" + err.Error())
+ }
+
+ // Finally, delete private details of marble
+ err = ctx.GetStub().DelPrivateData("collectionMarblePrivateDetails", marbleDeleteInput.Name)
+ if err != nil {
+ return err
+ }
+
+ return nil
+
+}
+
+// ===========================================================
+// transfer a marble by setting a new owner name on the marble
+// ===========================================================
+func (s *SmartContract) TransferMarble(ctx contractapi.TransactionContextInterface) error {
+
+ transMap, err := ctx.GetStub().GetTransient()
+ if err != nil {
+ return fmt.Errorf("Error getting transient: " + err.Error())
+ }
+
+ // Marble properties are private, therefore they get passed in transient field
+ transientTransferMarbleJSON, ok := transMap["marble_owner"]
+ if !ok {
+ return fmt.Errorf("marble owner not found in the transient map")
+ }
+
+ type marbleTransferTransientInput struct {
+ Name string `json:"name"`
+ Owner string `json:"owner"`
+ }
+
+ var marbleTransferInput marbleTransferTransientInput
+ err = json.Unmarshal(transientTransferMarbleJSON, &marbleTransferInput)
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
+ }
+
+
+ if len(marbleTransferInput.Name) == 0 {
+ return fmt.Errorf("name field must be a non-empty string")
+ }
+ if len(marbleTransferInput.Owner) == 0 {
+ return fmt.Errorf("owner field must be a non-empty string")
+ }
+
+ marbleAsBytes, err := ctx.GetStub().GetPrivateData("collectionMarbles", marbleTransferInput.Name)
+ if err != nil {
+ return fmt.Errorf("Failed to get marble:" + err.Error())
+ } else if marbleAsBytes == nil {
+ return fmt.Errorf("Marble does not exist: " + marbleTransferInput.Name)
+ }
+
+ marbleToTransfer := Marble{}
+ err = json.Unmarshal(marbleAsBytes, &marbleToTransfer) //unmarshal it aka JSON.parse()
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
+ }
+
+ marbleToTransfer.Owner = marbleTransferInput.Owner //change the owner
+
+ marbleJSONasBytes, _ := json.Marshal(marbleToTransfer)
+ err = ctx.GetStub().PutPrivateData("collectionMarbles", marbleToTransfer.Name, marbleJSONasBytes) //rewrite the marble
+ if err != nil {
+ return err
+ }
+
+ return nil
+
+}
+
+// ===========================================================================================
+// getMarblesByRange performs a range query based on the start and end keys provided.
+
+// Read-only function results are not typically submitted to ordering. If the read-only
+// results are submitted to ordering, or if the query is used in an update transaction
+// and submitted to ordering, then the committing peers will re-execute to guarantee that
+// result sets are stable between endorsement time and commit time. The transaction is
+// invalidated by the committing peers if the result set has changed between endorsement
+// time and commit time.
+// Therefore, range queries are a safe option for performing update transactions based on query results.
+// ===========================================================================================
+func (s *SmartContract) GetMarblesByRange(ctx contractapi.TransactionContextInterface, startKey string, endKey string) ([]Marble, error) {
+
+ resultsIterator, err := ctx.GetStub().GetPrivateDataByRange("collectionMarbles", startKey, endKey)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ results := []Marble{}
+
+ for resultsIterator.HasNext() {
+ response, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ newMarble := new(Marble)
+
+ err = json.Unmarshal(response.Value, newMarble)
+ if err != nil {
+ return nil, err
+ }
+
+ results = append(results, *newMarble)
+ }
+
+ return results, nil
+
+}
+
+
+// =======Rich queries =========================================================================
+// Two examples of rich queries are provided below (parameterized query and ad hoc query).
+// Rich queries pass a query string to the state database.
+// Rich queries are only supported by state database implementations
+// that support rich query (e.g. CouchDB).
+// The query string is in the syntax of the underlying state database.
+// With rich queries there is no guarantee that the result set hasn't changed between
+// endorsement time and commit time, aka 'phantom reads'.
+// Therefore, rich queries should not be used in update transactions, unless the
+// application handles the possibility of result set changes between endorsement and commit time.
+// Rich queries can be used for point-in-time queries against a peer.
+// ============================================================================================
+
+// ===== Example: Parameterized rich query =================================================
+// queryMarblesByOwner queries for marbles based on a passed in owner.
+// This is an example of a parameterized query where the query logic is baked into the chaincode,
+// and accepting a single query parameter (owner).
+// Only available on state databases that support rich query (e.g. CouchDB)
+// =========================================================================================
+func (s *SmartContract) QueryMarblesByOwner(ctx contractapi.TransactionContextInterface, owner string) ([]Marble, error) {
+
+ ownerString := strings.ToLower(owner)
+
+ queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"marble\",\"owner\":\"%s\"}}", ownerString)
+
+ queryResults, err := s.getQueryResultForQueryString(ctx, queryString)
+ if err != nil {
+ return nil, err
+ }
+ return queryResults, nil
+}
+
+// ===== Example: Ad hoc rich query ========================================================
+// queryMarbles uses a query string to perform a query for marbles.
+// Query string matching state database syntax is passed in and executed as is.
+// Supports ad hoc queries that can be defined at runtime by the client.
+// If this is not desired, follow the queryMarblesForOwner example for parameterized queries.
+// Only available on state databases that support rich query (e.g. CouchDB)
+// =========================================================================================
+func (s *SmartContract) QueryMarbles(ctx contractapi.TransactionContextInterface, queryString string) ([]Marble, error) {
+
+ queryResults, err := s.getQueryResultForQueryString(ctx, queryString)
+ if err != nil {
+ return nil, err
+ }
+ return queryResults, nil
+}
+
+// =========================================================================================
+// getQueryResultForQueryString executes the passed in query string.
+// Result set is built and returned as a byte array containing the JSON results.
+// =========================================================================================
+func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]Marble, error) {
+
+ resultsIterator, err := ctx.GetStub().GetPrivateDataQueryResult("collectionMarbles", queryString)
+ if err != nil {
+ return nil, err
+ }
+ defer resultsIterator.Close()
+
+ results := []Marble{}
+
+ for resultsIterator.HasNext() {
+ response, err := resultsIterator.Next()
+ if err != nil {
+ return nil, err
+ }
+
+ newMarble := new(Marble)
+
+ err = json.Unmarshal(response.Value, newMarble)
+ if err != nil {
+ return nil, err
+ }
+
+ results = append(results, *newMarble)
+ }
+ return results, nil
+}
+
+// ===============================================
+// getMarbleHash - use the public data hash to verify a private marble
+// Result is the hash on the public ledger of a marble stored a private data collection
+// ===============================================
+func (s *SmartContract) GetMarbleHash(ctx contractapi.TransactionContextInterface, collection string, marbleID string,) (string, error) {
+
+ // GetPrivateDataHash can use any collection deployed with the chaincode as input
+ hashAsbytes, err := ctx.GetStub().GetPrivateDataHash(collection, marbleID)
+ if err != nil {
+ return "", fmt.Errorf("Failed to get public data hash for marble:" + err.Error())
+ } else if hashAsbytes == nil {
+ return "", fmt.Errorf("Marble does not exist: " + marbleID)
+ }
+
+ return string(hashAsbytes), nil
+}
+
+func main() {
+
+ chaincode, err := contractapi.NewChaincode(new(SmartContract))
+
+ if err != nil {
+ fmt.Printf("Error creating private mables chaincode: %s", err.Error())
+ return
+ }
+
+ if err := chaincode.Start(); err != nil {
+ fmt.Printf("Error starting private mables chaincode: %s", err.Error())
+ }
+}
diff --git a/chaincode/sacc/go.mod b/chaincode/sacc/go.mod
new file mode 100644
index 0000000..f9deb8a
--- /dev/null
+++ b/chaincode/sacc/go.mod
@@ -0,0 +1,12 @@
+module github.com/hyperledger/fabric-samples/chaincode/sacc
+
+go 1.12
+
+require (
+ github.com/hyperledger/fabric-chaincode-go v0.0.0-20190823162523-04390e015b85
+ github.com/hyperledger/fabric-protos-go v0.0.0-20190821214336-621b908d5022
+ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
+ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
+ golang.org/x/text v0.3.2 // indirect
+ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect
+)
diff --git a/chaincode/sacc/go.sum b/chaincode/sacc/go.sum
new file mode 100644
index 0000000..da5b571
--- /dev/null
+++ b/chaincode/sacc/go.sum
@@ -0,0 +1,82 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20190823162523-04390e015b85 h1:VEm3tPRTCzq3J/1XpVERh1PbOSnshUVwx2G5s3cLiTw=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20190823162523-04390e015b85/go.mod h1:HZK6PKLWrvdD/t0oSLiyaRaUM6fZ7qjJuOlb0zrn0mo=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190821214336-621b908d5022 h1:WzttYAPO5xkQ87ZrxzEhvDZknfarSNu1PZt3NPMTE3Y=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190821214336-621b908d5022/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/chaincode/sacc/sacc.go b/chaincode/sacc/sacc.go
new file mode 100644
index 0000000..5fffe97
--- /dev/null
+++ b/chaincode/sacc/sacc.go
@@ -0,0 +1,97 @@
+/*
+ * Copyright IBM Corp All Rights Reserved
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-protos-go/peer"
+)
+
+// SimpleAsset implements a simple chaincode to manage an asset
+type SimpleAsset struct {
+}
+
+// Init is called during chaincode instantiation to initialize any
+// data. Note that chaincode upgrade also calls this function to reset
+// or to migrate data.
+func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
+ // Get the args from the transaction proposal
+ args := stub.GetStringArgs()
+ if len(args) != 2 {
+ return shim.Error("Incorrect arguments. Expecting a key and a value")
+ }
+
+ // Set up any variables or assets here by calling stub.PutState()
+
+ // We store the key and the value on the ledger
+ err := stub.PutState(args[0], []byte(args[1]))
+ if err != nil {
+ return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
+ }
+ return shim.Success(nil)
+}
+
+// Invoke is called per transaction on the chaincode. Each transaction is
+// either a 'get' or a 'set' on the asset created by Init function. The Set
+// method may create a new asset by specifying a new key-value pair.
+func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
+ // Extract the function and args from the transaction proposal
+ fn, args := stub.GetFunctionAndParameters()
+
+ var result string
+ var err error
+ if fn == "set" {
+ result, err = set(stub, args)
+ } else { // assume 'get' even if fn is nil
+ result, err = get(stub, args)
+ }
+ if err != nil {
+ return shim.Error(err.Error())
+ }
+
+ // Return the result as success payload
+ return shim.Success([]byte(result))
+}
+
+// Set stores the asset (both key and value) on the ledger. If the key exists,
+// it will override the value with the new one
+func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
+ if len(args) != 2 {
+ return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
+ }
+
+ err := stub.PutState(args[0], []byte(args[1]))
+ if err != nil {
+ return "", fmt.Errorf("Failed to set asset: %s", args[0])
+ }
+ return args[1], nil
+}
+
+// Get returns the value of the specified asset key
+func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
+ if len(args) != 1 {
+ return "", fmt.Errorf("Incorrect arguments. Expecting a key")
+ }
+
+ value, err := stub.GetState(args[0])
+ if err != nil {
+ return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
+ }
+ if value == nil {
+ return "", fmt.Errorf("Asset not found: %s", args[0])
+ }
+ return string(value), nil
+}
+
+// main function starts up the chaincode in the container during instantiate
+func main() {
+ if err := shim.Start(new(SimpleAsset)); err != nil {
+ fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
+ }
+}
diff --git a/chaincode/sacc/sacc_test.go b/chaincode/sacc/sacc_test.go
new file mode 100644
index 0000000..7ab39c9
--- /dev/null
+++ b/chaincode/sacc/sacc_test.go
@@ -0,0 +1,174 @@
+/*
+Copyright Hitachi America Ltd. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hyperledger/fabric-chaincode-go/shim"
+ "github.com/hyperledger/fabric-chaincode-go/shimtest"
+)
+
+func checkInit(t *testing.T, stub *shimtest.MockStub, args [][]byte) {
+ res := stub.MockInit("1", args)
+ if res.Status != shim.OK {
+ fmt.Println("Init failed", string(res.Message))
+ t.FailNow()
+ }
+}
+
+func checkState(t *testing.T, stub *shimtest.MockStub, name string, value string) {
+ bytes := stub.State[name]
+ if bytes == nil {
+ fmt.Println("State", name, "failed to get value")
+ t.FailNow()
+ }
+ if string(bytes) != value {
+ fmt.Println("State value", name, "was not", value, "as expected")
+ t.FailNow()
+ }
+}
+
+func checkQuery(t *testing.T, stub *shimtest.MockStub, name string, value string) {
+ res := stub.MockInvoke("1", [][]byte{[]byte("query"), []byte(name)})
+ if res.Status != shim.OK {
+ fmt.Println("Query", name, "failed", string(res.Message))
+ t.FailNow()
+ }
+ if res.Payload == nil {
+ fmt.Println("Query", name, "failed to get value")
+ t.FailNow()
+ }
+ if string(res.Payload) != value {
+ fmt.Println("Query value", name, "was not", value, "as expected")
+ t.FailNow()
+ }
+}
+
+func checkInvoke(t *testing.T, stub *shimtest.MockStub, args [][]byte) {
+ res := stub.MockInvoke("1", args)
+ if res.Status != shim.OK {
+ fmt.Println("Invoke", args, "failed", string(res.Message))
+ t.FailNow()
+ }
+}
+
+func TestSacc_Init(t *testing.T) {
+ cc := new(SimpleAsset)
+ stub := shimtest.NewMockStub("sacc", cc)
+
+ // Init a=10
+ checkInit(t, stub, [][]byte{[]byte("a"), []byte("10")})
+
+ checkState(t, stub, "a", "10")
+}
+
+func TestSacc_Query(t *testing.T) {
+ cc := new(SimpleAsset)
+ stub := shimtest.NewMockStub("sacc", cc)
+
+ // Init a=10
+ checkInit(t, stub, [][]byte{[]byte("a"), []byte("10")})
+
+ // Query a
+ checkQuery(t, stub, "a", "10")
+}
+
+func TestSacc_Invoke(t *testing.T) {
+ cc := new(SimpleAsset)
+ stub := shimtest.NewMockStub("sacc", cc)
+
+ // Init a=10
+ checkInit(t, stub, [][]byte{[]byte("a"), []byte("10")})
+
+ // Invoke: Set a=20
+ checkInvoke(t, stub, [][]byte{[]byte("set"), []byte("a"), []byte("20")})
+
+ // Query a
+ checkQuery(t, stub, "a", "20")
+}
+
+func TestSacc_InitWithIncorrectArguments(t *testing.T) {
+ cc := new(SimpleAsset)
+ stub := shimtest.NewMockStub("sacc", cc)
+
+ // Init with incorrect arguments
+ res := stub.MockInit("1", [][]byte{[]byte("a"), []byte("10"), []byte("10")})
+
+ if res.Status != shim.ERROR {
+ fmt.Println("Invalid Init accepted")
+ t.FailNow()
+ }
+
+ if res.Message != "Incorrect arguments. Expecting a key and a value" {
+ fmt.Println("Unexpected Error message:", string(res.Message))
+ t.FailNow()
+ }
+}
+
+func TestSacc_QueryWithIncorrectArguments(t *testing.T) {
+ cc := new(SimpleAsset)
+ stub := shimtest.NewMockStub("sacc", cc)
+
+ // Init a=10
+ checkInit(t, stub, [][]byte{[]byte("a"), []byte("10")})
+
+ // Query with incorrect arguments
+ res := stub.MockInvoke("1", [][]byte{[]byte("query"), []byte("a"), []byte("b")})
+
+ if res.Status != shim.ERROR {
+ fmt.Println("Invalid query accepted")
+ t.FailNow()
+ }
+
+ if res.Message != "Incorrect arguments. Expecting a key" {
+ fmt.Println("Unexpected Error message:", string(res.Message))
+ t.FailNow()
+ }
+}
+
+func TestSacc_QueryForAssetNotFound(t *testing.T) {
+ cc := new(SimpleAsset)
+ stub := shimtest.NewMockStub("sacc", cc)
+
+ // Init a=10
+ checkInit(t, stub, [][]byte{[]byte("a"), []byte("10")})
+
+ // Query for b (as a asset not found)
+ res := stub.MockInvoke("1", [][]byte{[]byte("query"), []byte("b")})
+
+ if res.Status != shim.ERROR {
+ fmt.Println("Invalid query accepted")
+ t.FailNow()
+ }
+
+ if res.Message != "Asset not found: b" {
+ fmt.Println("Unexpected Error message:", string(res.Message))
+ t.FailNow()
+ }
+}
+
+func TestSacc_InvokeWithIncorrectArguments(t *testing.T) {
+ cc := new(SimpleAsset)
+ stub := shimtest.NewMockStub("sacc", cc)
+
+ // Init a=10
+ checkInit(t, stub, [][]byte{[]byte("a"), []byte("10")})
+
+ // Invoke with incorrect arguments
+ res := stub.MockInvoke("1", [][]byte{[]byte("set"), []byte("a")})
+ if res.Status != shim.ERROR {
+ fmt.Println("Invalid Invoke accepted")
+ t.FailNow()
+ }
+
+ if res.Message != "Incorrect arguments. Expecting a key and a value" {
+ fmt.Println("Unexpected Error message:", string(res.Message))
+ t.FailNow()
+ }
+}
diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml
new file mode 100644
index 0000000..5d314a8
--- /dev/null
+++ b/ci/azure-pipelines.yml
@@ -0,0 +1,255 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+trigger:
+ - master
+ - release-1.4
+
+variables:
+ FABRIC_VERSION: 2.2
+ GO_BIN: $(Build.Repository.LocalPath)/bin
+ GO_VER: 1.14.6
+ NODE_VER: 12.x
+ PATH: $(Build.Repository.LocalPath)/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
+
+jobs:
+ - job: CommercialPaper_Go
+ displayName: Commercial Paper (Go)
+ pool:
+ vmImage: ubuntu-18.04
+ steps:
+ - template: templates/install-deps.yml
+ - template: templates/commercial-paper/azure-pipelines-go.yml
+
+ - job: CommercialPaper_Java
+ displayName: Commercial Paper (Java)
+ pool:
+ vmImage: ubuntu-18.04
+ steps:
+ - template: templates/install-deps.yml
+ - template: templates/commercial-paper/azure-pipelines-java.yml
+
+ - job: CommercialPaper_JavaScript
+ displayName: Commercial Paper (JavaScript)
+ pool:
+ vmImage: ubuntu-18.04
+ steps:
+ - template: templates/install-deps.yml
+ - template: templates/commercial-paper/azure-pipelines-javascript.yml
+
+ - job: FabCar_Go
+ displayName: FabCar (Go)
+ pool:
+ vmImage: ubuntu-18.04
+ steps:
+ - template: templates/install-deps.yml
+ - template: templates/fabcar/azure-pipelines-go.yml
+
+ - job: FabCar_Java
+ displayName: FabCar (Java)
+ pool:
+ vmImage: ubuntu-18.04
+ steps:
+ - template: templates/install-deps.yml
+ - template: templates/fabcar/azure-pipelines-java.yml
+
+ - job: FabCar_JavaScript
+ displayName: FabCar (JavaScript)
+ pool:
+ vmImage: ubuntu-18.04
+ steps:
+ - template: templates/install-deps.yml
+ - template: templates/fabcar/azure-pipelines-javascript.yml
+
+ - job: Fabcar_TypeScript
+ displayName: FabCar (TypeScript)
+ pool:
+ vmImage: ubuntu-18.04
+ steps:
+ - template: templates/install-deps.yml
+ - template: templates/fabcar/azure-pipelines-typescript.yml
+
+ - job: Lint
+ displayName: Lint
+ pool:
+ vmImage: ubuntu-18.04
+ strategy:
+ matrix:
+ Basic-Application-Go:
+ DIRECTORY: asset-transfer-basic
+ LANGUAGE: go
+ TYPE: application
+ Basic-Application-Java:
+ DIRECTORY: asset-transfer-basic
+ LANGUAGE: java
+ TYPE: application
+ Basic-Application-Javascript:
+ DIRECTORY: asset-transfer-basic
+ LANGUAGE: javascript
+ TYPE: application
+ Basic-Chaincode-Go:
+ DIRECTORY: asset-transfer-basic
+ LANGUAGE: go
+ TYPE: chaincode
+ Basic-Chaincode-Java:
+ DIRECTORY: asset-transfer-basic
+ LANGUAGE: java
+ TYPE: chaincode
+ Basic-Chaincode-Javascript:
+ DIRECTORY: asset-transfer-basic
+ LANGUAGE: javascript
+ TYPE: chaincode
+ Basic-Chaincode-Typescript:
+ DIRECTORY: asset-transfer-basic
+ LANGUAGE: typescript
+ TYPE: chaincode
+ Ledger-Application-Java:
+ DIRECTORY: asset-transfer-ledger-queries
+ LANGUAGE: java
+ TYPE: application
+ Ledger-Chaincode-Go:
+ DIRECTORY: asset-transfer-ledger-queries
+ LANGUAGE: go
+ TYPE: chaincode
+ Ledger-Chaincode-Javascript:
+ DIRECTORY: asset-transfer-ledger-queries
+ LANGUAGE: javascript
+ TYPE: chaincode
+ PrivateData-Application-Javascript:
+ DIRECTORY: asset-transfer-private-data
+ LANGUAGE: javascript
+ TYPE: application
+ PrivateData-Chaincode-Go:
+ DIRECTORY: asset-transfer-private-data
+ LANGUAGE: go
+ TYPE: chaincode
+ SBE-Chaincode-Typescript:
+ DIRECTORY: asset-transfer-sbe
+ LANGUAGE: typescript
+ TYPE: chaincode
+ SBE-Chaincode-Java:
+ DIRECTORY: asset-transfer-sbe
+ LANGUAGE: java
+ TYPE: chaincode
+ Secured-Chaincode-Go:
+ DIRECTORY: asset-transfer-secured-agreement
+ LANGUAGE: go
+ TYPE: chaincode
+ steps:
+ - task: GoTool@0
+ inputs:
+ goBin: $(GO_BIN)
+ version: $(GO_VER)
+ displayName: Install GoLang
+ - task: NodeTool@0
+ inputs:
+ versionSpec: $(NODE_VER)
+ displayName: Install Node.js
+ - script: ./ci/scripts/lint.sh
+ displayName: Lint Code
+
+ - job: TestNetworkBasic
+ displayName: Test Network
+ pool:
+ vmImage: ubuntu-18.04
+ strategy:
+ matrix:
+ Basic-Go:
+ CHAINCODE_NAME: basic
+ CHAINCODE_LANGUAGE: go
+ Basic-Java:
+ CHAINCODE_NAME: basic
+ CHAINCODE_LANGUAGE: java
+ Basic-Javascript:
+ CHAINCODE_NAME: basic
+ CHAINCODE_LANGUAGE: javascript
+ Basic-Typescript:
+ CHAINCODE_NAME: basic
+ CHAINCODE_LANGUAGE: typescript
+ steps:
+ - template: templates/install-deps.yml
+ - script: ../ci/scripts/run-test-network-basic.sh
+ workingDirectory: test-network
+ displayName: Run Test Network Basic Chaincode
+
+ - job: TestNetworkLedger
+ displayName: Test Network
+ pool:
+ vmImage: ubuntu-18.04
+ strategy:
+ matrix:
+ Ledger-Go:
+ CHAINCODE_NAME: ledger
+ CHAINCODE_LANGUAGE: go
+ Ledger-Javascript:
+ CHAINCODE_NAME: ledger
+ CHAINCODE_LANGUAGE: javascript
+ steps:
+ - template: templates/install-deps.yml
+ - script: ../ci/scripts/run-test-network-ledger.sh
+ workingDirectory: test-network
+ displayName: Run Test Network Ledger Chaincode
+
+ - job: TestNetworkPrivate
+ displayName: Test Network
+ pool:
+ vmImage: ubuntu-18.04
+ strategy:
+ matrix:
+ Private-Go:
+ CHAINCODE_NAME: private
+ CHAINCODE_LANGUAGE: go
+ steps:
+ - template: templates/install-deps.yml
+ - script: ../ci/scripts/run-test-network-private.sh
+ workingDirectory: test-network
+ displayName: Run Test Network Private Chaincode
+
+ - job: TestNetworkSBE
+ displayName: Test Network
+ pool:
+ vmImage: ubuntu-18.04
+ strategy:
+ matrix:
+ SBE-Typescript:
+ CHAINCODE_NAME: sbe
+ CHAINCODE_LANGUAGE: typescript
+ SBE-Java:
+ CHAINCODE_NAME: sbe
+ CHAINCODE_LANGUAGE: java
+ steps:
+ - template: templates/install-deps.yml
+ - script: ../ci/scripts/run-test-network-sbe.sh
+ workingDirectory: test-network
+ displayName: Run Test Network SBE Chaincode
+
+ - job: TestNetworkSecured
+ displayName: Test Network
+ pool:
+ vmImage: ubuntu-18.04
+ strategy:
+ matrix:
+ Secured-Go:
+ CHAINCODE_NAME: secured
+ CHAINCODE_LANGUAGE: go
+ steps:
+ - template: templates/install-deps.yml
+ - script: ../ci/scripts/run-test-network-secured.sh
+ workingDirectory: test-network
+ displayName: Run Test Network Secured Chaincode
+
+ - job: TestNetworkEvents
+ displayName: Test Network
+ pool:
+ vmImage: ubuntu-18.04
+ strategy:
+ matrix:
+ Events-Javascript:
+ CHAINCODE_NAME: events
+ CHAINCODE_LANGUAGE: javascript
+ steps:
+ - template: templates/install-deps.yml
+ - script: ../ci/scripts/run-test-network-events.sh
+ workingDirectory: test-network
+ displayName: Run Test Network Events Chaincode
diff --git a/ci/scripts/lint.sh b/ci/scripts/lint.sh
new file mode 100755
index 0000000..275b4d6
--- /dev/null
+++ b/ci/scripts/lint.sh
@@ -0,0 +1,51 @@
+set -euo pipefail
+
+function print() {
+ GREEN='\033[0;32m'
+ NC='\033[0m'
+ echo
+ echo -e "${GREEN}${1}${NC}"
+}
+
+if [[ "${LANGUAGE}" == "go" ]]; then
+ go get golang.org/x/tools/cmd/goimports
+
+ cd "${DIRECTORY}/${TYPE}-${LANGUAGE}"
+ print "Running go vet"
+ go vet ./...
+
+ print "Running gofmt"
+ output=$(gofmt -l -s $(go list -f '{{.Dir}}' ./...))
+ if [[ "${output}" != "" ]]; then
+ print "The following files contain formatting errors, please run 'gofmt -l -w ' to fix these issues:"
+ echo "${output}"
+ fi
+
+ print "Running goimports"
+ output=$(goimports -l $(go list -f '{{.Dir}}' ./...))
+ if [[ "${output}" != "" ]]; then
+ print "The following files contain import errors, please run 'goimports -l -w ' to fix these issues:"
+ echo "${output}"
+ fi
+elif [[ "${LANGUAGE}" == "java" ]]; then
+ cd "${DIRECTORY}/${TYPE}-${LANGUAGE}"
+ print "Running Gradle Build"
+ ./gradlew build
+elif [[ "${LANGUAGE}" == "javascript" ]]; then
+ npm install -g eslint
+ cd "${DIRECTORY}/${TYPE}-${LANGUAGE}"
+ print "Running ESLint"
+ if [[ "${TYPE}" == "chaincode" ]]; then
+ eslint *.js */**.js
+ else
+ eslint *.js
+ fi
+elif [[ "${LANGUAGE}" == "typescript" ]]; then
+ npm install -g typescript tslint
+ cd "${DIRECTORY}/${TYPE}-${LANGUAGE}"
+ print "Running TSLint"
+ tslint --project .
+else
+ echo "Language not supported"
+ exit 1
+fi
diff --git a/ci/scripts/pullFabricImages.sh b/ci/scripts/pullFabricImages.sh
new file mode 100755
index 0000000..38cb8e2
--- /dev/null
+++ b/ci/scripts/pullFabricImages.sh
@@ -0,0 +1,15 @@
+#!/bin/bash -e
+set -euo pipefail
+
+FABRIC_VERSION=${FABRIC_VERSION:-2.2}
+STABLE_TAG=amd64-${FABRIC_VERSION}-stable
+
+for image in baseos peer orderer ca tools orderer ccenv javaenv nodeenv tools; do
+ docker pull -q "hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG}"
+ docker tag "hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG}" hyperledger/fabric-${image}
+ docker tag "hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG}" "hyperledger/fabric-${image}:${FABRIC_VERSION}"
+ docker rmi -f "hyperledger-fabric.jfrog.io/fabric-${image}:${STABLE_TAG}"
+done
+
+docker pull -q couchdb:3.1.1
+docker images | grep hyperledger
diff --git a/ci/scripts/run-test-network-basic.sh b/ci/scripts/run-test-network-basic.sh
new file mode 100755
index 0000000..6cda208
--- /dev/null
+++ b/ci/scripts/run-test-network-basic.sh
@@ -0,0 +1,65 @@
+set -euo pipefail
+
+FABRIC_VERSION=${FABRIC_VERSION:-2.2}
+CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go}
+CHAINCODE_NAME=${CHAINCODE_NAME:-basic}
+CHAINCODE_PATH=${CHAINCODE_PATH:-../asset-transfer-basic}
+
+function print() {
+ GREEN='\033[0;32m'
+ NC='\033[0m'
+ echo
+ echo -e "${GREEN}${1}${NC}"
+}
+
+function createNetwork() {
+ print "Creating network"
+ ./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}"
+ print "Deploying ${CHAINCODE_NAME} chaincode"
+ ./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccp "${CHAINCODE_PATH}/chaincode-${CHAINCODE_LANGUAGE}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}"
+}
+
+function stopNetwork() {
+ print "Stopping network"
+ ./network.sh down
+}
+
+# Run Go application
+createNetwork
+print "Initializing Go application"
+pushd ../asset-transfer-basic/application-go
+print "Executing AssetTransfer.go"
+go run .
+popd
+stopNetwork
+
+# Run Java application
+createNetwork
+print "Initializing Java application"
+pushd ../asset-transfer-basic/application-java
+print "Executing Gradle Run"
+gradle run
+popd
+stopNetwork
+
+# Run Javascript application
+createNetwork
+print "Initializing Javascript application"
+pushd ../asset-transfer-basic/application-javascript
+npm install
+print "Executing app.js"
+node app.js
+popd
+stopNetwork
+
+# Run typescript application
+createNetwork
+print "Initializing Typescript application"
+pushd ../asset-transfer-basic/application-typescript
+npm install
+print "Building app.ts"
+npm run build
+print "Running the output app"
+node dist/app.js
+popd
+stopNetwork
diff --git a/ci/scripts/run-test-network-events.sh b/ci/scripts/run-test-network-events.sh
new file mode 100755
index 0000000..770f023
--- /dev/null
+++ b/ci/scripts/run-test-network-events.sh
@@ -0,0 +1,37 @@
+set -euo pipefail
+
+FABRIC_VERSION=${FABRIC_VERSION:-2.2}
+CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-javascript}
+CHAINCODE_NAME=${CHAINCODE_NAME:-events}
+CHAINCODE_PATH=${CHAINCODE_PATH:-../asset-transfer-events}
+
+function print() {
+ GREEN='\033[0;32m'
+ NC='\033[0m'
+ echo
+ echo -e "${GREEN}${1}${NC}"
+}
+
+function createNetwork() {
+ print "Creating network"
+ ./network.sh up createChannel -ca
+ print "Deploying ${CHAINCODE_NAME} chaincode"
+ ./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccp "${CHAINCODE_PATH}/chaincode-${CHAINCODE_LANGUAGE}" -ccl "${CHAINCODE_LANGUAGE}" -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
+}
+
+function stopNetwork() {
+ print "Stopping network"
+ ./network.sh down
+}
+
+# Run Javascript application
+createNetwork
+print "Initializing Javascript application"
+pushd ../asset-transfer-events/application-javascript
+npm install
+print "Executing app.js"
+node app.js
+popd
+stopNetwork
+print "Remove wallet storage"
+rm -R ../asset-transfer-events/application-javascript/wallet
diff --git a/ci/scripts/run-test-network-ledger.sh b/ci/scripts/run-test-network-ledger.sh
new file mode 100755
index 0000000..6546833
--- /dev/null
+++ b/ci/scripts/run-test-network-ledger.sh
@@ -0,0 +1,44 @@
+set -euo pipefail
+
+FABRIC_VERSION=${FABRIC_VERSION:-2.2}
+CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go}
+CHAINCODE_NAME=${CHAINCODE_NAME:-ledger}
+CHAINCODE_PATH=${CHAINCODE_PATH:-../asset-transfer-ledger-queries}
+
+function print() {
+ GREEN='\033[0;32m'
+ NC='\033[0m'
+ echo
+ echo -e "${GREEN}${1}${NC}"
+}
+
+function createNetwork() {
+ print "Creating network"
+ ./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}"
+ print "Deploying ${CHAINCODE_NAME} chaincode"
+ ./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccp "${CHAINCODE_PATH}/chaincode-${CHAINCODE_LANGUAGE}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}"
+}
+
+function stopNetwork() {
+ print "Stopping network"
+ ./network.sh down
+}
+
+# Run Java application
+createNetwork
+print "Initializing Java application"
+pushd ../asset-transfer-ledger-queries/application-java
+print "Executing Gradle Run"
+gradle run
+popd
+stopNetwork
+
+# Run Javascript application
+createNetwork
+print "Initializing Javascript application"
+pushd ../asset-transfer-ledger-queries/application-javascript
+npm install
+print "Executing app.js"
+node app.js
+popd
+stopNetwork
diff --git a/ci/scripts/run-test-network-private.sh b/ci/scripts/run-test-network-private.sh
new file mode 100755
index 0000000..19dff1a
--- /dev/null
+++ b/ci/scripts/run-test-network-private.sh
@@ -0,0 +1,35 @@
+set -euo pipefail
+
+FABRIC_VERSION=${FABRIC_VERSION:-2.2}
+CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go}
+CHAINCODE_NAME=${CHAINCODE_NAME:-private}
+CHAINCODE_PATH=${CHAINCODE_PATH:-../asset-transfer-private-data}
+
+function print() {
+ GREEN='\033[0;32m'
+ NC='\033[0m'
+ echo
+ echo -e "${GREEN}${1}${NC}"
+}
+
+function createNetwork() {
+ print "Creating network"
+ ./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}"
+ print "Deploying ${CHAINCODE_NAME} chaincode"
+ ./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccp "${CHAINCODE_PATH}/chaincode-${CHAINCODE_LANGUAGE}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}" -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -cccg ../asset-transfer-private-data/chaincode-go/collections_config.json
+}
+
+function stopNetwork() {
+ print "Stopping network"
+ ./network.sh down
+}
+
+# Run Javascript application
+createNetwork
+print "Initializing Javascript application"
+pushd ../asset-transfer-private-data/application-javascript
+npm install
+print "Executing app.js"
+node app.js
+popd
+stopNetwork
diff --git a/ci/scripts/run-test-network-sbe.sh b/ci/scripts/run-test-network-sbe.sh
new file mode 100755
index 0000000..c5ccfec
--- /dev/null
+++ b/ci/scripts/run-test-network-sbe.sh
@@ -0,0 +1,35 @@
+set -euo pipefail
+
+FABRIC_VERSION=${FABRIC_VERSION:-2.2}
+CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-typescript}
+CHAINCODE_NAME=${CHAINCODE_NAME:-sbe}
+CHAINCODE_PATH=${CHAINCODE_PATH:-../asset-transfer-sbe}
+
+function print() {
+ GREEN='\033[0;32m'
+ NC='\033[0m'
+ echo
+ echo -e "${GREEN}${1}${NC}"
+}
+
+function createNetwork() {
+ print "Creating network"
+ ./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}"
+ print "Deploying ${CHAINCODE_NAME} chaincode"
+ ./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccp "${CHAINCODE_PATH}/chaincode-${CHAINCODE_LANGUAGE}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}"
+}
+
+function stopNetwork() {
+ print "Stopping network"
+ ./network.sh down
+}
+
+# Run Javascript application
+createNetwork
+print "Initializing Javascript application"
+pushd ../asset-transfer-sbe/application-javascript
+npm install
+print "Executing app.js"
+node app.js
+popd
+stopNetwork
diff --git a/ci/scripts/run-test-network-secured.sh b/ci/scripts/run-test-network-secured.sh
new file mode 100755
index 0000000..6fb6a84
--- /dev/null
+++ b/ci/scripts/run-test-network-secured.sh
@@ -0,0 +1,37 @@
+set -euo pipefail
+
+FABRIC_VERSION=${FABRIC_VERSION:-2.2}
+CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go}
+CHAINCODE_NAME=${CHAINCODE_NAME:-secured}
+CHAINCODE_PATH=${CHAINCODE_PATH:-../asset-transfer-secured-agreement}
+
+function print() {
+ GREEN='\033[0;32m'
+ NC='\033[0m'
+ echo
+ echo -e "${GREEN}${1}${NC}"
+}
+
+function createNetwork() {
+ print "Creating network"
+ ./network.sh up createChannel -ca
+ print "Deploying ${CHAINCODE_NAME} chaincode"
+ ./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccp "${CHAINCODE_PATH}/chaincode-${CHAINCODE_LANGUAGE}" -ccl "${CHAINCODE_LANGUAGE}" -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
+}
+
+function stopNetwork() {
+ print "Stopping network"
+ ./network.sh down
+}
+
+# Run Javascript application
+createNetwork
+print "Initializing Javascript application"
+pushd ../asset-transfer-secured-agreement/application-javascript
+npm install
+print "Executing app.js"
+node app.js
+popd
+stopNetwork
+print "Remove wallet storage"
+rm -R ../asset-transfer-secured-agreement/application-javascript/wallet
diff --git a/ci/templates/commercial-paper/azure-pipelines-go.yml b/ci/templates/commercial-paper/azure-pipelines-go.yml
new file mode 100644
index 0000000..bb50ebd
--- /dev/null
+++ b/ci/templates/commercial-paper/azure-pipelines-go.yml
@@ -0,0 +1,97 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+steps:
+ - script: go test ./...
+ workingDirectory: commercial-paper/organization/magnetocorp/contract-go
+ displayName: Unit Test MagnetoCorp Chaincode
+ - script: go test ./...
+ workingDirectory: commercial-paper/organization/digibank/contract-go
+ displayName: Unit Test DigiBank Chaincode
+
+ - script: go mod vendor
+ workingDirectory: commercial-paper/organization/magnetocorp/contract-go
+ displayName: Vendor MagnetoCorp Dependencies
+ - script: go mod vendor
+ workingDirectory: commercial-paper/organization/digibank/contract-go
+ displayName: Vendor DigiBank Dependencies
+
+ - script: |
+ ./network.sh up createChannel -ca -s couchdb -i ${FABRIC_VERSION} # FABRIC_VERSION is set in ci/azure-pipelines.yml
+
+ # Copy the connection profiles so they are in the correct organizations.
+ cp "./organizations/peerOrganizations/org1.example.com/connection-org1.yaml" "../commercial-paper/organization/digibank/gateway/"
+ cp "./organizations/peerOrganizations/org2.example.com/connection-org2.yaml" "../commercial-paper/organization/magnetocorp/gateway/"
+ workingDirectory: test-network
+ displayName: Start Fabric
+ - script: |
+ source <(./magnetocorp.sh)
+ peer lifecycle chaincode package cp.tar.gz --lang golang --path ./contract-go --label cp_0
+ peer lifecycle chaincode install cp.tar.gz
+
+ export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+ echo $PACKAGE_ID
+
+ peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+ workingDirectory: commercial-paper/organization/magnetocorp
+ displayName: Setup Commercial Paper Contract
+ - script: |
+ source <(./digibank.sh)
+ peer lifecycle chaincode package cp.tar.gz --lang golang --path ./contract-go --label cp_0
+ peer lifecycle chaincode install cp.tar.gz
+
+ export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+ echo $PACKAGE_ID
+
+ peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+
+ peer lifecycle chaincode commit -o localhost:7050 \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel --name papercontract -v 0 \
+ --sequence 1 \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+ workingDirectory: commercial-paper/organization/digibank
+ displayName: Setup Commercial Paper Contract
+
+ - script: retry -- npm install
+ workingDirectory: commercial-paper/organization/magnetocorp/application
+ displayName: Install Magnetocorp Application
+ - script: |
+ set -ex
+ node enrollUser.js
+ node issue.js
+ workingDirectory: commercial-paper/organization/magnetocorp/application
+ displayName: MagnetoCorp Issue Paper
+
+ - script: retry -- npm install
+ workingDirectory: commercial-paper/organization/digibank/application
+ displayName: Install DigiBank Application
+ - script: |
+ set -ex
+ node enrollUser.js
+ node buy.js
+ node redeem.js
+ workingDirectory: commercial-paper/organization/digibank/application
+ displayName: Digibank Issue Paper
diff --git a/ci/templates/commercial-paper/azure-pipelines-java.yml b/ci/templates/commercial-paper/azure-pipelines-java.yml
new file mode 100644
index 0000000..6c4ba1b
--- /dev/null
+++ b/ci/templates/commercial-paper/azure-pipelines-java.yml
@@ -0,0 +1,92 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+steps:
+ - script: |
+ ./gradlew build
+ workingDirectory: commercial-paper/organization/digibank/contract-java
+ displayName: Build DigiBank Java Contract
+ - script: |
+ ./gradlew build
+ workingDirectory: commercial-paper/organization/magnetocorp/contract-java
+ displayName: Build MagnetoCorp Java Contract
+
+ - script: |
+ ./network.sh up createChannel -ca -s couchdb -i ${FABRIC_VERSION} # FABRIC_VERSION is set in ci/azure-pipelines.yml
+
+ # Copy the connection profiles so they are in the correct organizations.
+ cp "./organizations/peerOrganizations/org1.example.com/connection-org1.yaml" "../commercial-paper/organization/digibank/gateway/"
+ cp "./organizations/peerOrganizations/org2.example.com/connection-org2.yaml" "../commercial-paper/organization/magnetocorp/gateway/"
+ workingDirectory: test-network
+ displayName: Start Fabric
+ - script: |
+ source <(./magnetocorp.sh)
+ peer lifecycle chaincode package cp.tar.gz --lang java --path ./contract-java --label cp_0
+ peer lifecycle chaincode install cp.tar.gz
+
+ export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+ echo $PACKAGE_ID
+
+ peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+ workingDirectory: commercial-paper/organization/magnetocorp
+ displayName: Setup Commercial Paper Contract
+ - script: |
+ source <(./digibank.sh)
+ peer lifecycle chaincode package cp.tar.gz --lang java --path ./contract-java --label cp_0
+ peer lifecycle chaincode install cp.tar.gz
+
+ export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+ echo $PACKAGE_ID
+
+ peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+
+ peer lifecycle chaincode commit -o localhost:7050 \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel --name papercontract -v 0 \
+ --sequence 1 \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+ workingDirectory: commercial-paper/organization/digibank
+ displayName: Setup Commercial Paper Contract
+
+ - script: retry -- npm install
+ workingDirectory: commercial-paper/organization/magnetocorp/application
+ displayName: Install MagnetoCorp Application
+ - script: |
+ set -ex
+ node enrollUser.js
+ node issue.js
+ workingDirectory: commercial-paper/organization/magnetocorp/application
+ displayName: MagnetoCorp Issue Paper
+
+ - script: retry -- npm install
+ workingDirectory: commercial-paper/organization/digibank/application
+ displayName: Install DigiBank Application
+ - script: |
+ set -ex
+ node enrollUser.js
+ node buy.js
+ node redeem.js
+ workingDirectory: commercial-paper/organization/digibank/application
+ displayName: DigiBank Issue Paper
diff --git a/ci/templates/commercial-paper/azure-pipelines-javascript.yml b/ci/templates/commercial-paper/azure-pipelines-javascript.yml
new file mode 100644
index 0000000..9e30b97
--- /dev/null
+++ b/ci/templates/commercial-paper/azure-pipelines-javascript.yml
@@ -0,0 +1,82 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+steps:
+ - script: |
+ ./network.sh up createChannel -ca -s couchdb -i ${FABRIC_VERSION} # FABRIC_VERSION is set in ci/azure-pipelines.yml
+
+ # Copy the connection profiles so they are in the correct organizations.
+ cp "./organizations/peerOrganizations/org1.example.com/connection-org1.yaml" "../commercial-paper/organization/digibank/gateway/"
+ cp "./organizations/peerOrganizations/org2.example.com/connection-org2.yaml" "../commercial-paper/organization/magnetocorp/gateway/"
+ workingDirectory: test-network
+ displayName: Start Fabric
+ - script: |
+ source <(./magnetocorp.sh)
+ peer lifecycle chaincode package cp.tar.gz --lang node --path ./contract --label cp_0
+ peer lifecycle chaincode install cp.tar.gz
+
+ export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+ echo $PACKAGE_ID
+
+ peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+ workingDirectory: commercial-paper/organization/magnetocorp
+ displayName: Setup Commercial Paper Contract
+ - script: |
+ source <(./digibank.sh)
+ peer lifecycle chaincode package cp.tar.gz --lang node --path ./contract --label cp_0
+ peer lifecycle chaincode install cp.tar.gz
+
+ export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+ echo $PACKAGE_ID
+
+ peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+
+ peer lifecycle chaincode commit -o localhost:7050 \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel --name papercontract -v 0 \
+ --sequence 1 \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+ workingDirectory: commercial-paper/organization/digibank
+ displayName: Setup Commercial Paper Contract
+ - script: retry -- npm install
+ workingDirectory: commercial-paper/organization/magnetocorp/application
+ displayName: Install MagnetoCorp Application
+ - script: |
+ set -ex
+ node enrollUser.js
+ node issue.js
+ workingDirectory: commercial-paper/organization/magnetocorp/application
+ displayName: MagnetoCorp Issue Paper
+
+ - script: retry -- npm install
+ workingDirectory: commercial-paper/organization/digibank/application
+ displayName: Install DigiBank Application
+ - script: |
+ set -ex
+ node enrollUser.js
+ node buy.js
+ node redeem.js
+ workingDirectory: commercial-paper/organization/digibank/application
+ displayName: DigiBank Issue Paper
diff --git a/ci/templates/fabcar/azure-pipelines-go.yml b/ci/templates/fabcar/azure-pipelines-go.yml
new file mode 100644
index 0000000..6e1562c
--- /dev/null
+++ b/ci/templates/fabcar/azure-pipelines-go.yml
@@ -0,0 +1,21 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+steps:
+ - script: ./startFabric.sh go
+ workingDirectory: fabcar
+ displayName: Start Fabric
+ - task: GoTool@0
+ displayName: 'Use Go 1.14.2'
+ inputs:
+ version: '1.14.2'
+ - task: Go@0
+ displayName: 'go build'
+ inputs:
+ command: build
+ arguments: '-o "fabcar"'
+ workingDirectory: fabcar/go
+ - script: DISCOVERY_AS_LOCALHOST=TRUE ./fabcar
+ workingDirectory: fabcar/go
+ displayName: Run FabCar Application
diff --git a/ci/templates/fabcar/azure-pipelines-java.yml b/ci/templates/fabcar/azure-pipelines-java.yml
new file mode 100644
index 0000000..e166f74
--- /dev/null
+++ b/ci/templates/fabcar/azure-pipelines-java.yml
@@ -0,0 +1,14 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+steps:
+ - script: ./startFabric.sh java
+ workingDirectory: fabcar
+ displayName: Start Fabric
+ - script: retry -- mvn dependency:go-offline
+ workingDirectory: fabcar/java
+ displayName: Install FabCar Application Dependencies
+ - script: mvn test
+ workingDirectory: fabcar/java
+ displayName: Run FabCar Application
diff --git a/ci/templates/fabcar/azure-pipelines-javascript.yml b/ci/templates/fabcar/azure-pipelines-javascript.yml
new file mode 100644
index 0000000..dd4f474
--- /dev/null
+++ b/ci/templates/fabcar/azure-pipelines-javascript.yml
@@ -0,0 +1,21 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+steps:
+ - script: ./startFabric.sh javascript
+ workingDirectory: fabcar
+ displayName: Start Fabric
+ - script: |
+ retry -- npm install
+ npm ls
+ workingDirectory: fabcar/javascript
+ displayName: Install FabCar Application Dependencies
+ - script: |
+ set -ex
+ node enrollAdmin
+ node registerUser
+ node invoke
+ node query
+ workingDirectory: fabcar/javascript
+ displayName: Run FabCar Application
diff --git a/ci/templates/fabcar/azure-pipelines-typescript.yml b/ci/templates/fabcar/azure-pipelines-typescript.yml
new file mode 100644
index 0000000..4e566f5
--- /dev/null
+++ b/ci/templates/fabcar/azure-pipelines-typescript.yml
@@ -0,0 +1,22 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+steps:
+ - script: ./startFabric.sh typescript
+ workingDirectory: fabcar
+ displayName: Start Fabric
+ - script: retry -- npm install
+ workingDirectory: fabcar/typescript
+ displayName: Install FabCar Application Dependencies
+ - script: npm run build
+ workingDirectory: fabcar/typescript
+ displayName: Build FabCar application
+ - script: |
+ set -ex
+ node dist/enrollAdmin
+ node dist/registerUser
+ node dist/invoke
+ node dist/query
+ workingDirectory: fabcar/typescript
+ displayName: Run FabCar Application
diff --git a/ci/templates/install-deps.yml b/ci/templates/install-deps.yml
new file mode 100644
index 0000000..9572937
--- /dev/null
+++ b/ci/templates/install-deps.yml
@@ -0,0 +1,17 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+steps:
+ - task: NodeTool@0
+ inputs:
+ versionSpec: $(NODE_VER)
+ displayName: Install Node.js
+ - script: curl -L --retry 5 --retry-delay 3 https://hyperledger.jfrog.io/hyperledger/fabric-binaries/hyperledger-fabric-linux-amd64-${FABRIC_VERSION}-stable.tar.gz | tar xz
+ displayName: Download Fabric CLI
+ - script: curl -L --retry 5 --retry-delay 3 https://hyperledger.jfrog.io/hyperledger/fabric-binaries/hyperledger-fabric-ca-linux-amd64-${FABRIC_VERSION}-stable.tar.gz | tar xz
+ displayName: Download Fabric CA CLI
+ - script: curl https://raw.githubusercontent.com/kadwanev/retry/master/retry -o ./bin/retry && chmod +x ./bin/retry
+ displayName: Install retry CLI
+ - script: ./ci/scripts/pullFabricImages.sh
+ displayName: Pull Fabric Docker Imagess
\ No newline at end of file
diff --git a/commercial-paper/.gitignore b/commercial-paper/.gitignore
new file mode 100644
index 0000000..65971ce
--- /dev/null
+++ b/commercial-paper/.gitignore
@@ -0,0 +1,4 @@
+cp.tar.gz
+**/.gradle
+**/gateway/connection-org1.yaml
+**/gateway/connection-org2.yaml
diff --git a/commercial-paper/README.md b/commercial-paper/README.md
new file mode 100644
index 0000000..779dc6d
--- /dev/null
+++ b/commercial-paper/README.md
@@ -0,0 +1,646 @@
+
+
+# Commercial Paper Tutorial & Samples
+
+## Introduction
+
+This folder contains a structured set of smart contracts and application clients (ie. in a choice of languages, eg Node.js, Java, Go etc) relating to *Commercial Paper*, a finance 'instrument' (in Global Finance). At present, the Node.js sample contract in particular has further added functionality: an optional two-step authority check (see diagram below), when redeeming a commercial paper instance - and a range of sample ledger queries, to help consolidate your learning; both can be explored using the Node.js application client.
+
+While a more detailed 'explainer' of the Commercial Paper scenario (including use case analysis, code walkthrough & practices, logical/physical representation of ledger data etc) can be found in the [Hyperledger Fabric Commercial Paper Tutorial](https://hyperledger-fabric.readthedocs.io/en/latest/tutorial/commercial_paper.html), you don't have to read through this, just to try out this sample. There's also a [Wikipedia page](https://en.wikipedia.org/wiki/Commercial_paper)
+
+Key Objectives
+
+ * see a Commercial Paper use case in action
+
+ * explore the key 'takeaways': understand differences between asset _state_ changes ('e.g. 'lifecycle') and transaction _inputs_* (e.g. 'inputs' during lifecycle)
+
+ * try out a number of different query types: asset history, asset state, ownership, partial key, named query (criteria-based), ad-hoc queries (you supply a query string) - presently available in the Node.js sample only.
+
+ \* the smart contract uses these (along with business logic) to decide outcomes; some inputs change the asset _state_ (like 'ownership') ; some don't.
+
+
+
+
+Blockchain: benefits to Commercial Paper marketplaces?
+
+ * replace long-winded, time consuming processing between multiple organisations - the network makes it one centralized hub and helps simplify workflow.
+
+ * full transparency, traceability and ownership of issued papers
+
+ * speed up a process that can take days - e. make same-day issuance a reality, or even a market paradigm.
+
+ * in asset-backed commercial paper markets, blockchain can help increase accessibility (to a marketplace) to SMEs to partake in issuance, where otherwise it was inaccessible.
+
+ * integration to other areas, like supply chain finance
+
+
+
+
+
+ Expand the twisty below to see an overview diagram of a 'sample' Commercial paper marketplace - transactions, queries being executed by different organisations (we'll focus on two of these organisations)
+
+PaperNet Overview diagram - The sample commercial paper marketplace
+
+
+
+
+
+
+But first, it might useful to explain Commercial Paper, an unsecured promissory note issued to obtain capital, and operates in global financial markets. A Commercial Paper instance is represented as an asset with a lifecycle, recorded on the blockchain - transactions change its _state_ (over time) and those transactions, naturally - have _inputs_.
+
+
+#### Explainers
+
+Commercial Paper - what is it briefly?
+
+
+It is a type of unsecured promissory note, issued by established companies (eg big manufacturers, blue chip corporations) to gain short-term capital - usually no more than 6-9 months. Why? To meet short-term financial obligations. Commercial paper is generally purchased by money market funds and banks - in fact, it becomes a more important investment strategy during financial recessions :-) . A corporation issues a paper (in the form of a promissory note) for specific projects, such as big capital investments, to pay contractors or even to exercise debt restructuring. The tutorial describes MagnetoCorp (car manufacturer) who have landed a huge contract, and will need approx. $5m in capital (payroll obligations), to hire 1000 car workers for at least 6 months (with no car revenues yet - its a financial strain). Underpinning this, of course, is that MagnetoCorp, has every confidence that (say, in 6 months time) it will be in a position to pay out the face value ($5m in this case) when the commercial paper is redeemd by an owner of the paper, upon maturity :-).
+
+
+
+Ins and Outs, Attractions of Commercial Paper Investment?
+
+
+Investors (who buy Commercial Paper) are attracted as they agree to buy them at a discount (say $4.94m) on the face value (eg $5m) and moreso, they obtain a higher yield than if they were simply gaining interest in a bank (eg. 2% interest on $4.95m today = $5m in 6 months time). But there is a 'premium' attached, with carrying the risk of a debt/loan that is essentially unsecured (unlike a bank) - which is where credit risk and ratings comes in. As a result, the actual yield from the investment chosen is in effect $10k greater (than pure interest, in the example given).
+
+Once an issuing corporation becomes established in the commercial paper marketplace, it builds a high credit rating (risk affects how much of a premium investors seek and therefore discount accordingly) - in fact, it is often cheaper (for a blue chip company) to draw on a commercial paper than on a bank line of credit. But that rating reflects the issuer's ability to pay back on maturity.
+
+I mentioned marketplace: even during the typical 6-9 month period, a commercial paper can be bought and sold multiple times (its quoted, at the discounted price on money markets), before the Commercial Paper reaches its maturity date. On that date, the current investor (or owner) 'redeems' the paper bond with MagnetoCorp, the issuer and gets the face value of $5m.
+
+
+
+[_back to top_](#top)
+
+## Scenario Overview
+
+
+
+In this tutorial two organizations, MagnetoCorp and DigiBank, trade commercial paper with each other on 'PaperNet', the marketplace represented by a Hyperledger Fabric blockchain network. Note that there are two alternative transaction flows - one which mirrors the [Commercial Paper Tutorial](https://hyperledger-fabric.readthedocs.io/en/latest/tutorial/commercial_paper.html) as described in Fabric documentation, and one which requires the authorised owner of the paper to complete a transfer following a request to buy the commercial paper - the latter example features an authorization check in the smart contract that ensures the transactor is from the organization that currently owns the commercial paper - they approve and complete the buy request. These are the commercial paper transaction lifecycles you can try out:
+
+
+
+The tutorial exercises the commercial paper asset lifecycle: _issue_, _buy_ ( 1 to _n_ ) (or _buy_request_ / _transfer_ alternative), and _redeem_ transactions: the key 'takeaways' from the scenario are:
+
+- understanding the _changes in state_ in the commercial paper asset (reflected in the ledger world state) which reaches maturity after 6 months.
+- understanding the _transaction inputs_ for each transaction (some inputs change the asset _state_ - eg. ownership) and some _don't_ (e.g. purchase price) and not part of the asset - but importantly, the _inputs_ for a given transaction are recorded on the blockchain).
+- understanding the importance of _queries_ such as: asset history, rich queries (criteria matching etc), transaction history (where the inputs are recorded)
+
+Client applications (CLI based) are used:
+
+- to perform the transactions
+- run queries (Node.js sample only)
+- examine the transaction inputs (as opposed to _states_) that are written to the ledger after you perform a transaction (using the Node.js listener).
+
+This sample uses the `test-network` . You’ll act as Isabella, an employee of MagnetoCorp (Org2), who will issue a commercial paper on its behalf. You’ll then 'switch hats' to take the role of Balaji, an employee of DigiBank (Org1), who will buy this commercial paper, hold it for a period of time, and then redeem it with MagnetoCorp for a small profit or yield. Note that the smart contract sample doesn't enforce the actual hold period ; the user can, in fact, redeem the paper immediately.
+
+
+## Quick Start
+
+Below are the quick start instructions for running the tutorial. As mentioned, if you're interested in a 'deeper dive' analysis and importance of the concepts, design, structure and implementation of the smart contract, they can be found in the [Hyperledger Fabric Commercial Paper Tutorial](https://hyperledger-fabric.readthedocs.io/en/latest/tutorial/commercial_paper.html). Suffice to say, you DON'T have to have read this, to do this tutorial.
+
+This `README.md` file is in the `commercial-paper` directory, the source code for client applications and the contracts is in the `organization` directory.
+
+### High-Level Overview of Steps
+
+1) Install Binaries, Start the Hyperledger Fabric infrastructure
+
+ The Fabric 'test-network' will be used - this has two organizations 'Org1' and 'Org2' DigiBank will be Org1, and MagnetoCorp will be Org2.
+
+2) Install and Instantiate the Contracts
+
+3) Run client applications in the roles of MagnetoCorp and DigiBank to trade the commercial paper
+
+ - Issue the Paper as Magnetocorp (org2)
+ - Buy the paper as DigiBank (org1)
+ - Redeem the paper as DigiBank (org1)
+
+ See also the transaction flow and alternatives in the Scenario Overview below.
+
+[_back to top_](#top)
+
+## Setup
+
+You will need a machine with the following
+
+- Docker and docker-compose installed
+- Node.js v12 if you want to run JavaScript client applications
+- Java v8 if you want to run Java client applications
+- Maven to build the Java applications
+
+You will need to install the `peer` cli binaries and cloned the `fabric-samples` repository. For more information see
+[Install the Samples, Binaries and Docker Images](https://hyperledger-fabric.readthedocs.io/en/latest/install.html) in the Hyperledger Fabric documentation. Once you have installed the cli binaries, ensure you have added the `bin` directory (for your `peer` commands used by scripts below) to your exported `PATH` variable in your `.bashrc` or `.profile` directory (per below). This is important as you will be opening a number of windows which will need PATH set. Finally, check that it finds the `peer` command in your PATH using the `which` command eg.
+
+```
+export PATH=:$PATH
+which peer
+```
+
+
+It is advised to have 3 terminal windows (consoles) open;
+
+* one to monitor the infrastructure
+* one for MagnetoCorp
+* one for DigiBank.
+
+Once you've cloned the `fabric-samples` - change to the `commercial-paper` directory in each window.
+
+```
+git clone https://github.com/hyperledger/fabric-samples.git
+```
+
+```
+cd fabric-samples/commercial-paper
+```
+
+## Running the Infrastructure
+
+In one console window, run the network starter script - this will start the two organization `test-network` blockchain network.
+
+```
+./network-starter.sh
+```
+
+You can re-use this console window if you wish, but it is recommended to run a docker container monitoring script in its own window. This will let you view what Fabric is doing and help diagnose any failures.
+
+```bash
+./organization/magnetocorp/configuration/cli/monitordocker.sh net_test
+```
+
+### Setup the Organizations' environments
+
+The contract code is available as either JavaScript, Java or Go. You can use either one, and the choice of contract language does not affect the choice of client language. With the v2.0 Fabric chaincode lifecycle, this requires operations for both MagnetoCorp and Digibank admin. Open two windows in the fabric-samples/commercial paper directory, one for each organization.
+
+In your 'MagnetoCorp' window run the following commands, to set the shell environment variables needed to act as that organization. The leading '.' in the command sequence sets in your current environment - if you do not run this, you will not be able to package the chaincode.
+
+```
+cd fabric-samples/commercial-paper/organization/magnetocorp
+. ./magnetocorp.sh
+```
+
+You can either copy and paste them directly into the terminal, or invoke directly in your own command shell. For example if you are using bash or zsh on Linux you can use this command.
+
+```
+source <(./magnetocorp.sh)
+```
+
+Similarly in your 'DigiBank' window run the following commands as shown:
+
+```
+cd fabric-samples/commercial-paper/organization/digibank
+. ./digibank.sh
+```
+
+[_back to top_](#top)
+
+
+### Deploy the smart contract to the channel
+
+You need to perform similar operations for _both_ organizations and for your language choice from the instructions below. For the different contract languages, the steps are very similar - the full set of steps are actually shown in the JavaScript section (see twisty). However, you will perform one or two different initial steps for Java or Go before completing the remaining common steps as instructed in those language sections.
+
+Note that the commands below make use of the `jq` utility for parsing output - download and install it from [the jq download page](https://stedolan.github.io/jq/download/).
+
+
+**For a JavaScript Contract **
+
+
+Running in MagnetoCorp directory:
+
+```
+# MAGNETOCORP
+
+peer lifecycle chaincode package cp.tar.gz --lang node --path ./contract --label cp_0
+peer lifecycle chaincode install cp.tar.gz
+
+export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+echo $PACKAGE_ID # FYI may look like this: 'cp_0:nnnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+```
+
+```
+
+peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+```
+
+Running in Digibank directory:
+
+```
+
+# DIGIBANK
+
+peer lifecycle chaincode package cp.tar.gz --lang node --path ./contract --label cp_0
+peer lifecycle chaincode install cp.tar.gz
+
+export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+echo $PACKAGE_ID
+
+peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+
+```
+
+Once both organizations have installed, and approved the chaincode, it can be committed.
+
+```
+# MAGNETOCORP
+
+peer lifecycle chaincode commit -o localhost:7050 \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel --name papercontract -v 0 \
+ --sequence 1 \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+```
+
+To test, try sending some simple transactions.
+
+```
+
+peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --channelID mychannel --name papercontract \
+ -c '{"Args":["org.papernet.commercialpaper:instantiate"]}' ${PEER_ADDRESS_ORG1} ${PEER_ADDRESS_ORG2} \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -c '{"Args":["org.hyperledger.fabric:GetMetadata"]}' \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --tls --cafile $ORDERER_CA | jq '.' -C | more
+```
+
+
+
+
+**For a Java Contract: **
+
+
+Before the `peer lifecycle chaincode package` command below, you will first need to change into each organization's `contract-java` directory and issue
+
+```
+./gradlew build
+```
+
+Then complete the steps below.
+
+
+Running in MagnetoCorp contract directory:
+
+```
+# MAGNETOCORP
+
+peer lifecycle chaincode package cp.tar.gz --lang java --path ./contract-java --label cp_0
+peer lifecycle chaincode install cp.tar.gz
+
+export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+echo $PACKAGE_ID # FYI may look like this: 'cp_0:nnnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+```
+
+```
+
+peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+```
+
+Running in Digibank
+
+```
+
+# DIGIBANK
+
+peer lifecycle chaincode package cp.tar.gz --lang java --path ./contract-java --label cp_0
+peer lifecycle chaincode install cp.tar.gz
+
+export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+echo $PACKAGE_ID
+
+peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+
+```
+
+Once both organizations have installed, and approved the chaincode, it can be committed.
+
+```
+# MAGNETOCORP
+
+peer lifecycle chaincode commit -o localhost:7050 \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel --name papercontract -v 0 \
+ --sequence 1 \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+```
+
+To test, try sending some simple transactions.
+
+```
+
+peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --channelID mychannel --name papercontract \
+ -c '{"Args":["org.papernet.commercialpaper:instantiate"]}' ${PEER_ADDRESS_ORG1} ${PEER_ADDRESS_ORG2} \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -c '{"Args":["org.hyperledger.fabric:GetMetadata"]}' \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --tls --cafile $ORDERER_CA | jq '.' -C | more
+```
+
+
+
+
+**For a Go Contract **
+
+
+Before the `peer lifecycle chaincode package` command step, you will need to change into each organization's `contract-go` directory and issue
+
+```
+go mod vendor
+```
+
+Then complete the steps below.
+
+
+Running in MagnetoCorp contract directory:
+
+```
+# MAGNETOCORP
+
+peer lifecycle chaincode package cp.tar.gz --lang golang --path ./contract-go --label cp_0
+peer lifecycle chaincode install cp.tar.gz
+
+export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+echo $PACKAGE_ID # FYI may look like this: 'cp_0:nnnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+```
+
+```
+
+peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+```
+
+Running in Digibank
+
+```
+
+# DIGIBANK
+
+peer lifecycle chaincode package cp.tar.gz --lang golang --path ./contract-go --label cp_0
+peer lifecycle chaincode install cp.tar.gz
+
+export PACKAGE_ID=$(peer lifecycle chaincode queryinstalled --output json | jq -r '.installed_chaincodes[0].package_id')
+echo $PACKAGE_ID
+
+peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -v 0 \
+ --package-id $PACKAGE_ID \
+ --sequence 1 \
+ --tls \
+ --cafile $ORDERER_CA
+
+peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name papercontract -v 0 --sequence 1
+
+```
+
+Once both organizations have installed, and approved the chaincode, it can be committed.
+
+```
+# MAGNETOCORP
+
+peer lifecycle chaincode commit -o localhost:7050 \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel --name papercontract -v 0 \
+ --sequence 1 \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+```
+
+To test, try sending some simple transactions.
+
+```
+
+peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --channelID mychannel --name papercontract \
+ -c '{"Args":["org.papernet.commercialpaper:instantiate"]}' ${PEER_ADDRESS_ORG1} ${PEER_ADDRESS_ORG2} \
+ --tls --cafile $ORDERER_CA --waitForEvent
+
+peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com \
+ --channelID mychannel \
+ --name papercontract \
+ -c '{"Args":["org.hyperledger.fabric:GetMetadata"]}' \
+ --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} \
+ --tls --cafile $ORDERER_CA | jq '.' -C | more
+```
+
+
+
+
+
+
+
+[_back to top_](#top)
+
+
+## Client Applications
+
+Note for Java applications, you will need to compile the Java Code using `maven`. Use this command in each application-java directory
+
+```
+mvn clean package
+```
+
+Note for JavaScript applications, you will need to install the dependencies first. Use this command in each application directory
+
+```
+npm install
+```
+> Note that there is NO dependency between the language of any one client application and any contract. Mix and match as you wish!
+
+The docker containers don't contain the node or Java runtimes; so it is best to exit the docker containers - but keep the windows open and run the applications locally.
+
+As mentioned earlier in the Sample introduction section, transaction _inputs_ are recorded on the ledger, as well as any asset _state_ changes. Just *before* you run the _issue_ application script below - you need to launch a block 'listener' application that will show you these _inputs_, as you complete each transaction in the commercial paper lifecycle (eg. Paper Number: 00001, 00002 etc) .
+
+For the listener, its best to open a *new* terminal for this in the `commercial-paper/organization/magnetocorp/application` directory (javascript). Next, run the `addToWallet` step in the `issue` transaction below, to add Isabella's identity to the wallet - the listener will use this wallet. Once the listener is launched, it will show the inputs for transactions you will perform and which are committed to blocks (ie part of the ledger). Note: initially, the listener may show a spurious message, and then go into a _listening_ or 'wait' state. As transactions complete below, messages will be displayed by the listener - so keep an eye out. *After* adding Isabella's wallet, you can then launch the listener as follows:
+
+```
+node cpListener.js
+```
+
+**Issue the commercial paper **
+
+The paper is issued by *MagnetoCorp*
+
+You can now run the applications to issue the commercial paper. Change to either the
+`commercial-paper/organization/magnetocorp/application` directory (javascript) or `commercial-paper/organization/magnetocorp/application-java` directory (java)
+
+*Add the Identity to be used to the wallet*
+
+```
+node addToWallet.js
+# or
+java -cp target/commercial-paper-0.0.1-SNAPSHOT.jar org.magnetocorp.AddToWallet
+```
+
+*Issue the Commercial Paper*
+
+```
+node issue.js
+# or
+java -cp target/commercial-paper-0.0.1-SNAPSHOT.jar org.magnetocorp.Issue
+```
+
+Don't forget to check the application listener for messages above!
+
+
+
+
+
+**Buy the commercial paper **
+
+_Buy_ is performed as *Digibank*;
+
+You can now run the applications to buy the paper. Change to either the
+`commercial-paper/organization/digibank/application` directory or `commercial-paper/organization/digibank/application-java`
+
+*Add the Identity to be used*
+
+```
+node addToWallet.js
+# or
+java -cp target/commercial-paper-0.0.1-SNAPSHOT.jar org.digibank.AddToWallet
+```
+
+*Buy the paper*
+
+```
+node buy.js
+# or
+java -cp target/commercial-paper-0.0.1-SNAPSHOT.jar org.digibank.Buy
+```
+
+If you have just executed a `buy` transaction above - jump to the `redeem` transaction below - otherwise execute the _buy_/_transfer_ sequence as described earlier.
+
+*Alternative: Request to Buy the paper (buy/transfer)*
+
+```
+node buy_request.js
+```
+
+Now complete the _request_ by switching to the `MagnetoCorp` application directory (javascript) and execute a `transfer` transaction as MagnetoCorp:
+
+```
+cd ../../magnetocorp/application
+
+node transfer.js
+```
+
+
+
+
+**Redeem the commercial paper **
+
+_Redeem_ is performed as *Digibank* - the current owner (buyer) in the lifecycle.
+
+You can now run the applications to redeem the paper. Change to either the
+`commercial-paper/organization/digibank/application` directory or `commercial-paper/organization/digibank/application-java`
+
+
+*Redeem*
+
+```
+node redeem.js
+# or
+java -cp target/commercial-paper-0.0.1-SNAPSHOT.jar org.digibank.Redeem
+```
+
+
+
+
+
+**Perform Queries: Ownership, Asset History etc (Node.js sample only) **
+
+ Having completed the full commercial paper lifecycle for one paper (paper number: 00001) some queries below won't show a lot of data - as an optional exercise, you can change the scripts above (paper number: 00002) to create another paper lifecycle and run the `queryapp` application below (change query 1 to the new CP number FYI), with more data available. As indicated, the query transactions mentioned are presently only available in the Node.js sample.
+
+ Execute the Node.js application client script, which will run the following 5 queries, in order:
+
+ - History of Commercial Paper (Note: the paper state is shown more descriptively eg. 'ISSUED', 'TRADING' and based on currentState values on ledger)
+ - Ownership of Commercial Papers
+ - Partial Key query, for Commercial papers in org.papernet.papers namespace belonging to MagnetoCorp
+ - Named Query: all redeemed papers in a state of 'redeemed' (currentState = 4)
+ - Named Query: all commercial papers with a face value > $4m
+
+ From the `digibank/application` subdirectory run:
+
+ ```
+ node queryapp.js
+ ```
+
+
+
+
+When you're done with this section, return to the terminal where your Node.js _listener_ application is running, and terminate the process.
+
+## Clean up
+When you are finished using the Fabric test network and the commercial paper smart contract and applications, you can use the following command to clean up the network:
+
+```
+./network-clean.sh
+```
+
+[_back to top_](#top)
diff --git a/commercial-paper/img/overview.png b/commercial-paper/img/overview.png
new file mode 100644
index 0000000..9da7ace
Binary files /dev/null and b/commercial-paper/img/overview.png differ
diff --git a/commercial-paper/img/transaction-flow.png b/commercial-paper/img/transaction-flow.png
new file mode 100644
index 0000000..855a8ce
Binary files /dev/null and b/commercial-paper/img/transaction-flow.png differ
diff --git a/commercial-paper/network-clean.sh b/commercial-paper/network-clean.sh
new file mode 100755
index 0000000..5fe6c08
--- /dev/null
+++ b/commercial-paper/network-clean.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: Apache-2.0
+
+function _exit(){
+ printf "Exiting:%s\n" "$1"
+ exit -1
+}
+
+# Exit on first error, print all commands.
+set -ev
+set -o pipefail
+
+# Where am I?
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+export FABRIC_CFG_PATH="${DIR}/../config"
+
+cd "${DIR}/../test-network/"
+
+docker kill cliDigiBank cliMagnetoCorp logspout || true
+./network.sh down
+
+# remove any stopped containers
+docker rm $(docker ps -aq)
+
diff --git a/commercial-paper/network-starter.sh b/commercial-paper/network-starter.sh
new file mode 100755
index 0000000..c2fe682
--- /dev/null
+++ b/commercial-paper/network-starter.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: Apache-2.0
+
+function _exit(){
+ printf "Exiting:%s\n" "$1"
+ exit -1
+}
+
+# Exit on first error, print all commands.
+set -ev
+set -o pipefail
+
+# Where am I?
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+export FABRIC_CFG_PATH="${DIR}/../config"
+
+cd "${DIR}/../test-network/"
+
+docker kill cliDigiBank cliMagnetoCorp logspout || true
+./network.sh down
+./network.sh up createChannel -ca -s couchdb
+
+# Copy the connection profiles so they are in the correct organizations.
+cp "${DIR}/../test-network/organizations/peerOrganizations/org1.example.com/connection-org1.yaml" "${DIR}/organization/digibank/gateway/"
+cp "${DIR}/../test-network/organizations/peerOrganizations/org2.example.com/connection-org2.yaml" "${DIR}/organization/magnetocorp/gateway/"
+
+cp ${DIR}/../test-network/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/* ${DIR}/../test-network/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem
+cp ${DIR}/../test-network/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/* ${DIR}/../test-network/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/priv_sk
+
+cp ${DIR}/../test-network/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/signcerts/* ${DIR}/../test-network/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/signcerts/User1@org2.example.com-cert.pem
+cp ${DIR}/../test-network/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/keystore/* ${DIR}/../test-network/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/keystore/priv_sk
+
+echo Suggest that you monitor the docker containers by running
+echo "./organization/magnetocorp/configuration/cli/monitordocker.sh net_test"
diff --git a/commercial-paper/organization/digibank/.gitignore b/commercial-paper/organization/digibank/.gitignore
new file mode 100644
index 0000000..7d13901
--- /dev/null
+++ b/commercial-paper/organization/digibank/.gitignore
@@ -0,0 +1 @@
+identity
\ No newline at end of file
diff --git a/commercial-paper/organization/digibank/application-java/.gitignore b/commercial-paper/organization/digibank/application-java/.gitignore
new file mode 100644
index 0000000..2f7896d
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/commercial-paper/organization/digibank/application-java/.settings/org.eclipse.jdt.core.prefs b/commercial-paper/organization/digibank/application-java/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..b8947ec
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/commercial-paper/organization/digibank/application-java/.settings/org.eclipse.m2e.core.prefs b/commercial-paper/organization/digibank/application-java/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/commercial-paper/organization/digibank/application-java/pom.xml b/commercial-paper/organization/digibank/application-java/pom.xml
new file mode 100644
index 0000000..f80dc68
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/pom.xml
@@ -0,0 +1,96 @@
+
+ 4.0.0
+ commercial-paper
+ commercial-paper
+ 0.0.1-SNAPSHOT
+
+
+
+
+ 1.8
+ UTF-8
+ UTF-8
+
+
+ [2.0,3.0)
+
+ 2.1.0
+
+
+
+
+ src
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.0
+
+
+
+ package
+
+ shade
+
+
+
+
+ false
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+
+
+
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
+
+
+ org.hyperledger.fabric
+ fabric-gateway-java
+ ${fabric-gateway-java.version}
+
+
+
+
+ org.hyperledger.fabric-chaincode-java
+ fabric-chaincode-shim
+ ${fabric-chaincode-java.version}
+ compile
+
+
+
+
+ org.json
+ json
+ 20180813
+
+
+
+
diff --git a/commercial-paper/organization/digibank/application-java/src/org/digibank/AddToWallet.java b/commercial-paper/organization/digibank/application-java/src/org/digibank/AddToWallet.java
new file mode 100644
index 0000000..6a3fa12
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/src/org/digibank/AddToWallet.java
@@ -0,0 +1,68 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.digibank;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import org.hyperledger.fabric.gateway.Identities;
+import org.hyperledger.fabric.gateway.Identity;
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+
+public class AddToWallet {
+
+ private static X509Certificate readX509Certificate(final Path certificatePath) throws IOException, CertificateException {
+ try (Reader certificateReader = Files.newBufferedReader(certificatePath, StandardCharsets.UTF_8)) {
+ return Identities.readX509Certificate(certificateReader);
+ }
+ }
+
+ private static PrivateKey getPrivateKey(final Path privateKeyPath) throws IOException, InvalidKeyException {
+ try (Reader privateKeyReader = Files.newBufferedReader(privateKeyPath, StandardCharsets.UTF_8)) {
+ return Identities.readPrivateKey(privateKeyReader);
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ // A wallet stores a collection of identities
+ Path walletPath = Paths.get(".", "wallet");
+ Wallet wallet = Wallets.newFileSystemWallet(walletPath);
+
+ Path credentialPath = Paths.get("..", "..", "..",".." ,"test-network", "organizations",
+ "peerOrganizations", "org1.example.com", "users", "User1@org1.example.com", "msp");
+ System.out.println("credentialPath: " + credentialPath.toString());
+ Path certificatePath = credentialPath.resolve(Paths.get("signcerts",
+ "User1@org1.example.com-cert.pem"));
+ System.out.println("certificatePem: " + certificatePath.toString());
+ Path privateKeyPath = credentialPath.resolve(Paths.get("keystore",
+ "priv_sk"));
+
+ X509Certificate certificate = readX509Certificate(certificatePath);
+ PrivateKey privateKey = getPrivateKey(privateKeyPath);
+
+ Identity identity = Identities.newX509Identity("Org1MSP", certificate, privateKey);
+
+
+ String identityLabel = "User1@org1.example.com";
+ wallet.put(identityLabel, identity);
+
+ System.out.println("Write wallet info into " + walletPath.toString() + " successfully.");
+
+ } catch (IOException | CertificateException | InvalidKeyException e) {
+ System.err.println("Error adding to wallet");
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/commercial-paper/organization/digibank/application-java/src/org/digibank/Buy.java b/commercial-paper/organization/digibank/application-java/src/org/digibank/Buy.java
new file mode 100644
index 0000000..5e09bbd
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/src/org/digibank/Buy.java
@@ -0,0 +1,74 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.digibank;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import org.hyperledger.fabric.gateway.Contract;
+import org.hyperledger.fabric.gateway.Gateway;
+import org.hyperledger.fabric.gateway.GatewayException;
+import org.hyperledger.fabric.gateway.Network;
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.papernet.CommercialPaper;
+
+public class Buy {
+
+ private static final String ENVKEY="CONTRACT_NAME";
+
+ public static void main(String[] args) {
+ Gateway.Builder builder = Gateway.createBuilder();
+
+ String contractName="papercontract";
+ // get the name of the contract, in case it is overridden
+ Map envvar = System.getenv();
+ if (envvar.containsKey(ENVKEY)){
+ contractName=envvar.get(ENVKEY);
+ }
+
+ try {
+ // A wallet stores a collection of identities
+ Path walletPath = Paths.get(".", "wallet");
+ Wallet wallet = Wallets.newFileSystemWallet(walletPath);
+ System.out.println("Read wallet info from: " + walletPath);
+
+ String userName = "User1@org1.example.com";
+
+ Path connectionProfile = Paths.get("..", "gateway", "connection-org1.yaml");
+
+ // Set connection options on the gateway builder
+ builder.identity(wallet, userName).networkConfig(connectionProfile).discovery(false);
+
+ // Connect to gateway using application specified parameters
+ try(Gateway gateway = builder.connect()) {
+
+ // Access PaperNet network
+ System.out.println("Use network channel: mychannel.");
+ Network network = gateway.getNetwork("mychannel");
+
+ // Get addressability to commercial paper contract
+ System.out.println("Use org.papernet.commercialpaper smart contract.");
+ Contract contract = network.getContract(contractName, "org.papernet.commercialpaper");
+
+ // Buy commercial paper
+ System.out.println("Submit commercial paper buy transaction.");
+ byte[] response = contract.submitTransaction("buy", "MagnetoCorp", "00001", "MagnetoCorp", "DigiBank", "4900000", "2020-05-31");
+
+ // Process response
+ System.out.println("Process buy transaction response.");
+ CommercialPaper paper = CommercialPaper.deserialize(response);
+ System.out.println(paper);
+ }
+ } catch (GatewayException | IOException | TimeoutException | InterruptedException e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+
+}
diff --git a/commercial-paper/organization/digibank/application-java/src/org/digibank/Redeem.java b/commercial-paper/organization/digibank/application-java/src/org/digibank/Redeem.java
new file mode 100644
index 0000000..62eb9d9
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/src/org/digibank/Redeem.java
@@ -0,0 +1,73 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.digibank;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import org.hyperledger.fabric.gateway.Contract;
+import org.hyperledger.fabric.gateway.Gateway;
+import org.hyperledger.fabric.gateway.GatewayException;
+import org.hyperledger.fabric.gateway.Network;
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.papernet.CommercialPaper;
+
+public class Redeem {
+
+ private static final String ENVKEY="CONTRACT_NAME";
+
+ public static void main(String[] args) {
+ Gateway.Builder builder = Gateway.createBuilder();
+
+ String contractName="papercontract";
+ // get the name of the contract, in case it is overridden
+ Map envvar = System.getenv();
+ if (envvar.containsKey(ENVKEY)){
+ contractName=envvar.get(ENVKEY);
+ }
+
+ try {
+ // A wallet stores a collection of identities
+ Path walletPath = Paths.get(".", "wallet");
+ Wallet wallet = Wallets.newFileSystemWallet(walletPath);
+
+ String userName = "User1@org1.example.com";
+
+ Path connectionProfile = Paths.get("..", "gateway", "connection-org1.yaml");
+
+ // Set connection options on the gateway builder
+ builder.identity(wallet, userName).networkConfig(connectionProfile).discovery(false);
+
+ // Connect to gateway using application specified parameters
+ try(Gateway gateway = builder.connect()) {
+
+ // Access PaperNet network
+ System.out.println("Use network channel: mychannel.");
+ Network network = gateway.getNetwork("mychannel");
+
+ // Get addressability to commercial paper contract
+ System.out.println("Use org.papernet.commercialpaper smart contract.");
+ Contract contract = network.getContract("papercontract", "org.papernet.commercialpaper");
+
+ // Redeem commercial paper
+ System.out.println("Submit commercial paper redeem transaction.");
+ byte[] response = contract.submitTransaction("redeem", "MagnetoCorp", "00001", "DigiBank", "2020-11-30");
+
+ // Process response
+ System.out.println("Process redeem transaction response.");
+ CommercialPaper paper = CommercialPaper.deserialize(response);
+ System.out.println(paper);
+ }
+ } catch (GatewayException | IOException | TimeoutException | InterruptedException e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+
+}
diff --git a/commercial-paper/organization/digibank/application-java/src/org/papernet/CommercialPaper.java b/commercial-paper/organization/digibank/application-java/src/org/papernet/CommercialPaper.java
new file mode 100644
index 0000000..dbb4e3f
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/src/org/papernet/CommercialPaper.java
@@ -0,0 +1,181 @@
+/*
+ * SPDX-License-Identifier:
+ */
+
+package org.papernet;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.papernet.ledgerapi.State;
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+import org.json.JSONObject;
+import org.json.JSONPropertyIgnore;
+
+@DataType()
+public class CommercialPaper extends State {
+ // Enumerate commercial paper state values
+ public final static String ISSUED = "ISSUED";
+ public final static String TRADING = "TRADING";
+ public final static String REDEEMED = "REDEEMED";
+
+ @Property()
+ private String state="";
+
+ public String getState() {
+ return state;
+ }
+
+ public CommercialPaper setState(String state) {
+ this.state = state;
+ return this;
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isIssued() {
+ return this.state.equals(CommercialPaper.ISSUED);
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isTrading() {
+ return this.state.equals(CommercialPaper.TRADING);
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isRedeemed() {
+ return this.state.equals(CommercialPaper.REDEEMED);
+ }
+
+ public CommercialPaper setIssued() {
+ this.state = CommercialPaper.ISSUED;
+ return this;
+ }
+
+ public CommercialPaper setTrading() {
+ this.state = CommercialPaper.TRADING;
+ return this;
+ }
+
+ public CommercialPaper setRedeemed() {
+ this.state = CommercialPaper.REDEEMED;
+ return this;
+ }
+
+ @Property()
+ private String paperNumber;
+
+ @Property()
+ private String issuer;
+
+ @Property()
+ private String issueDateTime;
+
+ @Property()
+ private int faceValue;
+
+ @Property()
+ private String maturityDateTime;
+
+ @Property()
+ private String owner;
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public CommercialPaper setOwner(String owner) {
+ this.owner = owner;
+ return this;
+ }
+
+ public CommercialPaper() {
+ super();
+ }
+
+ public CommercialPaper setKey() {
+ this.key = State.makeKey(new String[] { this.paperNumber });
+ return this;
+ }
+
+ public String getPaperNumber() {
+ return paperNumber;
+ }
+
+ public CommercialPaper setPaperNumber(String paperNumber) {
+ this.paperNumber = paperNumber;
+ return this;
+ }
+
+ public String getIssuer() {
+ return issuer;
+ }
+
+ public CommercialPaper setIssuer(String issuer) {
+ this.issuer = issuer;
+ return this;
+ }
+
+ public String getIssueDateTime() {
+ return issueDateTime;
+ }
+
+ public CommercialPaper setIssueDateTime(String issueDateTime) {
+ this.issueDateTime = issueDateTime;
+ return this;
+ }
+
+ public int getFaceValue() {
+ return faceValue;
+ }
+
+ public CommercialPaper setFaceValue(int faceValue) {
+ this.faceValue = faceValue;
+ return this;
+ }
+
+ public String getMaturityDateTime() {
+ return maturityDateTime;
+ }
+
+ public CommercialPaper setMaturityDateTime(String maturityDateTime) {
+ this.maturityDateTime = maturityDateTime;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "Paper::" + this.key + " " + this.getPaperNumber() + " " + getIssuer() + " " + getFaceValue();
+ }
+
+ /**
+ * Deserialize a state data to commercial paper
+ *
+ * @param {Buffer} data to form back into the object
+ */
+ public static CommercialPaper deserialize(byte[] data) {
+ JSONObject json = new JSONObject(new String(data, UTF_8));
+
+ String issuer = json.getString("issuer");
+ String paperNumber = json.getString("paperNumber");
+ String issueDateTime = json.getString("issueDateTime");
+ String maturityDateTime = json.getString("maturityDateTime");
+ String owner = json.getString("owner");
+ int faceValue = json.getInt("faceValue");
+ String state = json.getString("state");
+ return createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue,owner,state);
+ }
+
+ public static byte[] serialize(CommercialPaper paper) {
+ return State.serialize(paper);
+ }
+
+ /**
+ * Factory method to create a commercial paper object
+ */
+ public static CommercialPaper createInstance(String issuer, String paperNumber, String issueDateTime,
+ String maturityDateTime, int faceValue, String owner, String state) {
+ return new CommercialPaper().setIssuer(issuer).setPaperNumber(paperNumber).setMaturityDateTime(maturityDateTime)
+ .setFaceValue(faceValue).setKey().setIssueDateTime(issueDateTime).setOwner(owner).setState(state);
+ }
+
+}
diff --git a/commercial-paper/organization/digibank/application-java/src/org/papernet/ledgerapi/State.java b/commercial-paper/organization/digibank/application-java/src/org/papernet/ledgerapi/State.java
new file mode 100644
index 0000000..a32abc0
--- /dev/null
+++ b/commercial-paper/organization/digibank/application-java/src/org/papernet/ledgerapi/State.java
@@ -0,0 +1,60 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+package org.papernet.ledgerapi;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.json.JSONObject;
+
+/**
+ * State class. States have a class, unique key, and a lifecycle current state
+ * the current state is determined by the specific subclass
+ */
+public class State {
+
+ protected String key;
+
+ /**
+ * @param {String|Object} class An identifiable class of the instance
+ * @param {keyParts[]} elements to pull together to make a key for the objects
+ */
+ public State() {
+
+ }
+
+ String getKey() {
+ return this.key;
+ }
+
+ public String[] getSplitKey() {
+ return State.splitKey(this.key);
+ }
+
+ /**
+ * Convert object to buffer containing JSON data serialization Typically used
+ * before putState()ledger API
+ *
+ * @param {Object} JSON object to serialize
+ * @return {buffer} buffer with the data to store
+ */
+ public static byte[] serialize(Object object) {
+ String jsonStr = new JSONObject(object).toString();
+ return jsonStr.getBytes(UTF_8);
+ }
+
+ /**
+ * Join the keyParts to make a unififed string
+ *
+ * @param (String[]) keyParts
+ */
+ public static String makeKey(String[] keyParts) {
+ return String.join(":", keyParts);
+ }
+
+ public static String[] splitKey(String key) {
+ System.out.println("splitting key " + key + " " + java.util.Arrays.asList(key.split(":")));
+ return key.split(":");
+ }
+
+}
diff --git a/commercial-paper/organization/digibank/application/.eslintrc.js b/commercial-paper/organization/digibank/application/.eslintrc.js
new file mode 100644
index 0000000..22fbefc
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 4],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-tabs': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
\ No newline at end of file
diff --git a/commercial-paper/organization/digibank/application/.gitignore b/commercial-paper/organization/digibank/application/.gitignore
new file mode 100644
index 0000000..b512c09
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/.gitignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/commercial-paper/organization/digibank/application/addToWallet.js b/commercial-paper/organization/digibank/application/addToWallet.js
new file mode 100644
index 0000000..9517e39
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/addToWallet.js
@@ -0,0 +1,55 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+// Bring key classes into scope, most importantly Fabric SDK network class
+const fs = require('fs');
+const { Wallets } = require('fabric-network');
+const path = require('path');
+
+const fixtures = path.resolve(__dirname, '../../../../test-network');
+
+async function main() {
+
+ // Main try/catch block
+ try {
+
+ // A wallet stores a collection of identities
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/balaji/wallet');
+
+ // Identity to credentials to be stored in the wallet
+ const credPath = path.join(fixtures, '/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com');
+ const certificate = fs.readFileSync(path.join(credPath, '/msp/signcerts/User1@org1.example.com-cert.pem')).toString();
+ const privateKey = fs.readFileSync(path.join(credPath, '/msp/keystore/priv_sk')).toString();
+
+ // Load credentials into wallet
+ const identityLabel = 'balaji';
+
+ const identity = {
+ credentials: {
+ certificate,
+ privateKey
+ },
+ mspId: 'Org1MSP',
+ type: 'X.509'
+ }
+
+ await wallet.put(identityLabel, identity);
+
+ } catch (error) {
+ console.log(`Error adding to wallet. ${error}`);
+ console.log(error.stack);
+ }
+}
+
+main().then(() => {
+ console.log('done');
+}).catch((e) => {
+ console.log(e);
+ console.log(e.stack);
+ process.exit(-1);
+});
diff --git a/commercial-paper/organization/digibank/application/buy.js b/commercial-paper/organization/digibank/application/buy.js
new file mode 100644
index 0000000..b052b75
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/buy.js
@@ -0,0 +1,105 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+ * This application has 6 basic steps:
+ * 1. Select an identity from a wallet
+ * 2. Connect to network gateway
+ * 3. Access PaperNet network
+ * 4. Construct request to buy commercial paper
+ * 5. Submit transaction
+ * 6. Process response
+ */
+
+'use strict';
+
+// Bring key classes into scope, most importantly Fabric SDK network class
+const fs = require('fs');
+const yaml = require('js-yaml');
+const { Wallets, Gateway } = require('fabric-network');
+const CommercialPaper = require('../../magnetocorp/contract/lib/paper.js');
+
+
+// Main program function
+async function main () {
+
+ // A wallet stores a collection of identities for use
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/balaji/wallet');
+
+
+ // A gateway defines the peers used to access Fabric networks
+ const gateway = new Gateway();
+
+ // Main try/catch block
+ try {
+
+ // Specify userName for network access
+ const userName = 'balaji';
+
+ // Load connection profile; will be used to locate a gateway
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org1.yaml', 'utf8'));
+
+ // Set connection options; identity and wallet
+ let connectionOptions = {
+ identity: userName,
+ wallet: wallet,
+ discovery: { enabled: true, asLocalhost: true }
+
+ };
+
+ // Connect to gateway using application specified parameters
+ console.log('Connect to Fabric gateway.');
+
+ await gateway.connect(connectionProfile, connectionOptions);
+
+ // Access PaperNet network
+ console.log('Use network channel: mychannel.');
+
+ const network = await gateway.getNetwork('mychannel');
+
+ // Get addressability to commercial paper contract
+ console.log('Use org.papernet.commercialpaper smart contract.');
+
+ const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
+
+ // buy commercial paper
+ console.log('Submit commercial paper buy transaction.');
+
+ const buyResponse = await contract.submitTransaction('buy', 'MagnetoCorp', '00001', 'MagnetoCorp', 'DigiBank', '4900000', '2020-05-31');
+
+ // process response
+ console.log('Process buy transaction response.');
+
+ let paper = CommercialPaper.fromBuffer(buyResponse);
+
+ console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully purchased by ${paper.owner}`);
+ console.log('Transaction complete.');
+
+ } catch (error) {
+
+ console.log(`Error processing transaction. ${error}`);
+ console.log(error.stack);
+
+ } finally {
+
+ // Disconnect from the gateway
+ console.log('Disconnect from Fabric gateway.');
+ gateway.disconnect();
+
+ }
+}
+main().then(() => {
+
+ console.log('Buy program complete.');
+
+}).catch((e) => {
+
+ console.log('Buy program exception.');
+ console.log(e);
+ console.log(e.stack);
+ process.exit(-1);
+
+});
diff --git a/commercial-paper/organization/digibank/application/buy_request.js b/commercial-paper/organization/digibank/application/buy_request.js
new file mode 100644
index 0000000..1493b80
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/buy_request.js
@@ -0,0 +1,105 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+ * This application has 6 basic steps:
+ * 1. Select an identity from a wallet
+ * 2. Connect to network gateway
+ * 3. Access PaperNet network
+ * 4. Construct request to buy (buy_request) commercial paper
+ * 5. Submit transaction
+ * 6. Process response
+ */
+
+'use strict';
+
+// Bring key classes into scope, most importantly Fabric SDK network class
+const fs = require('fs');
+const yaml = require('js-yaml');
+const { Wallets, Gateway } = require('fabric-network');
+const CommercialPaper = require('../../magnetocorp/contract/lib/paper.js');
+
+
+// Main program function
+async function main () {
+
+ // A wallet stores a collection of identities for use
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/balaji/wallet');
+
+
+ // A gateway defines the peers used to access Fabric networks
+ const gateway = new Gateway();
+
+ // Main try/catch block
+ try {
+
+ // Specify userName for network access
+ const userName = 'balaji';
+
+ // Load connection profile; will be used to locate a gateway
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org1.yaml', 'utf8'));
+
+ // Set connection options; identity and wallet
+ let connectionOptions = {
+ identity: userName,
+ wallet: wallet,
+ discovery: { enabled: true, asLocalhost: true }
+
+ };
+
+ // Connect to gateway using application specified parameters
+ console.log('Connect to Fabric gateway.');
+
+ await gateway.connect(connectionProfile, connectionOptions);
+
+ // Access PaperNet network
+ console.log('Use network channel: mychannel.');
+
+ const network = await gateway.getNetwork('mychannel');
+
+ // Get addressability to commercial paper contract
+ console.log('Use org.papernet.commercialpaper smart contract.');
+
+ const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
+
+ // request to buy commercial paper using buy_request / transfer two-part transaction
+ console.log('Submit commercial paper buy_request transaction.');
+
+ const buyResponse = await contract.submitTransaction('buy_request', 'MagnetoCorp', '00001', 'MagnetoCorp', 'DigiBank', '4900000', '2020-05-31');
+
+ // process response
+ console.log('Process buy_request transaction response.');
+
+ let paper = CommercialPaper.fromBuffer(buyResponse);
+
+ console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} has been provisionally purchased : the transfer must now be completed by paper owner`);
+ console.log('Transaction complete.');
+
+ } catch (error) {
+
+ console.log(`Error processing transaction. ${error}`);
+ console.log(error.stack);
+
+ } finally {
+
+ // Disconnect from the gateway
+ console.log('Disconnect from Fabric gateway.');
+ gateway.disconnect();
+
+ }
+}
+main().then(() => {
+
+ console.log('Buy_request program complete.');
+
+}).catch((e) => {
+
+ console.log('Buy_request program exception.');
+ console.log(e);
+ console.log(e.stack);
+ process.exit(-1);
+
+});
diff --git a/commercial-paper/organization/digibank/application/enrollUser.js b/commercial-paper/organization/digibank/application/enrollUser.js
new file mode 100644
index 0000000..2df5d6a
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/enrollUser.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const FabricCAServices = require('fabric-ca-client');
+const { Wallets } = require('fabric-network');
+const fs = require('fs');
+const yaml = require('js-yaml');
+const path = require('path');
+
+async function main() {
+ try {
+ // load the network configuration
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org1.yaml', 'utf8'));
+
+ // Create a new CA client for interacting with the CA.
+ const caInfo = connectionProfile.certificateAuthorities['ca.org1.example.com'];
+ const caTLSCACerts = caInfo.tlsCACerts.pem;
+ const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName);
+
+ // Create a new file system based wallet for managing identities.
+ const walletPath = path.join(process.cwd(), '../identity/user/balaji/wallet');
+ const wallet = await Wallets.newFileSystemWallet(walletPath);
+ console.log(`Wallet path: ${walletPath}`);
+
+ // Check to see if we've already enrolled the admin user.
+ const userExists = await wallet.get('balaji');
+ if (userExists) {
+ console.log('An identity for the client user "balaji" already exists in the wallet');
+ return;
+ }
+
+ // Enroll the admin user, and import the new identity into the wallet.
+ const enrollment = await ca.enroll({ enrollmentID: 'user1', enrollmentSecret: 'user1pw' });
+ const x509Identity = {
+ credentials: {
+ certificate: enrollment.certificate,
+ privateKey: enrollment.key.toBytes(),
+ },
+ mspId: 'Org1MSP',
+ type: 'X.509',
+ };
+ await wallet.put('balaji', x509Identity);
+ console.log('Successfully enrolled client user "balaji" and imported it into the wallet');
+
+ } catch (error) {
+ console.error(`Failed to enroll client user "balaji": ${error}`);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/commercial-paper/organization/digibank/application/package.json b/commercial-paper/organization/digibank/application/package.json
new file mode 100644
index 0000000..176c1ec
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "nodejs",
+ "version": "1.0.0",
+ "description": "",
+ "main": "buy.js",
+ "scripts": {
+ "test": "rm -rf _idwallet && node addToWallet.js && node buy.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-network": "^2.2.4",
+ "fabric-ca-client": "^2.2.4",
+ "js-yaml": "^3.12.0"
+ },
+ "devDependencies": {
+ "eslint": "^5.6.0"
+ }
+}
diff --git a/commercial-paper/organization/digibank/application/queryapp.js b/commercial-paper/organization/digibank/application/queryapp.js
new file mode 100644
index 0000000..fcba8fa
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/queryapp.js
@@ -0,0 +1,155 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+ * This application has 6 basic steps:
+ * 1. Select an identity from a wallet
+ * 2. Connect to network gateway
+ * 3. Access PaperNet network
+ * 4. Construct request to query the ledger
+ * 5. Evaluate transactions (queries)
+ * 6. Process responses
+ */
+
+'use strict';
+
+// Bring key classes into scope, most importantly Fabric SDK network class
+const fs = require('fs');
+const yaml = require('js-yaml');
+const { Wallets, Gateway } = require('fabric-network');
+
+
+// Main program function
+async function main() {
+
+ // A wallet stores a collection of identities for use
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/balaji/wallet');
+
+
+ // A gateway defines the peers used to access Fabric networks
+ const gateway = new Gateway();
+
+ // Main try/catch block
+ try {
+
+ // Specify userName for network access
+ const userName = 'balaji';
+
+ // Load connection profile; will be used to locate a gateway
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org1.yaml', 'utf8'));
+
+ // Set connection options; identity and wallet
+ let connectionOptions = {
+ identity: userName,
+ wallet: wallet,
+ discovery: { enabled: true, asLocalhost: true }
+
+ };
+
+ // Connect to gateway using application specified parameters
+ console.log('Connect to Fabric gateway.');
+
+ await gateway.connect(connectionProfile, connectionOptions);
+
+ // Access PaperNet network
+ console.log('Use network channel: mychannel.');
+
+ const network = await gateway.getNetwork('mychannel');
+
+ // Get addressability to commercial paper contract
+ console.log('Use org.papernet.commercialpaper smart contract.');
+
+ const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
+
+ // queries - commercial paper
+ console.log('-----------------------------------------------------------------------------------------');
+ console.log('****** Submitting commercial paper queries ****** \n\n ');
+
+
+ // 1 asset history
+ console.log('1. Query Commercial Paper History....');
+ console.log('-----------------------------------------------------------------------------------------\n');
+ let queryResponse = await contract.evaluateTransaction('queryHistory', 'MagnetoCorp', '00001');
+
+ let json = JSON.parse(queryResponse.toString());
+ console.log(json);
+ console.log('\n\n');
+ console.log('\n History query complete.');
+ console.log('-----------------------------------------------------------------------------------------\n\n');
+
+ // 2 ownership query
+ console.log('2. Query Commercial Paper Ownership.... Papers owned by MagnetoCorp');
+ console.log('-----------------------------------------------------------------------------------------\n');
+ let queryResponse2 = await contract.evaluateTransaction('queryOwner', 'MagnetoCorp');
+ json = JSON.parse(queryResponse2.toString());
+ console.log(json);
+
+ console.log('\n\n');
+ console.log('\n Paper Ownership query complete.');
+ console.log('-----------------------------------------------------------------------------------------\n\n');
+
+ // 3 partial key query
+ console.log('3. Query Commercial Paper Partial Key.... Papers in org.papernet.papers namespace and prefixed MagnetoCorp');
+ console.log('-----------------------------------------------------------------------------------------\n');
+ let queryResponse3 = await contract.evaluateTransaction('queryPartial', 'MagnetoCorp');
+
+ json = JSON.parse(queryResponse3.toString());
+ console.log(json);
+ console.log('\n\n');
+
+ console.log('\n Partial Key query complete.');
+ console.log('-----------------------------------------------------------------------------------------\n\n');
+
+
+ // 4 Named query - all redeemed papers
+ console.log('4. Named Query: ... All papers in org.papernet.papers that are in current state of redeemed');
+ console.log('-----------------------------------------------------------------------------------------\n');
+ let queryResponse4 = await contract.evaluateTransaction('queryNamed', 'redeemed');
+
+ json = JSON.parse(queryResponse4.toString());
+ console.log(json);
+ console.log('\n\n');
+
+ console.log('\n Named query "redeemed" complete.');
+ console.log('-----------------------------------------------------------------------------------------\n\n');
+
+
+ // 5 named query - by value
+ console.log('5. Named Query:.... All papers in org.papernet.papers with faceValue > 4000000');
+ console.log('-----------------------------------------------------------------------------------------\n');
+ let queryResponse5 = await contract.evaluateTransaction('queryNamed', 'value');
+
+ json = JSON.parse(queryResponse5.toString());
+ console.log(json);
+ console.log('\n\n');
+
+ console.log('\n Named query by "value" complete.');
+ console.log('-----------------------------------------------------------------------------------------\n\n');
+ } catch (error) {
+
+ console.log(`Error processing transaction. ${error}`);
+ console.log(error.stack);
+
+ } finally {
+
+ // Disconnect from the gateway
+ console.log('Disconnect from Fabric gateway.');
+ gateway.disconnect();
+
+ }
+}
+main().then(() => {
+
+ console.log('Queryapp program complete.');
+
+}).catch((e) => {
+
+ console.log('Queryapp program exception.');
+ console.log(e);
+ console.log(e.stack);
+ process.exit(-1);
+
+});
diff --git a/commercial-paper/organization/digibank/application/redeem.js b/commercial-paper/organization/digibank/application/redeem.js
new file mode 100644
index 0000000..0f6699e
--- /dev/null
+++ b/commercial-paper/organization/digibank/application/redeem.js
@@ -0,0 +1,106 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+ * This application has 6 basic steps:
+ * 1. Select an identity from a wallet
+ * 2. Connect to network gateway
+ * 3. Access PaperNet network
+ * 4. Construct request to issue commercial paper
+ * 5. Submit transaction
+ * 6. Process response
+ */
+
+'use strict';
+
+// Bring key classes into scope, most importantly Fabric SDK network class
+const fs = require('fs');
+const yaml = require('js-yaml');
+const { Wallets, Gateway } = require('fabric-network');
+const CommercialPaper = require('../contract/lib/paper.js');
+
+
+// Main program function
+async function main() {
+
+ // A wallet stores a collection of identities for use
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/balaji/wallet');
+
+
+ // A gateway defines the peers used to access Fabric networks
+ const gateway = new Gateway();
+
+ // Main try/catch block
+ try {
+
+ // Specify userName for network access
+ // Specify userName for network access
+ const userName = 'balaji';
+
+ // Load connection profile; will be used to locate a gateway
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org1.yaml', 'utf8'));
+
+ // Set connection options; identity and wallet
+ let connectionOptions = {
+ identity: userName,
+ wallet: wallet,
+ discovery: { enabled:true, asLocalhost: true }
+ };
+
+ // Connect to gateway using application specified parameters
+ console.log('Connect to Fabric gateway.');
+
+ await gateway.connect(connectionProfile, connectionOptions);
+
+ // Access PaperNet network
+ console.log('Use network channel: mychannel.');
+
+ const network = await gateway.getNetwork('mychannel');
+
+ // Get addressability to commercial paper contract
+ console.log('Use org.papernet.commercialpaper smart contract.');
+
+ const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
+
+ // redeem commercial paper
+ console.log('Submit commercial paper redeem transaction.');
+
+ const redeemResponse = await contract.submitTransaction('redeem', 'MagnetoCorp', '00001', 'DigiBank', 'Org2MSP', '2020-11-30');
+
+ // process response
+ console.log('Process redeem transaction response.');
+
+ let paper = CommercialPaper.fromBuffer(redeemResponse);
+
+ console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully redeemed with ${paper.owner}`);
+
+ console.log('Transaction complete.');
+
+ } catch (error) {
+
+ console.log(`Error processing transaction. ${error}`);
+ console.log(error.stack);
+
+ } finally {
+
+ // Disconnect from the gateway
+ console.log('Disconnect from Fabric gateway.')
+ gateway.disconnect();
+
+ }
+}
+main().then(() => {
+
+ console.log('Redeem program complete.');
+
+}).catch((e) => {
+
+ console.log('Redeem program exception.');
+ console.log(e);
+ console.log(e.stack);
+ process.exit(-1);
+
+});
diff --git a/commercial-paper/organization/digibank/configuration/cli/docker-compose.yml b/commercial-paper/organization/digibank/configuration/cli/docker-compose.yml
new file mode 100644
index 0000000..59b3885
--- /dev/null
+++ b/commercial-paper/organization/digibank/configuration/cli/docker-compose.yml
@@ -0,0 +1,38 @@
+#
+# Copyright IBM Corp All Rights Reserved
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+version: '2'
+
+networks:
+ basic:
+ external:
+ name: net_test
+
+services:
+ cliDigiBank:
+ container_name: cliDigiBank
+ image: hyperledger/fabric-tools:2.0.0-beta
+ tty: true
+ environment:
+ - GOPATH=/opt/gopath
+ - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
+ - FABRIC_LOGGING_SPEC=info
+ - CORE_PEER_ID=cli
+ - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
+ - CORE_PEER_LOCALMSPID=Org1MSP
+ - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
+ - CORE_CHAINCODE_KEEPALIVE=10
+ - CORE_PEER_TLS_ENABLED=true
+ - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
+ - ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
+ working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
+ command: /bin/bash
+ volumes:
+ - /var/run/docker.sock:/host/var/run/docker.sock
+ - ./../../../../organization/digibank:/opt/gopath/src/github.com/
+ - ./../../../../../test-network/organizations:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
+ networks:
+ - basic
+
diff --git a/commercial-paper/organization/digibank/configuration/cli/monitordocker.sh b/commercial-paper/organization/digibank/configuration/cli/monitordocker.sh
new file mode 100755
index 0000000..2cf82fb
--- /dev/null
+++ b/commercial-paper/organization/digibank/configuration/cli/monitordocker.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# This script uses the logspout and http stream tools to let you watch the docker containers
+# in action.
+#
+# More information at https://github.com/gliderlabs/logspout/tree/master/httpstream
+
+if [ -z "$1" ]; then
+ DOCKER_NETWORK=basicnetwork_basic
+else
+ DOCKER_NETWORK="$1"
+fi
+
+if [ -z "$2" ]; then
+ PORT=8000
+else
+ PORT="$2"
+fi
+
+echo Starting monitoring on all containers on the network ${DOCKER_NETWORK}
+
+docker kill logspout 2> /dev/null 1>&2 || true
+docker rm logspout 2> /dev/null 1>&2 || true
+
+docker run -d --name="logspout" \
+ --volume=/var/run/docker.sock:/var/run/docker.sock \
+ --publish=127.0.0.1:${PORT}:80 \
+ --network ${DOCKER_NETWORK} \
+ gliderlabs/logspout
+sleep 3
+curl http://127.0.0.1:${PORT}/logs
diff --git a/commercial-paper/organization/digibank/contract-go/commercial-paper/paper.go b/commercial-paper/organization/digibank/contract-go/commercial-paper/paper.go
new file mode 100644
index 0000000..94b072c
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/commercial-paper/paper.go
@@ -0,0 +1,139 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "encoding/json"
+ "fmt"
+
+ ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/digibank/contract-go/ledger-api"
+)
+
+// State enum for commercial paper state property
+type State uint
+
+const (
+ // ISSUED state for when a paper has been issued
+ ISSUED State = iota + 1
+ // TRADING state for when a paper is trading
+ TRADING
+ // REDEEMED state for when a paper has been redeemed
+ REDEEMED
+)
+
+func (state State) String() string {
+ names := []string{"ISSUED", "TRADING", "REDEEMED"}
+
+ if state < ISSUED || state > REDEEMED {
+ return "UNKNOWN"
+ }
+
+ return names[state-1]
+}
+
+// CreateCommercialPaperKey creates a key for commercial papers
+func CreateCommercialPaperKey(issuer string, paperNumber string) string {
+ return ledgerapi.MakeKey(issuer, paperNumber)
+}
+
+// Used for managing the fact status is private but want it in world state
+type commercialPaperAlias CommercialPaper
+type jsonCommercialPaper struct {
+ *commercialPaperAlias
+ State State `json:"currentState"`
+ Class string `json:"class"`
+ Key string `json:"key"`
+}
+
+// CommercialPaper defines a commercial paper
+type CommercialPaper struct {
+ PaperNumber string `json:"paperNumber"`
+ Issuer string `json:"issuer"`
+ IssueDateTime string `json:"issueDateTime"`
+ FaceValue int `json:"faceValue"`
+ MaturityDateTime string `json:"maturityDateTime"`
+ Owner string `json:"owner"`
+ state State `metadata:"currentState"`
+ class string `metadata:"class"`
+ key string `metadata:"key"`
+}
+
+// UnmarshalJSON special handler for managing JSON marshalling
+func (cp *CommercialPaper) UnmarshalJSON(data []byte) error {
+ jcp := jsonCommercialPaper{commercialPaperAlias: (*commercialPaperAlias)(cp)}
+
+ err := json.Unmarshal(data, &jcp)
+
+ if err != nil {
+ return err
+ }
+
+ cp.state = jcp.State
+
+ return nil
+}
+
+// MarshalJSON special handler for managing JSON marshalling
+func (cp CommercialPaper) MarshalJSON() ([]byte, error) {
+ jcp := jsonCommercialPaper{commercialPaperAlias: (*commercialPaperAlias)(&cp), State: cp.state, Class: "org.papernet.commercialpaper", Key: ledgerapi.MakeKey(cp.Issuer, cp.PaperNumber)}
+
+ return json.Marshal(&jcp)
+}
+
+// GetState returns the state
+func (cp *CommercialPaper) GetState() State {
+ return cp.state
+}
+
+// SetIssued returns the state to issued
+func (cp *CommercialPaper) SetIssued() {
+ cp.state = ISSUED
+}
+
+// SetTrading sets the state to trading
+func (cp *CommercialPaper) SetTrading() {
+ cp.state = TRADING
+}
+
+// SetRedeemed sets the state to redeemed
+func (cp *CommercialPaper) SetRedeemed() {
+ cp.state = REDEEMED
+}
+
+// IsIssued returns true if state is issued
+func (cp *CommercialPaper) IsIssued() bool {
+ return cp.state == ISSUED
+}
+
+// IsTrading returns true if state is trading
+func (cp *CommercialPaper) IsTrading() bool {
+ return cp.state == TRADING
+}
+
+// IsRedeemed returns true if state is redeemed
+func (cp *CommercialPaper) IsRedeemed() bool {
+ return cp.state == REDEEMED
+}
+
+// GetSplitKey returns values which should be used to form key
+func (cp *CommercialPaper) GetSplitKey() []string {
+ return []string{cp.Issuer, cp.PaperNumber}
+}
+
+// Serialize formats the commercial paper as JSON bytes
+func (cp *CommercialPaper) Serialize() ([]byte, error) {
+ return json.Marshal(cp)
+}
+
+// Deserialize formats the commercial paper from JSON bytes
+func Deserialize(bytes []byte, cp *CommercialPaper) error {
+ err := json.Unmarshal(bytes, cp)
+
+ if err != nil {
+ return fmt.Errorf("Error deserializing commercial paper. %s", err.Error())
+ }
+
+ return nil
+}
diff --git a/commercial-paper/organization/digibank/contract-go/commercial-paper/paper_test.go b/commercial-paper/organization/digibank/contract-go/commercial-paper/paper_test.go
new file mode 100644
index 0000000..6af65ef
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/commercial-paper/paper_test.go
@@ -0,0 +1,125 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "testing"
+
+ ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/digibank/contract-go/ledger-api"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestString(t *testing.T) {
+ assert.Equal(t, "ISSUED", ISSUED.String(), "should return string for issued")
+ assert.Equal(t, "TRADING", TRADING.String(), "should return string for issued")
+ assert.Equal(t, "REDEEMED", REDEEMED.String(), "should return string for issued")
+ assert.Equal(t, "UNKNOWN", State(REDEEMED+1).String(), "should return unknown when not one of constants")
+}
+
+func TestCreateCommercialPaperKey(t *testing.T) {
+ assert.Equal(t, ledgerapi.MakeKey("someissuer", "somepaper"), CreateCommercialPaperKey("someissuer", "somepaper"), "should return key comprised of passed values")
+}
+
+func TestGetState(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.state = ISSUED
+
+ assert.Equal(t, ISSUED, cp.GetState(), "should return set state")
+}
+
+func TestSetIssued(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.SetIssued()
+ assert.Equal(t, ISSUED, cp.state, "should set state to trading")
+}
+
+func TestSetTrading(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.SetTrading()
+ assert.Equal(t, TRADING, cp.state, "should set state to trading")
+}
+
+func TestSetRedeemed(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.SetRedeemed()
+ assert.Equal(t, REDEEMED, cp.state, "should set state to trading")
+}
+
+func TestIsIssued(t *testing.T) {
+ cp := new(CommercialPaper)
+
+ cp.SetIssued()
+ assert.True(t, cp.IsIssued(), "should be true when status set to issued")
+
+ cp.SetTrading()
+ assert.False(t, cp.IsIssued(), "should be false when status not set to issued")
+}
+
+func TestIsTrading(t *testing.T) {
+ cp := new(CommercialPaper)
+
+ cp.SetTrading()
+ assert.True(t, cp.IsTrading(), "should be true when status set to trading")
+
+ cp.SetRedeemed()
+ assert.False(t, cp.IsTrading(), "should be false when status not set to trading")
+}
+
+func TestIsRedeemed(t *testing.T) {
+ cp := new(CommercialPaper)
+
+ cp.SetRedeemed()
+ assert.True(t, cp.IsRedeemed(), "should be true when status set to redeemed")
+
+ cp.SetIssued()
+ assert.False(t, cp.IsRedeemed(), "should be false when status not set to redeemed")
+}
+
+func TestGetSplitKey(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.PaperNumber = "somepaper"
+ cp.Issuer = "someissuer"
+
+ assert.Equal(t, []string{"someissuer", "somepaper"}, cp.GetSplitKey(), "should return issuer and paper number as split key")
+}
+
+func TestSerialize(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.PaperNumber = "somepaper"
+ cp.Issuer = "someissuer"
+ cp.IssueDateTime = "sometime"
+ cp.FaceValue = 1000
+ cp.MaturityDateTime = "somelatertime"
+ cp.Owner = "someowner"
+ cp.state = TRADING
+
+ bytes, err := cp.Serialize()
+ assert.Nil(t, err, "should not error on serialize")
+ assert.Equal(t, `{"paperNumber":"somepaper","issuer":"someissuer","issueDateTime":"sometime","faceValue":1000,"maturityDateTime":"somelatertime","owner":"someowner","currentState":2,"class":"org.papernet.commercialpaper","key":"someissuer:somepaper"}`, string(bytes), "should return JSON formatted value")
+}
+
+func TestDeserialize(t *testing.T) {
+ var cp *CommercialPaper
+ var err error
+
+ goodJSON := `{"paperNumber":"somepaper","issuer":"someissuer","issueDateTime":"sometime","faceValue":1000,"maturityDateTime":"somelatertime","owner":"someowner","currentState":2,"class":"org.papernet.commercialpaper","key":"someissuer:somepaper"}`
+ expectedCp := new(CommercialPaper)
+ expectedCp.PaperNumber = "somepaper"
+ expectedCp.Issuer = "someissuer"
+ expectedCp.IssueDateTime = "sometime"
+ expectedCp.FaceValue = 1000
+ expectedCp.MaturityDateTime = "somelatertime"
+ expectedCp.Owner = "someowner"
+ expectedCp.state = TRADING
+ cp = new(CommercialPaper)
+ err = Deserialize([]byte(goodJSON), cp)
+ assert.Nil(t, err, "should not return error for deserialize")
+ assert.Equal(t, expectedCp, cp, "should create expected commercial paper")
+
+ badJSON := `{"paperNumber":"somepaper","issuer":"someissuer","issueDateTime":"sometime","faceValue":"NaN","maturityDateTime":"somelatertime","owner":"someowner","currentState":2,"class":"org.papernet.commercialpaper","key":"someissuer:somepaper"}`
+ cp = new(CommercialPaper)
+ err = Deserialize([]byte(badJSON), cp)
+ assert.EqualError(t, err, "Error deserializing commercial paper. json: cannot unmarshal string into Go struct field jsonCommercialPaper.faceValue of type int", "should return error for bad data")
+}
diff --git a/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontext.go b/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontext.go
new file mode 100644
index 0000000..c346cf3
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontext.go
@@ -0,0 +1,35 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// TransactionContextInterface an interface to
+// describe the minimum required functions for
+// a transaction context in the commercial
+// paper
+type TransactionContextInterface interface {
+ contractapi.TransactionContextInterface
+ GetPaperList() ListInterface
+}
+
+// TransactionContext implementation of
+// TransactionContextInterface for use with
+// commercial paper contract
+type TransactionContext struct {
+ contractapi.TransactionContext
+ paperList *list
+}
+
+// GetPaperList return paper list
+func (tc *TransactionContext) GetPaperList() ListInterface {
+ if tc.paperList == nil {
+ tc.paperList = newList(tc)
+ }
+
+ return tc.paperList
+}
diff --git a/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontext_test.go b/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontext_test.go
new file mode 100644
index 0000000..81317aa
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontext_test.go
@@ -0,0 +1,31 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "testing"
+
+ ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/digibank/contract-go/ledger-api"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetPaperList(t *testing.T) {
+ var tc *TransactionContext
+ var expectedPaperList *list
+
+ tc = new(TransactionContext)
+ expectedPaperList = newList(tc)
+ actualList := tc.GetPaperList().(*list)
+ assert.Equal(t, expectedPaperList.stateList.(*ledgerapi.StateList).Name, actualList.stateList.(*ledgerapi.StateList).Name, "should configure paper list when one not already configured")
+
+ tc = new(TransactionContext)
+ expectedPaperList = new(list)
+ expectedStateList := new(ledgerapi.StateList)
+ expectedStateList.Ctx = tc
+ expectedStateList.Name = "existing paper list"
+ expectedPaperList.stateList = expectedStateList
+ tc.paperList = expectedPaperList
+ assert.Equal(t, expectedPaperList, tc.GetPaperList(), "should return set paper list when already set")
+}
diff --git a/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontract.go b/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontract.go
new file mode 100644
index 0000000..4e8cee2
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontract.go
@@ -0,0 +1,96 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// Contract chaincode that defines
+// the business logic for managing commercial
+// paper
+type Contract struct {
+ contractapi.Contract
+}
+
+// Instantiate does nothing
+func (c *Contract) Instantiate() {
+ fmt.Println("Instantiated")
+}
+
+// Issue creates a new commercial paper and stores it in the world state
+func (c *Contract) Issue(ctx TransactionContextInterface, issuer string, paperNumber string, issueDateTime string, maturityDateTime string, faceValue int) (*CommercialPaper, error) {
+ paper := CommercialPaper{PaperNumber: paperNumber, Issuer: issuer, IssueDateTime: issueDateTime, FaceValue: faceValue, MaturityDateTime: maturityDateTime, Owner: issuer}
+ paper.SetIssued()
+
+ err := ctx.GetPaperList().AddPaper(&paper)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return &paper, nil
+}
+
+// Buy updates a commercial paper to be in trading status and sets the new owner
+func (c *Contract) Buy(ctx TransactionContextInterface, issuer string, paperNumber string, currentOwner string, newOwner string, price int, purchaseDateTime string) (*CommercialPaper, error) {
+ paper, err := ctx.GetPaperList().GetPaper(issuer, paperNumber)
+
+ if err != nil {
+ return nil, err
+ }
+
+ if paper.Owner != currentOwner {
+ return nil, fmt.Errorf("Paper %s:%s is not owned by %s", issuer, paperNumber, currentOwner)
+ }
+
+ if paper.IsIssued() {
+ paper.SetTrading()
+ }
+
+ if !paper.IsTrading() {
+ return nil, fmt.Errorf("Paper %s:%s is not trading. Current state = %s", issuer, paperNumber, paper.GetState())
+ }
+
+ paper.Owner = newOwner
+
+ err = ctx.GetPaperList().UpdatePaper(paper)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return paper, nil
+}
+
+// Redeem updates a commercial paper status to be redeemed
+func (c *Contract) Redeem(ctx TransactionContextInterface, issuer string, paperNumber string, redeemingOwner string, redeenDateTime string) (*CommercialPaper, error) {
+ paper, err := ctx.GetPaperList().GetPaper(issuer, paperNumber)
+
+ if err != nil {
+ return nil, err
+ }
+
+ if paper.Owner != redeemingOwner {
+ return nil, fmt.Errorf("Paper %s:%s is not owned by %s", issuer, paperNumber, redeemingOwner)
+ }
+
+ if paper.IsRedeemed() {
+ return nil, fmt.Errorf("Paper %s:%s is already redeemed", issuer, paperNumber)
+ }
+
+ paper.Owner = paper.Issuer
+ paper.SetRedeemed()
+
+ err = ctx.GetPaperList().UpdatePaper(paper)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return paper, nil
+}
diff --git a/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontract_test.go b/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontract_test.go
new file mode 100644
index 0000000..25c429b
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/commercial-paper/papercontract_test.go
@@ -0,0 +1,185 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+)
+
+// #########
+// HELPERS
+// #########
+type MockPaperList struct {
+ mock.Mock
+}
+
+func (mpl *MockPaperList) AddPaper(paper *CommercialPaper) error {
+ args := mpl.Called(paper)
+
+ return args.Error(0)
+}
+
+func (mpl *MockPaperList) GetPaper(issuer string, papernumber string) (*CommercialPaper, error) {
+ args := mpl.Called(issuer, papernumber)
+
+ return args.Get(0).(*CommercialPaper), args.Error(1)
+}
+
+func (mpl *MockPaperList) UpdatePaper(paper *CommercialPaper) error {
+ args := mpl.Called(paper)
+
+ return args.Error(0)
+}
+
+type MockTransactionContext struct {
+ contractapi.TransactionContext
+ paperList *MockPaperList
+}
+
+func (mtc *MockTransactionContext) GetPaperList() ListInterface {
+ return mtc.paperList
+}
+
+func resetPaper(paper *CommercialPaper) {
+ paper.Owner = "someowner"
+ paper.SetTrading()
+}
+
+// #########
+// TESTS
+// #########
+
+func TestIssue(t *testing.T) {
+ var paper *CommercialPaper
+ var err error
+
+ mpl := new(MockPaperList)
+ ctx := new(MockTransactionContext)
+ ctx.paperList = mpl
+
+ contract := new(Contract)
+
+ var sentPaper *CommercialPaper
+
+ mpl.On("AddPaper", mock.MatchedBy(func(paper *CommercialPaper) bool { sentPaper = paper; return paper.Issuer == "someissuer" })).Return(nil)
+ mpl.On("AddPaper", mock.MatchedBy(func(paper *CommercialPaper) bool { sentPaper = paper; return paper.Issuer == "someotherissuer" })).Return(errors.New("AddPaper error"))
+
+ expectedPaper := CommercialPaper{PaperNumber: "somepaper", Issuer: "someissuer", IssueDateTime: "someissuedate", FaceValue: 1000, MaturityDateTime: "somematuritydate", Owner: "someissuer", state: 1}
+ paper, err = contract.Issue(ctx, "someissuer", "somepaper", "someissuedate", "somematuritydate", 1000)
+ assert.Nil(t, err, "should not error when add paper does not error")
+ assert.Equal(t, sentPaper, paper, "should send the same paper as it returns to add paper")
+ assert.Equal(t, expectedPaper, *paper, "should correctly configure paper")
+
+ paper, err = contract.Issue(ctx, "someotherissuer", "somepaper", "someissuedate", "somematuritydate", 1000)
+ assert.EqualError(t, err, "AddPaper error", "should return error when add paper fails")
+ assert.Nil(t, paper, "should not return paper when fails")
+}
+
+func TestBuy(t *testing.T) {
+ var paper *CommercialPaper
+ var err error
+
+ mpl := new(MockPaperList)
+ ctx := new(MockTransactionContext)
+ ctx.paperList = mpl
+
+ contract := new(Contract)
+
+ wsPaper := new(CommercialPaper)
+ resetPaper(wsPaper)
+
+ var sentPaper *CommercialPaper
+ var emptyPaper *CommercialPaper
+ shouldError := false
+
+ mpl.On("GetPaper", "someissuer", "somepaper").Return(wsPaper, nil)
+ mpl.On("GetPaper", "someotherissuer", "someotherpaper").Return(emptyPaper, errors.New("GetPaper error"))
+ mpl.On("UpdatePaper", mock.MatchedBy(func(paper *CommercialPaper) bool { return shouldError })).Return(errors.New("UpdatePaper error"))
+ mpl.On("UpdatePaper", mock.MatchedBy(func(paper *CommercialPaper) bool { sentPaper = paper; return !shouldError })).Return(nil)
+
+ paper, err = contract.Buy(ctx, "someotherissuer", "someotherpaper", "someowner", "someotherowner", 100, "2019-12-10:10:00")
+ assert.EqualError(t, err, "GetPaper error", "should return error when GetPaper errors")
+ assert.Nil(t, paper, "should return nil for paper when GetPaper errors")
+
+ paper, err = contract.Buy(ctx, "someissuer", "somepaper", "someotherowner", "someowner", 100, "2019-12-10:10:00")
+ assert.EqualError(t, err, "Paper someissuer:somepaper is not owned by someotherowner", "should error when sent owner not correct")
+ assert.Nil(t, paper, "should not return paper for bad owner error")
+
+ resetPaper(wsPaper)
+ wsPaper.SetRedeemed()
+ paper, err = contract.Buy(ctx, "someissuer", "somepaper", "someowner", "someotherowner", 100, "2019-12-10:10:00")
+ assert.EqualError(t, err, "Paper someissuer:somepaper is not trading. Current state = REDEEMED")
+ assert.Nil(t, paper, "should not return paper for bad state error")
+
+ resetPaper(wsPaper)
+ shouldError = true
+ paper, err = contract.Buy(ctx, "someissuer", "somepaper", "someowner", "someotherowner", 100, "2019-12-10:10:00")
+ assert.EqualError(t, err, "UpdatePaper error", "should error when update paper fails")
+ assert.Nil(t, paper, "should not return paper for bad state error")
+ shouldError = false
+
+ resetPaper(wsPaper)
+ wsPaper.SetIssued()
+ paper, err = contract.Buy(ctx, "someissuer", "somepaper", "someowner", "someotherowner", 100, "2019-12-10:10:00")
+ assert.Nil(t, err, "should not error when good paper and owner")
+ assert.Equal(t, "someotherowner", paper.Owner, "should update the owner of the paper")
+ assert.True(t, paper.IsTrading(), "should mark issued paper as trading")
+ assert.Equal(t, sentPaper, paper, "should update same paper as it returns in the world state")
+}
+
+func TestRedeem(t *testing.T) {
+ var paper *CommercialPaper
+ var err error
+
+ mpl := new(MockPaperList)
+ ctx := new(MockTransactionContext)
+ ctx.paperList = mpl
+
+ contract := new(Contract)
+
+ var sentPaper *CommercialPaper
+ wsPaper := new(CommercialPaper)
+ resetPaper(wsPaper)
+
+ var emptyPaper *CommercialPaper
+ shouldError := false
+
+ mpl.On("GetPaper", "someissuer", "somepaper").Return(wsPaper, nil)
+ mpl.On("GetPaper", "someotherissuer", "someotherpaper").Return(emptyPaper, errors.New("GetPaper error"))
+ mpl.On("UpdatePaper", mock.MatchedBy(func(paper *CommercialPaper) bool { return shouldError })).Return(errors.New("UpdatePaper error"))
+ mpl.On("UpdatePaper", mock.MatchedBy(func(paper *CommercialPaper) bool { sentPaper = paper; return !shouldError })).Return(nil)
+
+ paper, err = contract.Redeem(ctx, "someotherissuer", "someotherpaper", "someowner", "2021-12-10:10:00")
+ assert.EqualError(t, err, "GetPaper error", "should error when GetPaper errors")
+ assert.Nil(t, paper, "should not return paper when GetPaper errors")
+
+ paper, err = contract.Redeem(ctx, "someissuer", "somepaper", "someotherowner", "2021-12-10:10:00")
+ assert.EqualError(t, err, "Paper someissuer:somepaper is not owned by someotherowner", "should error when paper owned by someone else")
+ assert.Nil(t, paper, "should not return paper when errors as owned by someone else")
+
+ resetPaper(wsPaper)
+ wsPaper.SetRedeemed()
+ paper, err = contract.Redeem(ctx, "someissuer", "somepaper", "someowner", "2021-12-10:10:00")
+ assert.EqualError(t, err, "Paper someissuer:somepaper is already redeemed", "should error when paper already redeemed")
+ assert.Nil(t, paper, "should not return paper when errors as already redeemed")
+
+ shouldError = true
+ resetPaper(wsPaper)
+ paper, err = contract.Redeem(ctx, "someissuer", "somepaper", "someowner", "2021-12-10:10:00")
+ assert.EqualError(t, err, "UpdatePaper error", "should error when update paper errors")
+ assert.Nil(t, paper, "should not return paper when UpdatePaper errors")
+ shouldError = false
+
+ resetPaper(wsPaper)
+ paper, err = contract.Redeem(ctx, "someissuer", "somepaper", "someowner", "2021-12-10:10:00")
+ assert.Nil(t, err, "should not error on good redeem")
+ assert.True(t, paper.IsRedeemed(), "should return redeemed paper")
+ assert.Equal(t, sentPaper, paper, "should update same paper as it returns in the world state")
+}
diff --git a/commercial-paper/organization/digibank/contract-go/commercial-paper/paperlist.go b/commercial-paper/organization/digibank/contract-go/commercial-paper/paperlist.go
new file mode 100644
index 0000000..c3bdf81
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/commercial-paper/paperlist.go
@@ -0,0 +1,55 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/digibank/contract-go/ledger-api"
+
+// ListInterface defines functionality needed
+// to interact with the world state on behalf
+// of a commercial paper
+type ListInterface interface {
+ AddPaper(*CommercialPaper) error
+ GetPaper(string, string) (*CommercialPaper, error)
+ UpdatePaper(*CommercialPaper) error
+}
+
+type list struct {
+ stateList ledgerapi.StateListInterface
+}
+
+func (cpl *list) AddPaper(paper *CommercialPaper) error {
+ return cpl.stateList.AddState(paper)
+}
+
+func (cpl *list) GetPaper(issuer string, paperNumber string) (*CommercialPaper, error) {
+ cp := new(CommercialPaper)
+
+ err := cpl.stateList.GetState(CreateCommercialPaperKey(issuer, paperNumber), cp)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return cp, nil
+}
+
+func (cpl *list) UpdatePaper(paper *CommercialPaper) error {
+ return cpl.stateList.UpdateState(paper)
+}
+
+// NewList create a new list from context
+func newList(ctx TransactionContextInterface) *list {
+ stateList := new(ledgerapi.StateList)
+ stateList.Ctx = ctx
+ stateList.Name = "org.papernet.commercialpaperlist"
+ stateList.Deserialize = func(bytes []byte, state ledgerapi.StateInterface) error {
+ return Deserialize(bytes, state.(*CommercialPaper))
+ }
+
+ list := new(list)
+ list.stateList = stateList
+
+ return list
+}
diff --git a/commercial-paper/organization/digibank/contract-go/commercial-paper/paperlist_test.go b/commercial-paper/organization/digibank/contract-go/commercial-paper/paperlist_test.go
new file mode 100644
index 0000000..33a2c30
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/commercial-paper/paperlist_test.go
@@ -0,0 +1,103 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "errors"
+ "testing"
+
+ ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/digibank/contract-go/ledger-api"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+)
+
+// #########
+// HELPERS
+// #########
+
+type MockStateList struct {
+ mock.Mock
+}
+
+func (msl *MockStateList) AddState(state ledgerapi.StateInterface) error {
+ args := msl.Called(state)
+
+ return args.Error(0)
+}
+
+func (msl *MockStateList) GetState(key string, state ledgerapi.StateInterface) error {
+ args := msl.Called(key, state)
+
+ state.(*CommercialPaper).PaperNumber = "somepaper"
+
+ return args.Error(0)
+}
+
+func (msl *MockStateList) UpdateState(state ledgerapi.StateInterface) error {
+ args := msl.Called(state)
+
+ return args.Error(0)
+}
+
+// #########
+// TESTS
+// #########
+
+func TestAddPaper(t *testing.T) {
+ paper := new(CommercialPaper)
+
+ list := new(list)
+ msl := new(MockStateList)
+ msl.On("AddState", paper).Return(errors.New("Called add state correctly"))
+ list.stateList = msl
+
+ err := list.AddPaper(paper)
+ assert.EqualError(t, err, "Called add state correctly", "should call state list add state with paper")
+}
+
+func TestGetPaper(t *testing.T) {
+ var cp *CommercialPaper
+ var err error
+
+ list := new(list)
+ msl := new(MockStateList)
+ msl.On("GetState", CreateCommercialPaperKey("someissuer", "somepaper"), mock.MatchedBy(func(state ledgerapi.StateInterface) bool { _, ok := state.(*CommercialPaper); return ok })).Return(nil)
+ msl.On("GetState", CreateCommercialPaperKey("someotherissuer", "someotherpaper"), mock.MatchedBy(func(state ledgerapi.StateInterface) bool { _, ok := state.(*CommercialPaper); return ok })).Return(errors.New("GetState error"))
+ list.stateList = msl
+
+ cp, err = list.GetPaper("someissuer", "somepaper")
+ assert.Nil(t, err, "should not error when get state on state list does not error")
+ assert.Equal(t, cp.PaperNumber, "somepaper", "should use state list GetState to fill commercial paper")
+
+ cp, err = list.GetPaper("someotherissuer", "someotherpaper")
+ assert.EqualError(t, err, "GetState error", "should return error when state list get state errors")
+ assert.Nil(t, cp, "should not return commercial paper on error")
+}
+
+func TestUpdatePaper(t *testing.T) {
+ paper := new(CommercialPaper)
+
+ list := new(list)
+ msl := new(MockStateList)
+ msl.On("UpdateState", paper).Return(errors.New("Called update state correctly"))
+ list.stateList = msl
+
+ err := list.UpdatePaper(paper)
+ assert.EqualError(t, err, "Called update state correctly", "should call state list update state with paper")
+}
+
+func TestNewStateList(t *testing.T) {
+ ctx := new(TransactionContext)
+ list := newList(ctx)
+ stateList, ok := list.stateList.(*ledgerapi.StateList)
+
+ assert.True(t, ok, "should make statelist of type ledgerapi.StateList")
+ assert.Equal(t, ctx, stateList.Ctx, "should set the context to passed context")
+ assert.Equal(t, "org.papernet.commercialpaperlist", stateList.Name, "should set the name for the list")
+
+ expectedErr := Deserialize([]byte("bad json"), new(CommercialPaper))
+ err := stateList.Deserialize([]byte("bad json"), new(CommercialPaper))
+ assert.EqualError(t, err, expectedErr.Error(), "should call Deserialize when stateList.Deserialize called")
+}
diff --git a/commercial-paper/organization/digibank/contract-go/go.mod b/commercial-paper/organization/digibank/contract-go/go.mod
new file mode 100644
index 0000000..4ee4f17
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/go.mod
@@ -0,0 +1,13 @@
+module github.com/hyperledger/fabric-samples/commercial-paper/organization/digibank/contract-go
+
+go 1.13
+
+require (
+ github.com/go-openapi/jsonreference v0.19.3 // indirect
+ github.com/hyperledger/fabric-contract-api-go v1.1.0
+ github.com/mailru/easyjson v0.7.0 // indirect
+ github.com/stretchr/testify v1.5.1
+ golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 // indirect
+ google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect
+ google.golang.org/grpc v1.24.0 // indirect
+)
diff --git a/commercial-paper/organization/digibank/contract-go/go.sum b/commercial-paper/organization/digibank/contract-go/go.sum
new file mode 100644
index 0000000..48c8221
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/go.sum
@@ -0,0 +1,167 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
+github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss=
+golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo=
+google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
+google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/commercial-paper/organization/digibank/contract-go/ledger-api/state.go b/commercial-paper/organization/digibank/contract-go/ledger-api/state.go
new file mode 100644
index 0000000..6d8c3f8
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/ledger-api/state.go
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package ledgerapi
+
+import (
+ "strings"
+)
+
+// SplitKey splits a key on colon
+func SplitKey(key string) []string {
+ return strings.Split(key, ":")
+}
+
+// MakeKey joins key parts using colon
+func MakeKey(keyParts ...string) string {
+ return strings.Join(keyParts, ":")
+}
+
+// StateInterface interface states must implement
+// for use in a list
+type StateInterface interface {
+ // GetSplitKey return components that combine to form the key
+ GetSplitKey() []string
+ Serialize() ([]byte, error)
+}
diff --git a/commercial-paper/organization/digibank/contract-go/ledger-api/statelist.go b/commercial-paper/organization/digibank/contract-go/ledger-api/statelist.go
new file mode 100644
index 0000000..492efb3
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/ledger-api/statelist.go
@@ -0,0 +1,61 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package ledgerapi
+
+import (
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// StateListInterface functions that a state list
+// should have
+type StateListInterface interface {
+ AddState(StateInterface) error
+ GetState(string, StateInterface) error
+ UpdateState(StateInterface) error
+}
+
+// StateList useful for managing putting data in and out
+// of the ledger. Implementation of StateListInterface
+type StateList struct {
+ Ctx contractapi.TransactionContextInterface
+ Name string
+ Deserialize func([]byte, StateInterface) error
+}
+
+// AddState puts state into world state
+func (sl *StateList) AddState(state StateInterface) error {
+ key, _ := sl.Ctx.GetStub().CreateCompositeKey(sl.Name, state.GetSplitKey())
+ data, err := state.Serialize()
+
+ if err != nil {
+ return err
+ }
+
+ return sl.Ctx.GetStub().PutState(key, data)
+}
+
+// GetState returns state from world state. Unmarshalls the JSON
+// into passed state. Key is the split key value used in Add/Update
+// joined using a colon
+func (sl *StateList) GetState(key string, state StateInterface) error {
+ ledgerKey, _ := sl.Ctx.GetStub().CreateCompositeKey(sl.Name, SplitKey(key))
+ data, err := sl.Ctx.GetStub().GetState(ledgerKey)
+
+ if err != nil {
+ return err
+ } else if data == nil {
+ return fmt.Errorf("No state found for %s", key)
+ }
+
+ return sl.Deserialize(data, state)
+}
+
+// UpdateState puts state into world state. Same as AddState but
+// separate as semantically different
+func (sl *StateList) UpdateState(state StateInterface) error {
+ return sl.AddState(state)
+}
diff --git a/commercial-paper/organization/digibank/contract-go/main.go b/commercial-paper/organization/digibank/contract-go/main.go
new file mode 100644
index 0000000..002c4f9
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-go/main.go
@@ -0,0 +1,35 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ "github.com/hyperledger/fabric-samples/commercial-paper/organization/digibank/contract-go/commercial-paper"
+)
+
+func main() {
+
+ contract := new(commercialpaper.Contract)
+ contract.TransactionContextHandler = new(commercialpaper.TransactionContext)
+ contract.Name = "org.papernet.commercialpaper"
+ contract.Info.Version = "0.0.1"
+
+ chaincode, err := contractapi.NewChaincode(contract)
+
+ if err != nil {
+ panic(fmt.Sprintf("Error creating chaincode. %s", err.Error()))
+ }
+
+ chaincode.Info.Title = "CommercialPaperChaincode"
+ chaincode.Info.Version = "0.0.1"
+
+ err = chaincode.Start()
+
+ if err != nil {
+ panic(fmt.Sprintf("Error starting chaincode. %s", err.Error()))
+ }
+}
diff --git a/commercial-paper/organization/digibank/contract-java/.gitignore b/commercial-paper/organization/digibank/contract-java/.gitignore
new file mode 100644
index 0000000..ae1478c
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/.gitignore
@@ -0,0 +1,10 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+/.classpath
+/.gradle/
+/.project
+/.settings/
+/bin/
+/build/
diff --git a/commercial-paper/organization/digibank/contract-java/build.gradle b/commercial-paper/organization/digibank/contract-java/build.gradle
new file mode 100644
index 0000000..c0a2b43
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/build.gradle
@@ -0,0 +1,38 @@
+plugins {
+ id 'java-library-distribution'
+}
+
+version '0.0.1'
+
+sourceCompatibility = 1.8
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven {
+ url 'https://jitpack.io'
+ }
+ maven {
+ url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
+ }
+
+}
+
+dependencies {
+ compileOnly group: 'org.hyperledger.fabric-chaincode-java', name: 'fabric-chaincode-shim', version: '2.+'
+ compile group: 'org.json', name: 'json', version: '20180813'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
+ testImplementation 'org.assertj:assertj-core:3.11.1'
+ testImplementation 'org.mockito:mockito-core:2.+'
+}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
+
+tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters"
+}
diff --git a/commercial-paper/organization/digibank/contract-java/gradle/wrapper/gradle-wrapper.jar b/commercial-paper/organization/digibank/contract-java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
Binary files /dev/null and b/commercial-paper/organization/digibank/contract-java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/commercial-paper/organization/digibank/contract-java/gradle/wrapper/gradle-wrapper.properties b/commercial-paper/organization/digibank/contract-java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..7c4388a
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/commercial-paper/organization/digibank/contract-java/gradlew b/commercial-paper/organization/digibank/contract-java/gradlew
new file mode 100755
index 0000000..83f2acf
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/commercial-paper/organization/digibank/contract-java/gradlew.bat b/commercial-paper/organization/digibank/contract-java/gradlew.bat
new file mode 100644
index 0000000..24467a1
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/commercial-paper/organization/digibank/contract-java/settings.gradle b/commercial-paper/organization/digibank/contract-java/settings.gradle
new file mode 100644
index 0000000..0c5f072
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'papercontract'
+
diff --git a/commercial-paper/organization/digibank/contract-java/shadow-build.gradle b/commercial-paper/organization/digibank/contract-java/shadow-build.gradle
new file mode 100644
index 0000000..0a29887
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/shadow-build.gradle
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+plugins {
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+ id 'java'
+}
+
+
+version '0.0.1'
+
+sourceCompatibility = 1.8
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+dependencies {
+ implementation group: 'org.hyperledger.fabric-chaincode-java', name: 'fabric-chaincode-shim', version: '2.+'
+ implementation group: 'org.json', name: 'json', version: '20180813'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
+ testImplementation 'org.assertj:assertj-core:3.11.1'
+ testImplementation 'org.mockito:mockito-core:2.+'
+}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
+
+shadowJar {
+ baseName = 'chaincode'
+ version = null
+ classifier = null
+
+ manifest {
+ attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter'
+ }
+}
+
+
+tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters"
+}
diff --git a/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaper.java b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaper.java
new file mode 100644
index 0000000..13d16b6
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaper.java
@@ -0,0 +1,183 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.example;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.example.ledgerapi.State;
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+import org.json.JSONObject;
+import org.json.JSONPropertyIgnore;
+
+@DataType()
+public class CommercialPaper extends State {
+
+ // Enumerate commercial paper state values
+ public final static String ISSUED = "ISSUED";
+ public final static String TRADING = "TRADING";
+ public final static String REDEEMED = "REDEEMED";
+
+ @Property()
+ private String state="";
+
+ public String getState() {
+ return state;
+ }
+
+ public CommercialPaper setState(String state) {
+ this.state = state;
+ return this;
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isIssued() {
+ return this.state.equals(CommercialPaper.ISSUED);
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isTrading() {
+ return this.state.equals(CommercialPaper.TRADING);
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isRedeemed() {
+ return this.state.equals(CommercialPaper.REDEEMED);
+ }
+
+ public CommercialPaper setIssued() {
+ this.state = CommercialPaper.ISSUED;
+ return this;
+ }
+
+ public CommercialPaper setTrading() {
+ this.state = CommercialPaper.TRADING;
+ return this;
+ }
+
+ public CommercialPaper setRedeemed() {
+ this.state = CommercialPaper.REDEEMED;
+ return this;
+ }
+
+ @Property()
+ private String paperNumber;
+
+ @Property()
+ private String issuer;
+
+ @Property()
+ private String issueDateTime;
+
+ @Property()
+ private int faceValue;
+
+ @Property()
+ private String maturityDateTime;
+
+ @Property()
+ private String owner;
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public CommercialPaper setOwner(String owner) {
+ this.owner = owner;
+ return this;
+ }
+
+ public CommercialPaper() {
+ super();
+ }
+
+ public CommercialPaper setKey() {
+ this.key = State.makeKey(new String[] { this.paperNumber });
+ return this;
+ }
+
+ public String getPaperNumber() {
+ return paperNumber;
+ }
+
+ public CommercialPaper setPaperNumber(String paperNumber) {
+ this.paperNumber = paperNumber;
+ return this;
+ }
+
+ public String getIssuer() {
+ return issuer;
+ }
+
+ public CommercialPaper setIssuer(String issuer) {
+ this.issuer = issuer;
+ return this;
+ }
+
+ public String getIssueDateTime() {
+ return issueDateTime;
+ }
+
+ public CommercialPaper setIssueDateTime(String issueDateTime) {
+ this.issueDateTime = issueDateTime;
+ return this;
+ }
+
+ public int getFaceValue() {
+ return faceValue;
+ }
+
+ public CommercialPaper setFaceValue(int faceValue) {
+ this.faceValue = faceValue;
+ return this;
+ }
+
+ public String getMaturityDateTime() {
+ return maturityDateTime;
+ }
+
+ public CommercialPaper setMaturityDateTime(String maturityDateTime) {
+ this.maturityDateTime = maturityDateTime;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "Paper::" + this.key + " " + this.getPaperNumber() + " " + getIssuer() + " " + getFaceValue();
+ }
+
+ /**
+ * Deserialize a state data to commercial paper
+ *
+ * @param {Buffer} data to form back into the object
+ */
+ public static CommercialPaper deserialize(byte[] data) {
+ JSONObject json = new JSONObject(new String(data, UTF_8));
+
+ String issuer = json.getString("issuer");
+ String paperNumber = json.getString("paperNumber");
+ String issueDateTime = json.getString("issueDateTime");
+ String maturityDateTime = json.getString("maturityDateTime");
+ String owner = json.getString("owner");
+ int faceValue = json.getInt("faceValue");
+ String state = json.getString("state");
+ return createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue,owner,state);
+ }
+
+ public static byte[] serialize(CommercialPaper paper) {
+ return State.serialize(paper);
+ }
+
+ /**
+ * Factory method to create a commercial paper object
+ */
+ public static CommercialPaper createInstance(String issuer, String paperNumber, String issueDateTime,
+ String maturityDateTime, int faceValue, String owner, String state) {
+ return new CommercialPaper().setIssuer(issuer).setPaperNumber(paperNumber).setMaturityDateTime(maturityDateTime)
+ .setFaceValue(faceValue).setKey().setIssueDateTime(issueDateTime).setOwner(owner).setState(state);
+ }
+
+
+}
diff --git a/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaperContext.java b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaperContext.java
new file mode 100644
index 0000000..7a946f2
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaperContext.java
@@ -0,0 +1,15 @@
+package org.example;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+
+class CommercialPaperContext extends Context {
+
+ public CommercialPaperContext(ChaincodeStub stub) {
+ super(stub);
+ this.paperList = new PaperList(this);
+ }
+
+ public PaperList paperList;
+
+}
\ No newline at end of file
diff --git a/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaperContract.java b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaperContract.java
new file mode 100644
index 0000000..a75f47e
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/CommercialPaperContract.java
@@ -0,0 +1,170 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+package org.example;
+
+import java.util.logging.Logger;
+
+import org.example.ledgerapi.State;
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.contract.ContractInterface;
+import org.hyperledger.fabric.contract.annotation.Contact;
+import org.hyperledger.fabric.contract.annotation.Contract;
+import org.hyperledger.fabric.contract.annotation.Default;
+import org.hyperledger.fabric.contract.annotation.Info;
+import org.hyperledger.fabric.contract.annotation.License;
+import org.hyperledger.fabric.contract.annotation.Transaction;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+
+/**
+ * A custom context provides easy access to list of all commercial papers
+ */
+
+/**
+ * Define commercial paper smart contract by extending Fabric Contract class
+ *
+ */
+@Contract(name = "org.papernet.commercialpaper", info = @Info(title = "MyAsset contract", description = "", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: ", url = ""), contact = @Contact(email = "java-contract@example.com", name = "java-contract", url = "http://java-contract.me")))
+@Default
+public class CommercialPaperContract implements ContractInterface {
+
+ // use the classname for the logger, this way you can refactor
+ private final static Logger LOG = Logger.getLogger(CommercialPaperContract.class.getName());
+
+ @Override
+ public Context createContext(ChaincodeStub stub) {
+ return new CommercialPaperContext(stub);
+ }
+
+ public CommercialPaperContract() {
+
+ }
+
+ /**
+ * Define a custom context for commercial paper
+ */
+
+ /**
+ * Instantiate to perform any setup of the ledger that might be required.
+ *
+ * @param {Context} ctx the transaction context
+ */
+ @Transaction
+ public void instantiate(CommercialPaperContext ctx) {
+ // No implementation required with this example
+ // It could be where data migration is performed, if necessary
+ LOG.info("No data migration to perform");
+ }
+
+ /**
+ * Issue commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} issueDateTime paper issue date
+ * @param {String} maturityDateTime paper maturity date
+ * @param {Integer} faceValue face value of paper
+ */
+ @Transaction
+ public CommercialPaper issue(CommercialPaperContext ctx, String issuer, String paperNumber, String issueDateTime,
+ String maturityDateTime, int faceValue) {
+
+ System.out.println(ctx);
+
+ // create an instance of the paper
+ CommercialPaper paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime, maturityDateTime,
+ faceValue,issuer,"");
+
+ // Smart contract, rather than paper, moves paper into ISSUED state
+ paper.setIssued();
+
+ // Newly issued paper is owned by the issuer
+ paper.setOwner(issuer);
+
+ System.out.println(paper);
+ // Add the paper to the list of all similar commercial papers in the ledger
+ // world state
+ ctx.paperList.addPaper(paper);
+
+ // Must return a serialized paper to caller of smart contract
+ return paper;
+ }
+
+ /**
+ * Buy commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} currentOwner current owner of paper
+ * @param {String} newOwner new owner of paper
+ * @param {Integer} price price paid for this paper
+ * @param {String} purchaseDateTime time paper was purchased (i.e. traded)
+ */
+ @Transaction
+ public CommercialPaper buy(CommercialPaperContext ctx, String issuer, String paperNumber, String currentOwner,
+ String newOwner, int price, String purchaseDateTime) {
+
+ // Retrieve the current paper using key fields provided
+ String paperKey = State.makeKey(new String[] { paperNumber });
+ CommercialPaper paper = ctx.paperList.getPaper(paperKey);
+
+ // Validate current owner
+ if (!paper.getOwner().equals(currentOwner)) {
+ throw new RuntimeException("Paper " + issuer + paperNumber + " is not owned by " + currentOwner);
+ }
+
+ // First buy moves state from ISSUED to TRADING
+ if (paper.isIssued()) {
+ paper.setTrading();
+ }
+
+ // Check paper is not already REDEEMED
+ if (paper.isTrading()) {
+ paper.setOwner(newOwner);
+ } else {
+ throw new RuntimeException(
+ "Paper " + issuer + paperNumber + " is not trading. Current state = " + paper.getState());
+ }
+
+ // Update the paper
+ ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ /**
+ * Redeem commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} redeemingOwner redeeming owner of paper
+ * @param {String} redeemDateTime time paper was redeemed
+ */
+ @Transaction
+ public CommercialPaper redeem(CommercialPaperContext ctx, String issuer, String paperNumber, String redeemingOwner,
+ String redeemDateTime) {
+
+ String paperKey = CommercialPaper.makeKey(new String[] { paperNumber });
+
+ CommercialPaper paper = ctx.paperList.getPaper(paperKey);
+
+ // Check paper is not REDEEMED
+ if (paper.isRedeemed()) {
+ throw new RuntimeException("Paper " + issuer + paperNumber + " already redeemed");
+ }
+
+ // Verify that the redeemer owns the commercial paper before redeeming it
+ if (paper.getOwner().equals(redeemingOwner)) {
+ paper.setOwner(paper.getIssuer());
+ paper.setRedeemed();
+ } else {
+ throw new RuntimeException("Redeeming owner does not own paper" + issuer + paperNumber);
+ }
+
+ ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+}
diff --git a/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/PaperList.java b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/PaperList.java
new file mode 100644
index 0000000..0ecd24c
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/PaperList.java
@@ -0,0 +1,31 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.example;
+
+import org.example.ledgerapi.StateList;
+import org.hyperledger.fabric.contract.Context;
+
+public class PaperList {
+
+ private StateList stateList;
+
+ public PaperList(Context ctx) {
+ this.stateList = StateList.getStateList(ctx, PaperList.class.getSimpleName(), CommercialPaper::deserialize);
+ }
+
+ public PaperList addPaper(CommercialPaper paper) {
+ stateList.addState(paper);
+ return this;
+ }
+
+ public CommercialPaper getPaper(String paperKey) {
+ return (CommercialPaper) this.stateList.getState(paperKey);
+ }
+
+ public PaperList updatePaper(CommercialPaper paper) {
+ this.stateList.updateState(paper);
+ return this;
+ }
+}
diff --git a/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/State.java b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/State.java
new file mode 100644
index 0000000..46d35c3
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/State.java
@@ -0,0 +1,60 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+package org.example.ledgerapi;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.json.JSONObject;
+
+/**
+ * State class. States have a class, unique key, and a lifecycle current state
+ * the current state is determined by the specific subclass
+ */
+public class State {
+
+ protected String key;
+
+ /**
+ * @param {String|Object} class An identifiable class of the instance
+ * @param {keyParts[]} elements to pull together to make a key for the objects
+ */
+ public State() {
+
+ }
+
+ String getKey() {
+ return this.key;
+ }
+
+ public String[] getSplitKey() {
+ return State.splitKey(this.key);
+ }
+
+ /**
+ * Convert object to buffer containing JSON data serialization Typically used
+ * before putState()ledger API
+ *
+ * @param {Object} JSON object to serialize
+ * @return {buffer} buffer with the data to store
+ */
+ public static byte[] serialize(Object object) {
+ String jsonStr = new JSONObject(object).toString();
+ return jsonStr.getBytes(UTF_8);
+ }
+
+ /**
+ * Join the keyParts to make a unififed string
+ *
+ * @param (String[]) keyParts
+ */
+ public static String makeKey(String[] keyParts) {
+ return String.join(":", keyParts);
+ }
+
+ public static String[] splitKey(String key) {
+ System.out.println("splitting key " + key + " " + java.util.Arrays.asList(key.split(":")));
+ return key.split(":");
+ }
+
+}
diff --git a/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/StateDeserializer.java b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/StateDeserializer.java
new file mode 100644
index 0000000..891788e
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/StateDeserializer.java
@@ -0,0 +1,6 @@
+package org.example.ledgerapi;
+
+@FunctionalInterface
+public interface StateDeserializer {
+ State deserialize(byte[] buffer);
+}
diff --git a/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/StateList.java b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/StateList.java
new file mode 100644
index 0000000..d672586
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/StateList.java
@@ -0,0 +1,48 @@
+package org.example.ledgerapi;
+
+import org.example.ledgerapi.impl.StateListImpl;
+import org.hyperledger.fabric.contract.Context;
+
+public interface StateList {
+
+ /*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+ /**
+ * StateList provides a named virtual container for a set of ledger states. Each
+ * state has a unique key which associates it with the container, rather than
+ * the container containing a link to the state. This minimizes collisions for
+ * parallel transactions on different states.
+ */
+
+ /**
+ * Store Fabric context for subsequent API access, and name of list
+ */
+ static StateList getStateList(Context ctx, String listName, StateDeserializer deserializer) {
+ return new StateListImpl(ctx, listName, deserializer);
+ }
+
+ /**
+ * Add a state to the list. Creates a new state in worldstate with appropriate
+ * composite key. Note that state defines its own key. State object is
+ * serialized before writing.
+ */
+ public StateList addState(State state);
+
+ /**
+ * Get a state from the list using supplied keys. Form composite keys to
+ * retrieve state from world state. State data is deserialized into JSON object
+ * before being returned.
+ */
+ public State getState(String key);
+
+ /**
+ * Update a state in the list. Puts the new state in world state with
+ * appropriate composite key. Note that state defines its own key. A state is
+ * serialized before writing. Logic is very similar to addState() but kept
+ * separate becuase it is semantically distinct.
+ */
+ public StateList updateState(State state);
+
+}
diff --git a/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/impl/StateListImpl.java b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/impl/StateListImpl.java
new file mode 100644
index 0000000..78a4293
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract-java/src/main/java/org/example/ledgerapi/impl/StateListImpl.java
@@ -0,0 +1,100 @@
+package org.example.ledgerapi.impl;
+
+import java.util.Arrays;
+
+import org.example.ledgerapi.State;
+import org.example.ledgerapi.StateDeserializer;
+import org.example.ledgerapi.StateList;
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ledger.CompositeKey;
+
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+/**
+ * StateList provides a named virtual container for a set of ledger states. Each
+ * state has a unique key which associates it with the container, rather than
+ * the container containing a link to the state. This minimizes collisions for
+ * parallel transactions on different states.
+ */
+public class StateListImpl implements StateList {
+
+ private Context ctx;
+ private String name;
+ private Object supportedClasses;
+ private StateDeserializer deserializer;
+
+ /**
+ * Store Fabric context for subsequent API access, and name of list
+ *
+ * @param deserializer
+ */
+ public StateListImpl(Context ctx, String listName, StateDeserializer deserializer) {
+ this.ctx = ctx;
+ this.name = listName;
+ this.deserializer = deserializer;
+
+ }
+
+ /**
+ * Add a state to the list. Creates a new state in worldstate with appropriate
+ * composite key. Note that state defines its own key. State object is
+ * serialized before writing.
+ */
+ @Override
+ public StateList addState(State state) {
+ System.out.println("Adding state " + this.name);
+ ChaincodeStub stub = this.ctx.getStub();
+ System.out.println("Stub=" + stub);
+ String[] splitKey = state.getSplitKey();
+ System.out.println("Split key " + Arrays.asList(splitKey));
+
+ CompositeKey ledgerKey = stub.createCompositeKey(this.name, splitKey);
+ System.out.println("ledgerkey is ");
+ System.out.println(ledgerKey);
+
+ byte[] data = State.serialize(state);
+ System.out.println("ctx" + this.ctx);
+ System.out.println("stub" + this.ctx.getStub());
+ this.ctx.getStub().putState(ledgerKey.toString(), data);
+
+ return this;
+ }
+
+ /**
+ * Get a state from the list using supplied keys. Form composite keys to
+ * retrieve state from world state. State data is deserialized into JSON object
+ * before being returned.
+ */
+ @Override
+ public State getState(String key) {
+
+ CompositeKey ledgerKey = this.ctx.getStub().createCompositeKey(this.name, State.splitKey(key));
+
+ byte[] data = this.ctx.getStub().getState(ledgerKey.toString());
+ if (data != null) {
+ State state = this.deserializer.deserialize(data);
+ return state;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Update a state in the list. Puts the new state in world state with
+ * appropriate composite key. Note that state defines its own key. A state is
+ * serialized before writing. Logic is very similar to addState() but kept
+ * separate becuase it is semantically distinct.
+ */
+ @Override
+ public StateList updateState(State state) {
+ CompositeKey ledgerKey = this.ctx.getStub().createCompositeKey(this.name, state.getSplitKey());
+ byte[] data = State.serialize(state);
+ this.ctx.getStub().putState(ledgerKey.toString(), data);
+
+ return this;
+ }
+
+}
diff --git a/commercial-paper/organization/digibank/contract/.editorconfig b/commercial-paper/organization/digibank/contract/.editorconfig
new file mode 100755
index 0000000..75a13be
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/.editorconfig
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/commercial-paper/organization/digibank/contract/.eslintignore b/commercial-paper/organization/digibank/contract/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/commercial-paper/organization/digibank/contract/.eslintrc.js b/commercial-paper/organization/digibank/contract/.eslintrc.js
new file mode 100644
index 0000000..6772c66
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 4],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-tabs': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/commercial-paper/organization/digibank/contract/.npmignore b/commercial-paper/organization/digibank/contract/.npmignore
new file mode 100644
index 0000000..a00ca94
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/.npmignore
@@ -0,0 +1,77 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
diff --git a/commercial-paper/organization/digibank/contract/index.js b/commercial-paper/organization/digibank/contract/index.js
new file mode 100644
index 0000000..e0ea2c7
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/index.js
@@ -0,0 +1,10 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+const cpcontract = require('./lib/papercontract.js');
+module.exports.contracts = [cpcontract];
diff --git a/commercial-paper/organization/digibank/contract/ledger-api/state.js b/commercial-paper/organization/digibank/contract/ledger-api/state.js
new file mode 100644
index 0000000..f63e792
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/ledger-api/state.js
@@ -0,0 +1,105 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+/**
+ * State class. States have a class, unique key, and a lifecycle current state
+ * the current state is determined by the specific subclass
+ */
+class State {
+
+ /**
+ * @param {String|Object} class An indentifiable class of the instance
+ * @param {keyParts[]} elements to pull together to make a key for the objects
+ */
+ constructor(stateClass, keyParts) {
+ this.class = stateClass;
+ this.key = State.makeKey(keyParts);
+ this.currentState = null;
+ }
+
+ getClass() {
+ return this.class;
+ }
+
+ getKey() {
+ return this.key;
+ }
+
+ getSplitKey(){
+ return State.splitKey(this.key);
+ }
+
+ getCurrentState(){
+ return this.currentState;
+ }
+
+ // not used
+/* serialize() {
+
+ return State.serialize(this);
+ } */
+
+ /**
+ * Convert object to buffer containing JSON data serialization
+ * Typically used before putState()ledger API
+ * @param {Object} JSON object to serialize
+ * @return {buffer} buffer with the data to store
+ */
+ static serialize(object) {
+ // don't write the key:value passed in - we already have a real composite key, issuer and paper Number.
+ delete object.key;
+ return Buffer.from(JSON.stringify(object));
+ }
+
+ /**
+ * Deserialize object into one of a set of supported JSON classes
+ * i.e. Covert serialized data to JSON object
+ * Typically used after getState() ledger API
+ * @param {data} data to deserialize into JSON object
+ * @param (supportedClasses) the set of classes data can be serialized to
+ * @return {json} json with the data to store
+ */
+ static deserialize(data, supportedClasses) {
+ let json = JSON.parse(data.toString());
+ let objClass = supportedClasses[json.class];
+ if (!objClass) {
+ throw new Error(`Unknown class of ${json.class}`);
+ }
+ let object = new (objClass)(json);
+
+ return object;
+ }
+
+ /**
+ * Deserialize object into specific object class
+ * Typically used after getState() ledger API
+ * @param {data} data to deserialize into JSON object
+ * @return {json} json with the data to store
+ */
+ static deserializeClass(data, objClass) {
+ let json = JSON.parse(data.toString());
+ let object = new (objClass)(json);
+ return object;
+ }
+
+ /**
+ * Join the keyParts to make a unififed string
+ * @param (String[]) keyParts
+ */
+ static makeKey(keyParts) {
+ // return keyParts.map(part => JSON.stringify(part)).join(':');
+ return keyParts.map(part => part).join(':');
+ }
+
+ static splitKey(key){
+ return key.split(':');
+ }
+
+}
+
+module.exports = State;
diff --git a/commercial-paper/organization/digibank/contract/ledger-api/statelist.js b/commercial-paper/organization/digibank/contract/ledger-api/statelist.js
new file mode 100644
index 0000000..15ec837
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/ledger-api/statelist.js
@@ -0,0 +1,74 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+const State = require('./state.js');
+
+/**
+ * StateList provides a named virtual container for a set of ledger states.
+ * Each state has a unique key which associates it with the container, rather
+ * than the container containing a link to the state. This minimizes collisions
+ * for parallel transactions on different states.
+ */
+class StateList {
+
+ /**
+ * Store Fabric context for subsequent API access, and name of list
+ */
+ constructor(ctx, listName) {
+ this.ctx = ctx;
+ this.name = listName;
+ this.supportedClasses = {};
+
+ }
+
+ /**
+ * Add a state to the list. Creates a new state in worldstate with
+ * appropriate composite key. Note that state defines its own key.
+ * State object is serialized before writing.
+ */
+ async addState(state) {
+ let key = this.ctx.stub.createCompositeKey(this.name, state.getSplitKey());
+ let data = State.serialize(state);
+ await this.ctx.stub.putState(key, data);
+ }
+
+ /**
+ * Get a state from the list using supplied keys. Form composite
+ * keys to retrieve state from world state. State data is deserialized
+ * into JSON object before being returned.
+ */
+ async getState(key) {
+ let ledgerKey = this.ctx.stub.createCompositeKey(this.name, State.splitKey(key));
+ let data = await this.ctx.stub.getState(ledgerKey);
+ if (data && data.toString('utf8')) {
+ let state = State.deserialize(data, this.supportedClasses);
+ return state;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Update a state in the list. Puts the new state in world state with
+ * appropriate composite key. Note that state defines its own key.
+ * A state is serialized before writing. Logic is very similar to
+ * addState() but kept separate becuase it is semantically distinct.
+ */
+ async updateState(state) {
+ let key = this.ctx.stub.createCompositeKey(this.name, state.getSplitKey());
+ let data = State.serialize(state);
+ await this.ctx.stub.putState(key, data);
+ }
+
+ /** Stores the class for future deserialization */
+ use(stateClass) {
+ this.supportedClasses[stateClass.getClass()] = stateClass;
+ }
+
+}
+
+module.exports = StateList;
diff --git a/commercial-paper/organization/digibank/contract/lib/paper.js b/commercial-paper/organization/digibank/contract/lib/paper.js
new file mode 100644
index 0000000..0629ba7
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/lib/paper.js
@@ -0,0 +1,121 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+// Utility class for ledger state
+const State = require('./../ledger-api/state.js');
+
+// Enumerate commercial paper state values
+const cpState = {
+ ISSUED: 1,
+ PENDING: 2,
+ TRADING: 3,
+ REDEEMED: 4
+};
+
+/**
+ * CommercialPaper class extends State class
+ * Class will be used by application and smart contract to define a paper
+ */
+class CommercialPaper extends State {
+
+ constructor(obj) {
+ super(CommercialPaper.getClass(), [obj.issuer, obj.paperNumber]);
+ Object.assign(this, obj);
+ }
+
+ /**
+ * Basic getters and setters
+ */
+ getIssuer() {
+ return this.issuer;
+ }
+
+ setIssuer(newIssuer) {
+ this.issuer = newIssuer;
+ }
+
+ getOwner() {
+ return this.owner;
+ }
+
+ setOwnerMSP(mspid) {
+ this.mspid = mspid;
+ }
+
+ getOwnerMSP() {
+ return this.mspid;
+ }
+
+ setOwner(newOwner) {
+ this.owner = newOwner;
+ }
+
+ /**
+ * Useful methods to encapsulate commercial paper states
+ */
+ setIssued() {
+ this.currentState = cpState.ISSUED;
+ }
+
+ setTrading() {
+ this.currentState = cpState.TRADING;
+ }
+
+ setRedeemed() {
+ this.currentState = cpState.REDEEMED;
+ }
+
+ setPending() {
+ this.currentState = cpState.PENDING;
+ }
+
+ isIssued() {
+ return this.currentState === cpState.ISSUED;
+ }
+
+ isTrading() {
+ return this.currentState === cpState.TRADING;
+ }
+
+ isRedeemed() {
+ return this.currentState === cpState.REDEEMED;
+ }
+
+ isPending() {
+ return this.currentState === cpState.PENDING;
+ }
+
+ static fromBuffer(buffer) {
+ return CommercialPaper.deserialize(buffer);
+ }
+
+ toBuffer() {
+ return Buffer.from(JSON.stringify(this));
+ }
+
+ /**
+ * Deserialize a state data to commercial paper
+ * @param {Buffer} data to form back into the object
+ */
+ static deserialize(data) {
+ return State.deserializeClass(data, CommercialPaper);
+ }
+
+ /**
+ * Factory method to create a commercial paper object
+ */
+ static createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
+ return new CommercialPaper({ issuer, paperNumber, issueDateTime, maturityDateTime, faceValue });
+ }
+
+ static getClass() {
+ return 'org.papernet.commercialpaper';
+ }
+}
+
+module.exports = CommercialPaper;
diff --git a/commercial-paper/organization/digibank/contract/lib/papercontract.js b/commercial-paper/organization/digibank/contract/lib/papercontract.js
new file mode 100644
index 0000000..c3d2cdb
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/lib/papercontract.js
@@ -0,0 +1,339 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+// Fabric smart contract classes
+const { Contract, Context } = require('fabric-contract-api');
+
+// PaperNet specifc classes
+const CommercialPaper = require('./paper.js');
+const PaperList = require('./paperlist.js');
+const QueryUtils = require('./queries.js');
+
+/**
+ * A custom context provides easy access to list of all commercial papers
+ */
+class CommercialPaperContext extends Context {
+
+ constructor() {
+ super();
+ // All papers are held in a list of papers
+ this.paperList = new PaperList(this);
+ }
+
+}
+
+/**
+ * Define commercial paper smart contract by extending Fabric Contract class
+ *
+ */
+class CommercialPaperContract extends Contract {
+
+ constructor() {
+ // Unique namespace when multiple contracts per chaincode file
+ super('org.papernet.commercialpaper');
+ }
+
+ /**
+ * Define a custom context for commercial paper
+ */
+ createContext() {
+ return new CommercialPaperContext();
+ }
+
+ /**
+ * Instantiate to perform any setup of the ledger that might be required.
+ * @param {Context} ctx the transaction context
+ */
+ async instantiate(ctx) {
+ // No implementation required with this example
+ // It could be where data migration is performed, if necessary
+ console.log('Instantiate the contract');
+ }
+
+ /**
+ * Issue commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} issueDateTime paper issue date
+ * @param {String} maturityDateTime paper maturity date
+ * @param {Integer} faceValue face value of paper
+ */
+ async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
+
+ // create an instance of the paper
+ let paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, parseInt(faceValue));
+
+ // Smart contract, rather than paper, moves paper into ISSUED state
+ paper.setIssued();
+
+ // save the owner's MSP
+ let mspid = ctx.clientIdentity.getMSPID();
+ paper.setOwnerMSP(mspid);
+
+ // Newly issued paper is owned by the issuer to begin with (recorded for reporting purposes)
+ paper.setOwner(issuer);
+
+ // Add the paper to the list of all similar commercial papers in the ledger world state
+ await ctx.paperList.addPaper(paper);
+
+ // Must return a serialized paper to caller of smart contract
+ return paper;
+ }
+
+ /**
+ * Buy commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} currentOwner current owner of paper
+ * @param {String} newOwner new owner of paper
+ * @param {Integer} price price paid for this paper // transaction input - not written to asset
+ * @param {String} purchaseDateTime time paper was purchased (i.e. traded) // transaction input - not written to asset
+ */
+ async buy(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) {
+
+ // Retrieve the current paper using key fields provided
+ let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
+ let paper = await ctx.paperList.getPaper(paperKey);
+
+ // Validate current owner
+ if (paper.getOwner() !== currentOwner) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not owned by ' + currentOwner);
+ }
+
+ // First buy moves state from ISSUED to TRADING (when running )
+ if (paper.isIssued()) {
+ paper.setTrading();
+ }
+
+ // Check paper is not already REDEEMED
+ if (paper.isTrading()) {
+ paper.setOwner(newOwner);
+ // save the owner's MSP
+ let mspid = ctx.clientIdentity.getMSPID();
+ paper.setOwnerMSP(mspid);
+ } else {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not trading. Current state = ' + paper.getCurrentState());
+ }
+
+ // Update the paper
+ await ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ /**
+ * Buy request: (2-phase confirmation: Commercial paper is 'PENDING' subject to completion of transfer by owning org)
+ * Alternative to 'buy' transaction
+ * Note: 'buy_request' puts paper in 'PENDING' state - subject to transfer confirmation [below].
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} currentOwner current owner of paper
+ * @param {String} newOwner new owner of paper // transaction input - not written to asset per se - but written to block
+ * @param {Integer} price price paid for this paper // transaction input - not written to asset per se - but written to block
+ * @param {String} purchaseDateTime time paper was requested // transaction input - ditto.
+ */
+ async buy_request(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) {
+
+
+ // Retrieve the current paper using key fields provided
+ let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
+ let paper = await ctx.paperList.getPaper(paperKey);
+
+ // Validate current owner - this is really information for the user trying the sample, rather than any 'authorisation' check per se FYI
+ if (paper.getOwner() !== currentOwner) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not owned by ' + currentOwner + ' provided as a paraneter');
+ }
+ // paper set to 'PENDING' - can only be transferred (confirmed) by identity from owning org (MSP check).
+ paper.setPending();
+
+ // Update the paper
+ await ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ /**
+ * transfer commercial paper: only the owning org has authority to execute. It is the complement to the 'buy_request' transaction. '[]' is optional below.
+ * eg. issue -> buy_request -> transfer -> [buy ...n | [buy_request...n | transfer ...n] ] -> redeem
+ * this transaction 'pair' is an alternative to the straight issue -> buy -> [buy....n] -> redeem ...path
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} newOwner new owner of paper
+ * @param {String} newOwnerMSP MSP id of the transferee
+ * @param {String} confirmDateTime confirmed transfer date.
+ */
+ async transfer(ctx, issuer, paperNumber, newOwner, newOwnerMSP, confirmDateTime) {
+
+ // Retrieve the current paper using key fields provided
+ let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
+ let paper = await ctx.paperList.getPaper(paperKey);
+
+ // Validate current owner's MSP in the paper === invoking transferor's MSP id - can only transfer if you are the owning org.
+
+ if (paper.getOwnerMSP() !== ctx.clientIdentity.getMSPID()) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not owned by the current invoking Organisation, and not authorised to transfer');
+ }
+
+ // Paper needs to be 'pending' - which means you need to have run 'buy_pending' transaction first.
+ if ( ! paper.isPending()) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not currently in state: PENDING for transfer to occur: \n must run buy_request transaction first');
+ }
+ // else all good
+
+ paper.setOwner(newOwner);
+ // set the MSP of the transferee (so that, that org may also pass MSP check, if subsequently transferred/sold on)
+ paper.setOwnerMSP(newOwnerMSP);
+ paper.setTrading();
+ paper.confirmDateTime = confirmDateTime;
+
+ // Update the paper
+ await ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ /**
+ * Redeem commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} redeemingOwner redeeming owner of paper
+ * @param {String} issuingOwnerMSP the MSP of the org that the paper will be redeemed with.
+ * @param {String} redeemDateTime time paper was redeemed
+ */
+ async redeem(ctx, issuer, paperNumber, redeemingOwner, issuingOwnerMSP, redeemDateTime) {
+
+ let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
+
+ let paper = await ctx.paperList.getPaper(paperKey);
+
+ // Check paper is not alread in a state of REDEEMED
+ if (paper.isRedeemed()) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' has already been redeemed');
+ }
+
+ // Validate current redeemer's MSP matches the invoking redeemer's MSP id - can only redeem if you are the owning org.
+
+ if (paper.getOwnerMSP() !== ctx.clientIdentity.getMSPID()) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' cannot be redeemed by ' + ctx.clientIdentity.getMSPID() + ', as it is not the authorised owning Organisation');
+ }
+
+ // As this is just a sample, can show additional verification check: that the redeemer provided matches that on record, before redeeming it
+ if (paper.getOwner() === redeemingOwner) {
+ paper.setOwner(paper.getIssuer());
+ paper.setOwnerMSP(issuingOwnerMSP);
+ paper.setRedeemed();
+ paper.redeemDateTime = redeemDateTime; // record redemption date against the asset (the complement to 'issue date')
+ } else {
+ throw new Error('\nRedeeming owner: ' + redeemingOwner + ' organisation does not currently own paper: ' + issuer + paperNumber);
+ }
+
+ await ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ // Query transactions
+
+ /**
+ * Query history of a commercial paper
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ */
+ async queryHistory(ctx, issuer, paperNumber) {
+
+ // Get a key to be used for History query
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let results = await query.getAssetHistory(issuer, paperNumber); // (cpKey);
+ return results;
+
+ }
+
+ /**
+ * queryOwner commercial paper: supply name of owning org, to find list of papers based on owner field
+ * @param {Context} ctx the transaction context
+ * @param {String} owner commercial paper owner
+ */
+ async queryOwner(ctx, owner) {
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let owner_results = await query.queryKeyByOwner(owner);
+
+ return owner_results;
+ }
+
+ /**
+ * queryPartial commercial paper - provide a prefix eg. "DigiBank" will list all papers _issued_ by DigiBank etc etc
+ * @param {Context} ctx the transaction context
+ * @param {String} prefix asset class prefix (added to paperlist namespace) eg. org.papernet.paperMagnetoCorp asset listing: papers issued by MagnetoCorp.
+ */
+ async queryPartial(ctx, prefix) {
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let partial_results = await query.queryKeyByPartial(prefix);
+
+ return partial_results;
+ }
+
+ /**
+ * queryAdHoc commercial paper - supply a custom mango query
+ * eg - as supplied as a param:
+ * ex1: ["{\"selector\":{\"faceValue\":{\"$lt\":8000000}}}"]
+ * ex2: ["{\"selector\":{\"faceValue\":{\"$gt\":4999999}}}"]
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} queryString querystring
+ */
+ async queryAdhoc(ctx, queryString) {
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let querySelector = JSON.parse(queryString);
+ let adhoc_results = await query.queryByAdhoc(querySelector);
+
+ return adhoc_results;
+ }
+
+
+ /**
+ * queryNamed - supply named query - 'case' statement chooses selector to build (pre-canned for demo purposes)
+ * @param {Context} ctx the transaction context
+ * @param {String} queryname the 'named' query (built here) - or - the adHoc query string, provided as a parameter
+ */
+ async queryNamed(ctx, queryname) {
+ let querySelector = {};
+ switch (queryname) {
+ case "redeemed":
+ querySelector = { "selector": { "currentState": 4 } }; // 4 = redeemd state
+ break;
+ case "trading":
+ querySelector = { "selector": { "currentState": 3 } }; // 3 = trading state
+ break;
+ case "value":
+ // may change to provide as a param - fixed value for now in this sample
+ querySelector = { "selector": { "faceValue": { "$gt": 4000000 } } }; // to test, issue CommPapers with faceValue <= or => this figure.
+ break;
+ default: // else, unknown named query
+ throw new Error('invalid named query supplied: ' + queryname + '- please try again ');
+ }
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let adhoc_results = await query.queryByAdhoc(querySelector);
+
+ return adhoc_results;
+ }
+
+}
+
+module.exports = CommercialPaperContract;
diff --git a/commercial-paper/organization/digibank/contract/lib/paperlist.js b/commercial-paper/organization/digibank/contract/lib/paperlist.js
new file mode 100644
index 0000000..73528b6
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/lib/paperlist.js
@@ -0,0 +1,35 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+// Utility class for collections of ledger states -- a state list
+const StateList = require('./../ledger-api/statelist.js');
+
+const CommercialPaper = require('./paper.js');
+
+class PaperList extends StateList {
+
+ constructor(ctx) {
+ super(ctx, 'org.papernet.paper');
+ this.use(CommercialPaper);
+ }
+
+ async addPaper(paper) {
+ return this.addState(paper);
+ }
+
+ async getPaper(paperKey) {
+ return this.getState(paperKey);
+ }
+
+ async updatePaper(paper) {
+ return this.updateState(paper);
+ }
+}
+
+
+module.exports = PaperList;
diff --git a/commercial-paper/organization/digibank/contract/lib/queries.js b/commercial-paper/organization/digibank/contract/lib/queries.js
new file mode 100644
index 0000000..a7cd40a
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/lib/queries.js
@@ -0,0 +1,215 @@
+
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+'use strict';
+
+const State = require('../ledger-api/state.js');
+//const CommercialPaper = require('./paper.js');
+/**
+ * Query Class for query functions such as history etc
+ *
+ */
+class QueryUtils {
+
+ constructor(ctx, listName) {
+ this.ctx = ctx;
+ this.name = listName;
+ //this.supportedTypes = {};
+ }
+
+ // =========================================================================================
+ // getAssetHistory takes the composite key as arg, gets returns results as JSON to 'main contract'
+ // =========================================================================================
+ /**
+ * Get Asset History for a commercial paper
+ * @param {String} issuer the CP issuer
+ * @param {String} paperNumber commercial paper number
+ */
+ async getAssetHistory(issuer, paperNumber) {
+
+ let ledgerKey = await this.ctx.stub.createCompositeKey(this.name, [issuer, paperNumber]);
+ const resultsIterator = await this.ctx.stub.getHistoryForKey(ledgerKey);
+ let results = await this.getAllResults(resultsIterator, true);
+
+ return results;
+ }
+
+ // ===========================================================================================
+ // queryKeyByPartial performs a partial query based on the namespace and asset key prefix provided
+
+ // Read-only function results are not typically submitted to ordering. If the read-only
+ // results are submitted to ordering, or if the query is used in an update transaction
+ // and submitted to ordering, then the committing peers will re-execute to guarantee that
+ // result sets are stable between endorsement time and commit time. The transaction is
+ // invalidated by the committing peers if the result set has changed between endorsement
+ // time and commit time.
+ //
+ // ===========================================================================================
+ /**
+ * queryOwner commercial paper
+ * @param {String} assetspace the asset space (eg MagnetoCorp's assets)
+ */
+ async queryKeyByPartial(assetspace) {
+
+ if (arguments.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting 1');
+ }
+ // ie namespace + prefix to assets etc eg
+ // "Key":"org.papernet.paperMagnetoCorp0001" (0002, etc)
+ // "Partial":'org.papernet.paperlistMagnetoCorp"' (using partial key, find keys "0001", "0002" etc)
+ const resultsIterator = await this.ctx.stub.getStateByPartialCompositeKey(this.name, [assetspace]);
+ let method = this.getAllResults;
+ let results = await method(resultsIterator, false);
+
+ return results;
+ }
+
+
+ // ===== Example: Parameterized rich query =================================================
+ // queryKeyByOwner queries for assets based on a passed in owner.
+ // This is an example of a parameterized query accepting a single query parameter (owner).
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ // =========================================================================================
+ /**
+ * queryKeyByOwner commercial paper
+ * @param {String} owner commercial paper owner
+ */
+ async queryKeyByOwner(owner) {
+ //
+ let self = this;
+ if (arguments.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting owner name.');
+ }
+ let queryString = {};
+ queryString.selector = {};
+ // queryString.selector.docType = 'indexOwnerDoc';
+ queryString.selector.owner = owner;
+ // set to (eg) '{selector:{owner:MagnetoCorp}}'
+ let method = self.getQueryResultForQueryString;
+ let queryResults = await method(this.ctx, self, JSON.stringify(queryString));
+ return queryResults;
+ }
+
+ // ===== Example: Ad hoc rich query ========================================================
+ // queryAdhoc uses a query string to perform a query for marbles..
+ // Query string matching state database syntax is passed in and executed as is.
+ // Supports ad hoc queries that can be defined at runtime by the client.
+ // If this is not desired, follow the queryKeyByOwner example for parameterized queries.
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ // example passed using VS Code ext: ["{\"selector\": {\"owner\": \"MagnetoCorp\"}}"]
+ // =========================================================================================
+ /**
+ * query By AdHoc string (commercial paper)
+ * @param {String} queryString actual MangoDB query string (escaped)
+ */
+ async queryByAdhoc(queryString) {
+
+ if (arguments.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting ad-hoc string, which gets stringified for mango query');
+ }
+ let self = this;
+
+ if (!queryString) {
+ throw new Error('queryString must not be empty');
+ }
+ let method = self.getQueryResultForQueryString;
+ let queryResults = await method(this.ctx, self, JSON.stringify(queryString));
+ return queryResults;
+ }
+
+ // WORKER functions are below this line: these are called by the above functions, where iterator is passed in
+
+ // =========================================================================================
+ // getQueryResultForQueryString woerk function executes the passed-in query string.
+ // Result set is built and returned as a byte array containing the JSON results.
+ // =========================================================================================
+ /**
+ * Function getQueryResultForQueryString
+ * @param {Context} ctx the transaction context
+ * @param {any} self within scope passed in
+ * @param {String} the query string created prior to calling this fn
+ */
+ async getQueryResultForQueryString(ctx, self, queryString) {
+
+ // console.log('- getQueryResultForQueryString queryString:\n' + queryString);
+
+ const resultsIterator = await ctx.stub.getQueryResult(queryString);
+ let results = await self.getAllResults(resultsIterator, false);
+
+ return results;
+
+ }
+
+ /**
+ * Function getAllResults
+ * @param {resultsIterator} iterator within scope passed in
+ * @param {Boolean} isHistory query string created prior to calling this fn
+ */
+ async getAllResults(iterator, isHistory) {
+ let allResults = [];
+ let res = { done: false, value: null };
+
+ while (true) {
+ res = await iterator.next();
+ let jsonRes = {};
+ if (res.value && res.value.value.toString()) {
+ if (isHistory && isHistory === true) {
+ //jsonRes.TxId = res.value.tx_id;
+ jsonRes.TxId = res.value.txId;
+ jsonRes.Timestamp = res.value.timestamp;
+ jsonRes.Timestamp = new Date((res.value.timestamp.seconds.low * 1000));
+ let ms = res.value.timestamp.nanos / 1000000;
+ jsonRes.Timestamp.setMilliseconds(ms);
+ if (res.value.is_delete) {
+ jsonRes.IsDelete = res.value.is_delete.toString();
+ } else {
+ try {
+ jsonRes.Value = JSON.parse(res.value.value.toString('utf8'));
+ // report the commercial paper states during the asset lifecycle, just for asset history reporting
+ switch (jsonRes.Value.currentState) {
+ case 1:
+ jsonRes.Value.currentState = 'ISSUED';
+ break;
+ case 2:
+ jsonRes.Value.currentState = 'PENDING';
+ break;
+ case 3:
+ jsonRes.Value.currentState = 'TRADING';
+ break;
+ case 4:
+ jsonRes.Value.currentState = 'REDEEMED';
+ break;
+ default: // else, unknown named query
+ jsonRes.Value.currentState = 'UNKNOWN';
+ }
+
+ } catch (err) {
+ console.log(err);
+ jsonRes.Value = res.value.value.toString('utf8');
+ }
+ }
+ } else { // non history query ..
+ jsonRes.Key = res.value.key;
+ try {
+ jsonRes.Record = JSON.parse(res.value.value.toString('utf8'));
+ } catch (err) {
+ console.log(err);
+ jsonRes.Record = res.value.value.toString('utf8');
+ }
+ }
+ allResults.push(jsonRes);
+ }
+ // check to see if we have reached the end
+ if (res.done) {
+ // explicitly close the iterator
+ console.log('iterator is done');
+ await iterator.close();
+ return allResults;
+ }
+
+ } // while true
+ }
+
+}
+module.exports = QueryUtils;
\ No newline at end of file
diff --git a/commercial-paper/organization/digibank/contract/package.json b/commercial-paper/organization/digibank/contract/package.json
new file mode 100644
index 0000000..2c4a644
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "papernet-js",
+ "version": "0.0.1",
+ "description": "Papernet Contract",
+ "main": "index.js",
+ "engines": {
+ "node": ">=8",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "test": "nyc mocha test --recursive",
+ "start": "fabric-chaincode-node start",
+ "mocha": "mocha test --recursive"
+ },
+ "engineStrict": true,
+ "author": "hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ },
+ "devDependencies": {
+ "chai": "^4.1.2",
+ "chai-as-promised": "^7.1.1",
+ "eslint": "^4.19.1",
+ "mocha": "^5.2.0",
+ "nyc": "^12.0.2",
+ "sinon": "^6.0.0",
+ "sinon-chai": "^3.2.0"
+ },
+ "nyc": {
+ "exclude": [
+ "coverage/**",
+ "test/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/commercial-paper/organization/digibank/contract/test/contract.js b/commercial-paper/organization/digibank/contract/test/contract.js
new file mode 100644
index 0000000..63576ee
--- /dev/null
+++ b/commercial-paper/organization/digibank/contract/test/contract.js
@@ -0,0 +1,43 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+'use strict';
+
+const Chaincode = require('../lib/chaincode');
+const { Stub } = require('fabric-shim');
+
+require('chai').should();
+const sinon = require('sinon');
+
+describe('Chaincode', () => {
+
+ describe('#Init', () => {
+
+ it('should work', async () => {
+ const cc = new Chaincode();
+ const stub = sinon.createStubInstance(Stub);
+ stub.getFunctionAndParameters.returns({ fcn: 'initFunc', params: [] });
+ const res = await cc.Init(stub);
+ res.status.should.equal(Stub.RESPONSE_CODE.OK);
+ });
+
+ });
+
+ describe('#Invoke', async () => {
+
+ it('should work', async () => {
+ const cc = new Chaincode();
+ const stub = sinon.createStubInstance(Stub);
+ stub.getFunctionAndParameters.returns({ fcn: 'initFunc', params: [] });
+ let res = await cc.Init(stub);
+ res.status.should.equal(Stub.RESPONSE_CODE.OK);
+ stub.getFunctionAndParameters.returns({ fcn: 'invokeFunc', params: [] });
+ res = await cc.Invoke(stub);
+ res.status.should.equal(Stub.RESPONSE_CODE.OK);
+ });
+
+ });
+
+});
diff --git a/commercial-paper/organization/digibank/digibank.sh b/commercial-paper/organization/digibank/digibank.sh
new file mode 100755
index 0000000..414d4d2
--- /dev/null
+++ b/commercial-paper/organization/digibank/digibank.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+shopt -s extglob
+
+function _exit(){
+ printf "Exiting:%s\n" "$1"
+ exit -1
+}
+
+: ${CHANNEL_NAME:="mychannel"}
+: ${DELAY:="3"}
+: ${MAX_RETRY:="5"}
+: ${VERBOSE:="false"}
+
+# Where am I?
+DIR=${PWD}
+
+# Locate the test network
+cd "${DIR}/../../../test-network"
+env | sort > /tmp/env.orig
+
+OVERRIDE_ORG="1"
+. ./scripts/envVar.sh
+
+parsePeerConnectionParameters 1 2
+
+export PEER_PARMS="${PEER_CONN_PARMS##*( )}"
+
+# set the fabric config path
+export FABRIC_CFG_PATH="${DIR}/../../../config"
+export PATH="${DIR}/../../../bin:${PWD}:$PATH"
+
+env | sort | comm -1 -3 /tmp/env.orig - | sed -E 's/(.*)=(.*)/export \1="\2"/'
+
+rm /tmp/env.orig
+
+cd ${DIR}
diff --git a/commercial-paper/organization/digibank/gateway/.gitkeep b/commercial-paper/organization/digibank/gateway/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/commercial-paper/organization/magnetocorp/.gitignore b/commercial-paper/organization/magnetocorp/.gitignore
new file mode 100644
index 0000000..7d13901
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/.gitignore
@@ -0,0 +1 @@
+identity
\ No newline at end of file
diff --git a/commercial-paper/organization/magnetocorp/application-java/.gitignore b/commercial-paper/organization/magnetocorp/application-java/.gitignore
new file mode 100644
index 0000000..2f7896d
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.core.resources.prefs b/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..7a53139
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding/=UTF-8
+encoding/src=UTF-8
diff --git a/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.jdt.core.prefs b/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..b8947ec
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.m2e.core.prefs b/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/commercial-paper/organization/magnetocorp/application-java/pom.xml b/commercial-paper/organization/magnetocorp/application-java/pom.xml
new file mode 100644
index 0000000..f80dc68
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/pom.xml
@@ -0,0 +1,96 @@
+
+ 4.0.0
+ commercial-paper
+ commercial-paper
+ 0.0.1-SNAPSHOT
+
+
+
+
+ 1.8
+ UTF-8
+ UTF-8
+
+
+ [2.0,3.0)
+
+ 2.1.0
+
+
+
+
+ src
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.0
+
+
+
+ package
+
+ shade
+
+
+
+
+ false
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+
+
+
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
+
+
+ org.hyperledger.fabric
+ fabric-gateway-java
+ ${fabric-gateway-java.version}
+
+
+
+
+ org.hyperledger.fabric-chaincode-java
+ fabric-chaincode-shim
+ ${fabric-chaincode-java.version}
+ compile
+
+
+
+
+ org.json
+ json
+ 20180813
+
+
+
+
diff --git a/commercial-paper/organization/magnetocorp/application-java/src/org/magnetocorp/AddToWallet.java b/commercial-paper/organization/magnetocorp/application-java/src/org/magnetocorp/AddToWallet.java
new file mode 100644
index 0000000..36d0c1f
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/src/org/magnetocorp/AddToWallet.java
@@ -0,0 +1,68 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.magnetocorp;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import org.hyperledger.fabric.gateway.Identities;
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.hyperledger.fabric.gateway.Identity;
+
+public class AddToWallet {
+
+ private static X509Certificate readX509Certificate(final Path certificatePath) throws IOException, CertificateException {
+ try (Reader certificateReader = Files.newBufferedReader(certificatePath, StandardCharsets.UTF_8)) {
+ return Identities.readX509Certificate(certificateReader);
+ }
+ }
+
+ private static PrivateKey getPrivateKey(final Path privateKeyPath) throws IOException, InvalidKeyException {
+ try (Reader privateKeyReader = Files.newBufferedReader(privateKeyPath, StandardCharsets.UTF_8)) {
+ return Identities.readPrivateKey(privateKeyReader);
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ // A wallet stores a collection of identities
+ Path walletPath = Paths.get(".", "wallet");
+ Wallet wallet = Wallets.newFileSystemWallet(walletPath);
+
+ Path credentialPath = Paths.get("..", "..", "..",".." ,"test-network", "organizations",
+ "peerOrganizations", "org2.example.com", "users", "User1@org2.example.com", "msp");
+ System.out.println("credentialPath: " + credentialPath.toString());
+ Path certificatePath = credentialPath.resolve(Paths.get("signcerts",
+ "User1@org2.example.com-cert.pem"));
+ System.out.println("certificatePem: " + certificatePath.toString());
+ Path privateKeyPath = credentialPath.resolve(Paths.get("keystore",
+ "priv_sk"));
+
+ X509Certificate certificate = readX509Certificate(certificatePath);
+ PrivateKey privateKey = getPrivateKey(privateKeyPath);
+
+ Identity identity = Identities.newX509Identity("Org2MSP", certificate, privateKey);
+
+
+ String identityLabel = "User1@org2.example.com";
+ wallet.put(identityLabel, identity);
+
+ System.out.println("Write wallet info into " + walletPath.toString() + " successfully.");
+
+ } catch (IOException | CertificateException | InvalidKeyException e) {
+ System.err.println("Error adding to wallet");
+ e.printStackTrace();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/commercial-paper/organization/magnetocorp/application-java/src/org/magnetocorp/Issue.java b/commercial-paper/organization/magnetocorp/application-java/src/org/magnetocorp/Issue.java
new file mode 100644
index 0000000..cdc268e
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/src/org/magnetocorp/Issue.java
@@ -0,0 +1,75 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.magnetocorp;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import org.hyperledger.fabric.gateway.Contract;
+import org.hyperledger.fabric.gateway.Gateway;
+import org.hyperledger.fabric.gateway.GatewayException;
+import org.hyperledger.fabric.gateway.Network;
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.papernet.CommercialPaper;
+
+public class Issue {
+
+ private static final String ENVKEY="CONTRACT_NAME";
+
+ public static void main(String[] args) {
+
+ String contractName="papercontract";
+ // get the name of the contract, in case it is overridden
+ Map envvar = System.getenv();
+ if (envvar.containsKey(ENVKEY)){
+ contractName=envvar.get(ENVKEY);
+ }
+
+ Gateway.Builder builder = Gateway.createBuilder();
+
+ try {
+ // A wallet stores a collection of identities
+ Path walletPath = Paths.get(".", "wallet");
+ Wallet wallet = Wallets.newFileSystemWallet(walletPath);
+ System.out.println("Read wallet info from: " + walletPath.toString());
+
+ String userName = "User1@org2.example.com";
+
+ Path connectionProfile = Paths.get("..", "gateway", "connection-org2.yaml");
+
+ // Set connection options on the gateway builder
+ builder.identity(wallet, userName).networkConfig(connectionProfile).discovery(false);
+
+ // Connect to gateway using application specified parameters
+ try(Gateway gateway = builder.connect()) {
+
+ // Access PaperNet network
+ System.out.println("Use network channel: mychannel.");
+ Network network = gateway.getNetwork("mychannel");
+
+ // Get addressability to commercial paper contract
+ System.out.println("Use org.papernet.commercialpaper smart contract.");
+ Contract contract = network.getContract(contractName, "org.papernet.commercialpaper");
+
+ // Issue commercial paper
+ System.out.println("Submit commercial paper issue transaction.");
+ byte[] response = contract.submitTransaction("issue", "MagnetoCorp", "00001", "2020-05-31", "2020-11-30", "5000000");
+
+ // Process response
+ System.out.println("Process issue transaction response.");
+ CommercialPaper paper = CommercialPaper.deserialize(response);
+ System.out.println(paper);
+ }
+ } catch (GatewayException | IOException | TimeoutException | InterruptedException e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/commercial-paper/organization/magnetocorp/application-java/src/org/papernet/CommercialPaper.java b/commercial-paper/organization/magnetocorp/application-java/src/org/papernet/CommercialPaper.java
new file mode 100644
index 0000000..e909b49
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/src/org/papernet/CommercialPaper.java
@@ -0,0 +1,183 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.papernet;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+import org.json.JSONObject;
+import org.json.JSONPropertyIgnore;
+import org.papernet.ledgerapi.State;
+
+@DataType()
+public class CommercialPaper extends State {
+
+ // Enumerate commercial paper state values
+ public final static String ISSUED = "ISSUED";
+ public final static String TRADING = "TRADING";
+ public final static String REDEEMED = "REDEEMED";
+
+ @Property()
+ private String state="";
+
+ public String getState() {
+ return state;
+ }
+
+ public CommercialPaper setState(String state) {
+ this.state = state;
+ return this;
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isIssued() {
+ return this.state.equals(CommercialPaper.ISSUED);
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isTrading() {
+ return this.state.equals(CommercialPaper.TRADING);
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isRedeemed() {
+ return this.state.equals(CommercialPaper.REDEEMED);
+ }
+
+ public CommercialPaper setIssued() {
+ this.state = CommercialPaper.ISSUED;
+ return this;
+ }
+
+ public CommercialPaper setTrading() {
+ this.state = CommercialPaper.TRADING;
+ return this;
+ }
+
+ public CommercialPaper setRedeemed() {
+ this.state = CommercialPaper.REDEEMED;
+ return this;
+ }
+
+ @Property()
+ private String paperNumber;
+
+ @Property()
+ private String issuer;
+
+ @Property()
+ private String issueDateTime;
+
+ @Property()
+ private int faceValue;
+
+ @Property()
+ private String maturityDateTime;
+
+ @Property()
+ private String owner;
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public CommercialPaper setOwner(String owner) {
+ this.owner = owner;
+ return this;
+ }
+
+ public CommercialPaper() {
+ super();
+ }
+
+ public CommercialPaper setKey() {
+ this.key = State.makeKey(new String[] { this.paperNumber });
+ return this;
+ }
+
+ public String getPaperNumber() {
+ return paperNumber;
+ }
+
+ public CommercialPaper setPaperNumber(String paperNumber) {
+ this.paperNumber = paperNumber;
+ return this;
+ }
+
+ public String getIssuer() {
+ return issuer;
+ }
+
+ public CommercialPaper setIssuer(String issuer) {
+ this.issuer = issuer;
+ return this;
+ }
+
+ public String getIssueDateTime() {
+ return issueDateTime;
+ }
+
+ public CommercialPaper setIssueDateTime(String issueDateTime) {
+ this.issueDateTime = issueDateTime;
+ return this;
+ }
+
+ public int getFaceValue() {
+ return faceValue;
+ }
+
+ public CommercialPaper setFaceValue(int faceValue) {
+ this.faceValue = faceValue;
+ return this;
+ }
+
+ public String getMaturityDateTime() {
+ return maturityDateTime;
+ }
+
+ public CommercialPaper setMaturityDateTime(String maturityDateTime) {
+ this.maturityDateTime = maturityDateTime;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "Paper::" + this.key + " " + this.getPaperNumber() + " " + getIssuer() + " " + getFaceValue();
+ }
+
+ /**
+ * Deserialize a state data to commercial paper
+ *
+ * @param {Buffer} data to form back into the object
+ */
+ public static CommercialPaper deserialize(byte[] data) {
+ JSONObject json = new JSONObject(new String(data, UTF_8));
+
+ String issuer = json.getString("issuer");
+ String paperNumber = json.getString("paperNumber");
+ String issueDateTime = json.getString("issueDateTime");
+ String maturityDateTime = json.getString("maturityDateTime");
+ String owner = json.getString("owner");
+ int faceValue = json.getInt("faceValue");
+ String state = json.getString("state");
+ return createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue, owner, state);
+ }
+
+ public static byte[] serialize(CommercialPaper paper) {
+ return State.serialize(paper);
+ }
+
+ /**
+ * Factory method to create a commercial paper object
+ */
+ public static CommercialPaper createInstance(String issuer, String paperNumber, String issueDateTime,
+ String maturityDateTime, int faceValue, String owner, String state) {
+ return new CommercialPaper().setIssuer(issuer).setPaperNumber(paperNumber).setMaturityDateTime(maturityDateTime)
+ .setFaceValue(faceValue).setKey().setIssueDateTime(issueDateTime).setOwner(issuer).setState(state);
+ }
+
+
+}
diff --git a/commercial-paper/organization/magnetocorp/application-java/src/org/papernet/ledgerapi/State.java b/commercial-paper/organization/magnetocorp/application-java/src/org/papernet/ledgerapi/State.java
new file mode 100644
index 0000000..a32abc0
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application-java/src/org/papernet/ledgerapi/State.java
@@ -0,0 +1,60 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+package org.papernet.ledgerapi;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.json.JSONObject;
+
+/**
+ * State class. States have a class, unique key, and a lifecycle current state
+ * the current state is determined by the specific subclass
+ */
+public class State {
+
+ protected String key;
+
+ /**
+ * @param {String|Object} class An identifiable class of the instance
+ * @param {keyParts[]} elements to pull together to make a key for the objects
+ */
+ public State() {
+
+ }
+
+ String getKey() {
+ return this.key;
+ }
+
+ public String[] getSplitKey() {
+ return State.splitKey(this.key);
+ }
+
+ /**
+ * Convert object to buffer containing JSON data serialization Typically used
+ * before putState()ledger API
+ *
+ * @param {Object} JSON object to serialize
+ * @return {buffer} buffer with the data to store
+ */
+ public static byte[] serialize(Object object) {
+ String jsonStr = new JSONObject(object).toString();
+ return jsonStr.getBytes(UTF_8);
+ }
+
+ /**
+ * Join the keyParts to make a unififed string
+ *
+ * @param (String[]) keyParts
+ */
+ public static String makeKey(String[] keyParts) {
+ return String.join(":", keyParts);
+ }
+
+ public static String[] splitKey(String key) {
+ System.out.println("splitting key " + key + " " + java.util.Arrays.asList(key.split(":")));
+ return key.split(":");
+ }
+
+}
diff --git a/commercial-paper/organization/magnetocorp/application/.eslintrc.js b/commercial-paper/organization/magnetocorp/application/.eslintrc.js
new file mode 100644
index 0000000..22fbefc
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 4],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-tabs': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
\ No newline at end of file
diff --git a/commercial-paper/organization/magnetocorp/application/.gitignore b/commercial-paper/organization/magnetocorp/application/.gitignore
new file mode 100644
index 0000000..b512c09
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application/.gitignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/commercial-paper/organization/magnetocorp/application/addToWallet.js b/commercial-paper/organization/magnetocorp/application/addToWallet.js
new file mode 100644
index 0000000..eb49123
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application/addToWallet.js
@@ -0,0 +1,55 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+// Bring key classes into scope, most importantly Fabric SDK network class
+const fs = require('fs');
+const { Wallets } = require('fabric-network');
+const path = require('path');
+
+const fixtures = path.resolve(__dirname, '../../../../test-network');
+
+async function main() {
+
+ // Main try/catch block
+ try {
+ // A wallet stores a collection of identities
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/isabella/wallet');
+
+ // Identity to credentials to be stored in the wallet
+ const credPath = path.join(fixtures, '/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com');
+ const certificate = fs.readFileSync(path.join(credPath, '/msp/signcerts/User1@org2.example.com-cert.pem')).toString();
+ const privateKey = fs.readFileSync(path.join(credPath, '/msp/keystore/priv_sk')).toString();
+
+ // Load credentials into wallet
+ const identityLabel = 'isabella';
+
+ const identity = {
+ credentials: {
+ certificate,
+ privateKey
+ },
+ mspId: 'Org2MSP',
+ type: 'X.509'
+ }
+
+
+ await wallet.put(identityLabel,identity);
+
+ } catch (error) {
+ console.log(`Error adding to wallet. ${error}`);
+ console.log(error.stack);
+ }
+}
+
+main().then(() => {
+ console.log('done');
+}).catch((e) => {
+ console.log(e);
+ console.log(e.stack);
+ process.exit(-1);
+});
diff --git a/commercial-paper/organization/magnetocorp/application/cpListener.js b/commercial-paper/organization/magnetocorp/application/cpListener.js
new file mode 100644
index 0000000..78c8ba1
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application/cpListener.js
@@ -0,0 +1,97 @@
+"use strict";
+
+const yaml = require('js-yaml');
+const { Wallets, Gateway } = require('fabric-network');
+const path = require("path");
+const fs = require("fs");
+
+let finished;
+async function main() {
+ try {
+ // Set up the wallet - just use Org2's wallet (isabella)
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/isabella/wallet');
+
+ // Create a new gateway for connecting to our peer node.
+ const gateway = new Gateway();
+
+ const userName = 'isabella';
+
+ // Load connection profile; will be used to locate a gateway
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org2.yaml', 'utf8'));
+
+ // Set connection options; identity and wallet
+ let connectionOptions = {
+ identity: userName,
+ wallet: wallet,
+ discovery: { enabled:true, asLocalhost: true }
+ };
+
+ // connect to the gateway
+ await gateway.connect(connectionProfile, connectionOptions);
+ // get the channel and smart contract
+ const network = await gateway.getNetwork('mychannel');
+
+ // Listen for blocks being added, display relevant contents: in particular, the transaction inputs
+ finished = false;
+
+ const listener = async (event) => {
+ if (event.blockData !== undefined) {
+ for (const i in event.blockData.data.data) {
+ if (event.blockData.data.data[i].payload.data.actions !== undefined) {
+ const inputArgs = event.blockData.data.data[i].payload.data.actions[0].payload.chaincode_proposal_payload.input.chaincode_spec.input.args;
+ // Print block details
+ console.log('----------');
+ console.log('Block:', parseInt(event.blockData.header.number), 'transaction', i);
+ // Show ID and timestamp of the transaction
+ const tx_id = event.blockData.data.data[i].payload.header.channel_header.tx_id;
+ const txTime = new Date(event.blockData.data.data[i].payload.header.channel_header.timestamp).toUTCString();
+ // Show ID, date and time of transaction
+ console.log('Transaction ID:', tx_id);
+ console.log('Timestamp:', txTime);
+ // Show transaction inputs (formatted, as may contain binary data)
+ let inputData = 'Inputs: ';
+ for (let j = 0; j < inputArgs.length; j++) {
+ const inputArgPrintable = inputArgs[j].toString().replace(/[^\x20-\x7E]+/g, '');
+ inputData = inputData.concat(inputArgPrintable, ' ');
+ }
+ console.log(inputData);
+ // Show the proposed writes to the world state
+ let keyData = 'Keys updated: ';
+ for (const l in event.blockData.data.data[i].payload.data.actions[0].payload.action.proposal_response_payload.extension.results.ns_rwset[1].rwset.writes) {
+ // add a ' ' space between multiple keys in 'concat'
+ keyData = keyData.concat(event.blockData.data.data[i].payload.data.actions[0].payload.action.proposal_response_payload.extension.results.ns_rwset[1].rwset.writes[l].key, ' ');
+ }
+ console.log(keyData);
+ // Show which organizations endorsed
+ let endorsers = 'Endorsers: ';
+ for (const k in event.blockData.data.data[i].payload.data.actions[0].payload.action.endorsements) {
+ endorsers = endorsers.concat(event.blockData.data.data[i].payload.data.actions[0].payload.action.endorsements[k].endorser.mspid, ' ');
+ }
+ console.log(endorsers);
+ // Was the transaction valid or not?
+ // (Invalid transactions are still logged on the blockchain but don't affect the world state)
+ if ((event.blockData.metadata.metadata[2])[i] !== 0) {
+ console.log('INVALID TRANSACTION');
+ }
+ }
+ }
+ }
+ };
+ const options = {
+ type: 'full',
+ startBlock: 1
+ };
+ await network.addBlockListener(listener, options);
+ while (!finished) {
+ await new Promise(resolve => setTimeout(resolve, 5000));
+ // Disconnect from the gateway after Promise is resolved.
+ // ... do other things
+ }
+ gateway.disconnect();
+ }
+ catch (error) {
+ console.error('Error: ', error);
+ process.exit(1);
+ }
+}
+void main();
diff --git a/commercial-paper/organization/magnetocorp/application/enrollUser.js b/commercial-paper/organization/magnetocorp/application/enrollUser.js
new file mode 100644
index 0000000..bb673f0
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application/enrollUser.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const FabricCAServices = require('fabric-ca-client');
+const { Wallets } = require('fabric-network');
+const fs = require('fs');
+const yaml = require('js-yaml');
+const path = require('path');
+
+async function main() {
+ try {
+ // load the network configuration
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org2.yaml', 'utf8'));
+
+ // Create a new CA client for interacting with the CA.
+ const caInfo = connectionProfile.certificateAuthorities['ca.org2.example.com'];
+ const caTLSCACerts = caInfo.tlsCACerts.pem;
+ const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName);
+
+ // Create a new file system based wallet for managing identities.
+ const walletPath = path.join(process.cwd(), '../identity/user/isabella/wallet');
+ const wallet = await Wallets.newFileSystemWallet(walletPath);
+ console.log(`Wallet path: ${walletPath}`);
+
+ // Check to see if we've already enrolled the admin user.
+ const userExists = await wallet.get('isabella');
+ if (userExists) {
+ console.log('An identity for the client user "user1" already exists in the wallet');
+ return;
+ }
+
+ // Enroll the admin user, and import the new identity into the wallet.
+ const enrollment = await ca.enroll({ enrollmentID: 'user1', enrollmentSecret: 'user1pw' });
+ const x509Identity = {
+ credentials: {
+ certificate: enrollment.certificate,
+ privateKey: enrollment.key.toBytes(),
+ },
+ mspId: 'Org2MSP',
+ type: 'X.509',
+ };
+ await wallet.put('isabella', x509Identity);
+ console.log('Successfully enrolled client user "isabella" and imported it into the wallet');
+
+ } catch (error) {
+ console.error(`Failed to enroll client user "isabella": ${error}`);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/commercial-paper/organization/magnetocorp/application/issue.js b/commercial-paper/organization/magnetocorp/application/issue.js
new file mode 100644
index 0000000..2d44fce
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application/issue.js
@@ -0,0 +1,103 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+ * This application has 6 basic steps:
+ * 1. Select an identity from a wallet
+ * 2. Connect to network gateway
+ * 3. Access PaperNet network
+ * 4. Construct request to issue commercial paper
+ * 5. Submit transaction
+ * 6. Process response
+ */
+
+'use strict';
+
+// Bring key classes into scope, most importantly Fabric SDK network class
+const fs = require('fs');
+const yaml = require('js-yaml');
+const { Wallets, Gateway } = require('fabric-network');
+const CommercialPaper = require('../contract/lib/paper.js');
+
+// Main program function
+async function main() {
+
+ // A wallet stores a collection of identities for use
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/isabella/wallet');
+
+ // A gateway defines the peers used to access Fabric networks
+ const gateway = new Gateway();
+
+ // Main try/catch block
+ try {
+
+ // Specify userName for network access
+ // const userName = 'isabella.issuer@magnetocorp.com';
+ const userName = 'isabella';
+
+ // Load connection profile; will be used to locate a gateway
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org2.yaml', 'utf8'));
+
+ // Set connection options; identity and wallet
+ let connectionOptions = {
+ identity: userName,
+ wallet: wallet,
+ discovery: { enabled:true, asLocalhost: true }
+ };
+
+ // Connect to gateway using application specified parameters
+ console.log('Connect to Fabric gateway.');
+
+ await gateway.connect(connectionProfile, connectionOptions);
+
+ // Access PaperNet network
+ console.log('Use network channel: mychannel.');
+
+ const network = await gateway.getNetwork('mychannel');
+
+ // Get addressability to commercial paper contract
+ console.log('Use org.papernet.commercialpaper smart contract.');
+
+ const contract = await network.getContract('papercontract');
+
+ // issue commercial paper
+ console.log('Submit commercial paper issue transaction.');
+
+ const issueResponse = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', '2020-05-31', '2020-11-30', '5000000');
+
+ // process response
+ console.log('Process issue transaction response.'+issueResponse);
+
+ let paper = CommercialPaper.fromBuffer(issueResponse);
+
+ console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully issued for value ${paper.faceValue}`);
+ console.log('Transaction complete.');
+
+ } catch (error) {
+
+ console.log(`Error processing transaction. ${error}`);
+ console.log(error.stack);
+
+ } finally {
+
+ // Disconnect from the gateway
+ console.log('Disconnect from Fabric gateway.');
+ gateway.disconnect();
+
+ }
+}
+main().then(() => {
+
+ console.log('Issue program complete.');
+
+}).catch((e) => {
+
+ console.log('Issue program exception.');
+ console.log(e);
+ console.log(e.stack);
+ process.exit(-1);
+
+});
diff --git a/commercial-paper/organization/magnetocorp/application/package.json b/commercial-paper/organization/magnetocorp/application/package.json
new file mode 100644
index 0000000..f8fac56
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "nodejs",
+ "version": "1.0.0",
+ "description": "",
+ "main": "issue.js",
+ "scripts": {
+ "test": "rm -rf _idwallet && node addToWallet.js && node issue.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-network": "^2.2.4",
+ "fabric-ca-client": "^2.2.4",
+ "js-yaml": "^3.12.0"
+ },
+ "devDependencies": {
+ "eslint": "^5.6.0"
+ }
+}
diff --git a/commercial-paper/organization/magnetocorp/application/transfer.js b/commercial-paper/organization/magnetocorp/application/transfer.js
new file mode 100644
index 0000000..908903a
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/application/transfer.js
@@ -0,0 +1,103 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+ * This application has 6 basic steps:
+ * 1. Select an identity from a wallet
+ * 2. Connect to network gateway
+ * 3. Access PaperNet network
+ * 4. Construct request to transfer commercial paper
+ * 5. Submit transaction
+ * 6. Process response
+ */
+
+'use strict';
+
+// Bring key classes into scope, most importantly Fabric SDK network class
+const fs = require('fs');
+const yaml = require('js-yaml');
+const { Wallets, Gateway } = require('fabric-network');
+const CommercialPaper = require('../contract/lib/paper.js');
+
+// Main program function
+async function main() {
+
+ // A wallet stores a collection of identities for use
+ const wallet = await Wallets.newFileSystemWallet('../identity/user/isabella/wallet');
+
+ // A gateway defines the peers used to access Fabric networks
+ const gateway = new Gateway();
+
+ // Main try/catch block
+ try {
+
+ // Specify userName for network access
+ // const userName = 'isabella.issuer@magnetocorp.com';
+ const userName = 'isabella';
+
+ // Load connection profile; will be used to locate a gateway
+ let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/connection-org2.yaml', 'utf8'));
+
+ // Set connection options; identity and wallet
+ let connectionOptions = {
+ identity: userName,
+ wallet: wallet,
+ discovery: { enabled:true, asLocalhost: true }
+ };
+
+ // Connect to gateway using application specified parameters
+ console.log('Connect to Fabric gateway.');
+
+ await gateway.connect(connectionProfile, connectionOptions);
+
+ // Access PaperNet network
+ console.log('Use network channel: mychannel.');
+
+ const network = await gateway.getNetwork('mychannel');
+
+ // Get addressability to commercial paper contract
+ console.log('Use org.papernet.commercialpaper smart contract.');
+
+ const contract = await network.getContract('papercontract');
+
+ // transfer commercial paper
+ console.log('Submit commercial paper transfer transaction.');
+
+ const transferResponse = await contract.submitTransaction('transfer', 'MagnetoCorp', '00001', 'DigiBank', 'Org1MSP', '2020-06-01');
+
+ // process response
+ console.log('Process transfer transaction response.'+ transferResponse);
+
+ let paper = CommercialPaper.fromBuffer(transferResponse);
+
+ console.log(`commercial paper issued by ${paper.issuer} : ${paper.paperNumber} was successfully transferred`);
+ console.log('Transaction complete.');
+
+ } catch (error) {
+
+ console.log(`Error processing transaction. ${error}`);
+ console.log(error.stack);
+
+ } finally {
+
+ // Disconnect from the gateway
+ console.log('Disconnect from Fabric gateway.');
+ gateway.disconnect();
+
+ }
+}
+main().then(() => {
+
+ console.log('Transfer program complete.');
+
+}).catch((e) => {
+
+ console.log('Transfer program exception.');
+ console.log(e);
+ console.log(e.stack);
+ process.exit(-1);
+
+});
diff --git a/commercial-paper/organization/magnetocorp/configuration/cli/docker-compose.yml b/commercial-paper/organization/magnetocorp/configuration/cli/docker-compose.yml
new file mode 100644
index 0000000..3bdda81
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/configuration/cli/docker-compose.yml
@@ -0,0 +1,37 @@
+#
+# Copyright IBM Corp All Rights Reserved
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+version: '2'
+
+networks:
+ basic:
+ external:
+ name: net_test
+
+services:
+ cliMagnetoCorp:
+ container_name: cliMagnetoCorp
+ image: hyperledger/fabric-tools:2.0.0-beta
+ tty: true
+ environment:
+ - GOPATH=/opt/gopath
+ - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
+ - FABRIC_LOGGING_SPEC=info
+ - CORE_PEER_ID=cli
+ - CORE_PEER_ADDRESS=peer0.org2.example.com:9051
+ - CORE_PEER_LOCALMSPID=Org2MSP
+ - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
+ - CORE_CHAINCODE_KEEPALIVE=10
+ - CORE_PEER_TLS_ENABLED=true
+ - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
+ - ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
+ working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
+ command: /bin/bash
+ volumes:
+ - /var/run/docker.sock:/host/var/run/docker.sock
+ - ./../../../../organization/magnetocorp:/opt/gopath/src/github.com/
+ - ./../../../../../test-network/organizations:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
+ networks:
+ - basic
diff --git a/commercial-paper/organization/magnetocorp/configuration/cli/monitordocker.sh b/commercial-paper/organization/magnetocorp/configuration/cli/monitordocker.sh
new file mode 100755
index 0000000..cd388b2
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/configuration/cli/monitordocker.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# This script uses the logspout and http stream tools to let you watch the docker containers
+# in action.
+#
+# More information at https://github.com/gliderlabs/logspout/tree/master/httpstream
+
+if [ -z "$1" ]; then
+ DOCKER_NETWORK=net_test
+else
+ DOCKER_NETWORK="$1"
+fi
+
+if [ -z "$2" ]; then
+ PORT=8000
+else
+ PORT="$2"
+fi
+
+echo Starting monitoring on all containers on the network ${DOCKER_NETWORK}
+
+docker kill logspout 2> /dev/null 1>&2 || true
+docker rm logspout 2> /dev/null 1>&2 || true
+
+docker run -d --name="logspout" \
+ --volume=/var/run/docker.sock:/var/run/docker.sock \
+ --publish=127.0.0.1:${PORT}:80 \
+ --network ${DOCKER_NETWORK} \
+ gliderlabs/logspout
+sleep 3
+curl http://127.0.0.1:${PORT}/logs
diff --git a/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paper.go b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paper.go
new file mode 100644
index 0000000..7eecdf4
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paper.go
@@ -0,0 +1,139 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "encoding/json"
+ "fmt"
+
+ ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/magnetocorp/contract-go/ledger-api"
+)
+
+// State enum for commercial paper state property
+type State uint
+
+const (
+ // ISSUED state for when a paper has been issued
+ ISSUED State = iota + 1
+ // TRADING state for when a paper is trading
+ TRADING
+ // REDEEMED state for when a paper has been redeemed
+ REDEEMED
+)
+
+func (state State) String() string {
+ names := []string{"ISSUED", "TRADING", "REDEEMED"}
+
+ if state < ISSUED || state > REDEEMED {
+ return "UNKNOWN"
+ }
+
+ return names[state-1]
+}
+
+// CreateCommercialPaperKey creates a key for commercial papers
+func CreateCommercialPaperKey(issuer string, paperNumber string) string {
+ return ledgerapi.MakeKey(issuer, paperNumber)
+}
+
+// Used for managing the fact status is private but want it in world state
+type commercialPaperAlias CommercialPaper
+type jsonCommercialPaper struct {
+ *commercialPaperAlias
+ State State `json:"currentState"`
+ Class string `json:"class"`
+ Key string `json:"key"`
+}
+
+// CommercialPaper defines a commercial paper
+type CommercialPaper struct {
+ PaperNumber string `json:"paperNumber"`
+ Issuer string `json:"issuer"`
+ IssueDateTime string `json:"issueDateTime"`
+ FaceValue int `json:"faceValue"`
+ MaturityDateTime string `json:"maturityDateTime"`
+ Owner string `json:"owner"`
+ state State `metadata:"currentState"`
+ class string `metadata:"class"`
+ key string `metadata:"key"`
+}
+
+// UnmarshalJSON special handler for managing JSON marshalling
+func (cp *CommercialPaper) UnmarshalJSON(data []byte) error {
+ jcp := jsonCommercialPaper{commercialPaperAlias: (*commercialPaperAlias)(cp)}
+
+ err := json.Unmarshal(data, &jcp)
+
+ if err != nil {
+ return err
+ }
+
+ cp.state = jcp.State
+
+ return nil
+}
+
+// MarshalJSON special handler for managing JSON marshalling
+func (cp CommercialPaper) MarshalJSON() ([]byte, error) {
+ jcp := jsonCommercialPaper{commercialPaperAlias: (*commercialPaperAlias)(&cp), State: cp.state, Class: "org.papernet.commercialpaper", Key: ledgerapi.MakeKey(cp.Issuer, cp.PaperNumber)}
+
+ return json.Marshal(&jcp)
+}
+
+// GetState returns the state
+func (cp *CommercialPaper) GetState() State {
+ return cp.state
+}
+
+// SetIssued returns the state to issued
+func (cp *CommercialPaper) SetIssued() {
+ cp.state = ISSUED
+}
+
+// SetTrading sets the state to trading
+func (cp *CommercialPaper) SetTrading() {
+ cp.state = TRADING
+}
+
+// SetRedeemed sets the state to redeemed
+func (cp *CommercialPaper) SetRedeemed() {
+ cp.state = REDEEMED
+}
+
+// IsIssued returns true if state is issued
+func (cp *CommercialPaper) IsIssued() bool {
+ return cp.state == ISSUED
+}
+
+// IsTrading returns true if state is trading
+func (cp *CommercialPaper) IsTrading() bool {
+ return cp.state == TRADING
+}
+
+// IsRedeemed returns true if state is redeemed
+func (cp *CommercialPaper) IsRedeemed() bool {
+ return cp.state == REDEEMED
+}
+
+// GetSplitKey returns values which should be used to form key
+func (cp *CommercialPaper) GetSplitKey() []string {
+ return []string{cp.Issuer, cp.PaperNumber}
+}
+
+// Serialize formats the commercial paper as JSON bytes
+func (cp *CommercialPaper) Serialize() ([]byte, error) {
+ return json.Marshal(cp)
+}
+
+// Deserialize formats the commercial paper from JSON bytes
+func Deserialize(bytes []byte, cp *CommercialPaper) error {
+ err := json.Unmarshal(bytes, cp)
+
+ if err != nil {
+ return fmt.Errorf("Error deserializing commercial paper. %s", err.Error())
+ }
+
+ return nil
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paper_test.go b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paper_test.go
new file mode 100644
index 0000000..07c888b
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paper_test.go
@@ -0,0 +1,125 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "testing"
+
+ ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/magnetocorp/contract-go/ledger-api"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestString(t *testing.T) {
+ assert.Equal(t, "ISSUED", ISSUED.String(), "should return string for issued")
+ assert.Equal(t, "TRADING", TRADING.String(), "should return string for issued")
+ assert.Equal(t, "REDEEMED", REDEEMED.String(), "should return string for issued")
+ assert.Equal(t, "UNKNOWN", State(REDEEMED+1).String(), "should return unknown when not one of constants")
+}
+
+func TestCreateCommercialPaperKey(t *testing.T) {
+ assert.Equal(t, ledgerapi.MakeKey("someissuer", "somepaper"), CreateCommercialPaperKey("someissuer", "somepaper"), "should return key comprised of passed values")
+}
+
+func TestGetState(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.state = ISSUED
+
+ assert.Equal(t, ISSUED, cp.GetState(), "should return set state")
+}
+
+func TestSetIssued(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.SetIssued()
+ assert.Equal(t, ISSUED, cp.state, "should set state to trading")
+}
+
+func TestSetTrading(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.SetTrading()
+ assert.Equal(t, TRADING, cp.state, "should set state to trading")
+}
+
+func TestSetRedeemed(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.SetRedeemed()
+ assert.Equal(t, REDEEMED, cp.state, "should set state to trading")
+}
+
+func TestIsIssued(t *testing.T) {
+ cp := new(CommercialPaper)
+
+ cp.SetIssued()
+ assert.True(t, cp.IsIssued(), "should be true when status set to issued")
+
+ cp.SetTrading()
+ assert.False(t, cp.IsIssued(), "should be false when status not set to issued")
+}
+
+func TestIsTrading(t *testing.T) {
+ cp := new(CommercialPaper)
+
+ cp.SetTrading()
+ assert.True(t, cp.IsTrading(), "should be true when status set to trading")
+
+ cp.SetRedeemed()
+ assert.False(t, cp.IsTrading(), "should be false when status not set to trading")
+}
+
+func TestIsRedeemed(t *testing.T) {
+ cp := new(CommercialPaper)
+
+ cp.SetRedeemed()
+ assert.True(t, cp.IsRedeemed(), "should be true when status set to redeemed")
+
+ cp.SetIssued()
+ assert.False(t, cp.IsRedeemed(), "should be false when status not set to redeemed")
+}
+
+func TestGetSplitKey(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.PaperNumber = "somepaper"
+ cp.Issuer = "someissuer"
+
+ assert.Equal(t, []string{"someissuer", "somepaper"}, cp.GetSplitKey(), "should return issuer and paper number as split key")
+}
+
+func TestSerialize(t *testing.T) {
+ cp := new(CommercialPaper)
+ cp.PaperNumber = "somepaper"
+ cp.Issuer = "someissuer"
+ cp.IssueDateTime = "sometime"
+ cp.FaceValue = 1000
+ cp.MaturityDateTime = "somelatertime"
+ cp.Owner = "someowner"
+ cp.state = TRADING
+
+ bytes, err := cp.Serialize()
+ assert.Nil(t, err, "should not error on serialize")
+ assert.Equal(t, `{"paperNumber":"somepaper","issuer":"someissuer","issueDateTime":"sometime","faceValue":1000,"maturityDateTime":"somelatertime","owner":"someowner","currentState":2,"class":"org.papernet.commercialpaper","key":"someissuer:somepaper"}`, string(bytes), "should return JSON formatted value")
+}
+
+func TestDeserialize(t *testing.T) {
+ var cp *CommercialPaper
+ var err error
+
+ goodJSON := `{"paperNumber":"somepaper","issuer":"someissuer","issueDateTime":"sometime","faceValue":1000,"maturityDateTime":"somelatertime","owner":"someowner","currentState":2,"class":"org.papernet.commercialpaper","key":"someissuer:somepaper"}`
+ expectedCp := new(CommercialPaper)
+ expectedCp.PaperNumber = "somepaper"
+ expectedCp.Issuer = "someissuer"
+ expectedCp.IssueDateTime = "sometime"
+ expectedCp.FaceValue = 1000
+ expectedCp.MaturityDateTime = "somelatertime"
+ expectedCp.Owner = "someowner"
+ expectedCp.state = TRADING
+ cp = new(CommercialPaper)
+ err = Deserialize([]byte(goodJSON), cp)
+ assert.Nil(t, err, "should not return error for deserialize")
+ assert.Equal(t, expectedCp, cp, "should create expected commercial paper")
+
+ badJSON := `{"paperNumber":"somepaper","issuer":"someissuer","issueDateTime":"sometime","faceValue":"NaN","maturityDateTime":"somelatertime","owner":"someowner","currentState":2,"class":"org.papernet.commercialpaper","key":"someissuer:somepaper"}`
+ cp = new(CommercialPaper)
+ err = Deserialize([]byte(badJSON), cp)
+ assert.EqualError(t, err, "Error deserializing commercial paper. json: cannot unmarshal string into Go struct field jsonCommercialPaper.faceValue of type int", "should return error for bad data")
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontext.go b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontext.go
new file mode 100644
index 0000000..c346cf3
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontext.go
@@ -0,0 +1,35 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// TransactionContextInterface an interface to
+// describe the minimum required functions for
+// a transaction context in the commercial
+// paper
+type TransactionContextInterface interface {
+ contractapi.TransactionContextInterface
+ GetPaperList() ListInterface
+}
+
+// TransactionContext implementation of
+// TransactionContextInterface for use with
+// commercial paper contract
+type TransactionContext struct {
+ contractapi.TransactionContext
+ paperList *list
+}
+
+// GetPaperList return paper list
+func (tc *TransactionContext) GetPaperList() ListInterface {
+ if tc.paperList == nil {
+ tc.paperList = newList(tc)
+ }
+
+ return tc.paperList
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontext_test.go b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontext_test.go
new file mode 100644
index 0000000..7ffc90f
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontext_test.go
@@ -0,0 +1,31 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "testing"
+
+ ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/magnetocorp/contract-go/ledger-api"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetPaperList(t *testing.T) {
+ var tc *TransactionContext
+ var expectedPaperList *list
+
+ tc = new(TransactionContext)
+ expectedPaperList = newList(tc)
+ actualList := tc.GetPaperList().(*list)
+ assert.Equal(t, expectedPaperList.stateList.(*ledgerapi.StateList).Name, actualList.stateList.(*ledgerapi.StateList).Name, "should configure paper list when one not already configured")
+
+ tc = new(TransactionContext)
+ expectedPaperList = new(list)
+ expectedStateList := new(ledgerapi.StateList)
+ expectedStateList.Ctx = tc
+ expectedStateList.Name = "existing paper list"
+ expectedPaperList.stateList = expectedStateList
+ tc.paperList = expectedPaperList
+ assert.Equal(t, expectedPaperList, tc.GetPaperList(), "should return set paper list when already set")
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontract.go b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontract.go
new file mode 100644
index 0000000..4e8cee2
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontract.go
@@ -0,0 +1,96 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// Contract chaincode that defines
+// the business logic for managing commercial
+// paper
+type Contract struct {
+ contractapi.Contract
+}
+
+// Instantiate does nothing
+func (c *Contract) Instantiate() {
+ fmt.Println("Instantiated")
+}
+
+// Issue creates a new commercial paper and stores it in the world state
+func (c *Contract) Issue(ctx TransactionContextInterface, issuer string, paperNumber string, issueDateTime string, maturityDateTime string, faceValue int) (*CommercialPaper, error) {
+ paper := CommercialPaper{PaperNumber: paperNumber, Issuer: issuer, IssueDateTime: issueDateTime, FaceValue: faceValue, MaturityDateTime: maturityDateTime, Owner: issuer}
+ paper.SetIssued()
+
+ err := ctx.GetPaperList().AddPaper(&paper)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return &paper, nil
+}
+
+// Buy updates a commercial paper to be in trading status and sets the new owner
+func (c *Contract) Buy(ctx TransactionContextInterface, issuer string, paperNumber string, currentOwner string, newOwner string, price int, purchaseDateTime string) (*CommercialPaper, error) {
+ paper, err := ctx.GetPaperList().GetPaper(issuer, paperNumber)
+
+ if err != nil {
+ return nil, err
+ }
+
+ if paper.Owner != currentOwner {
+ return nil, fmt.Errorf("Paper %s:%s is not owned by %s", issuer, paperNumber, currentOwner)
+ }
+
+ if paper.IsIssued() {
+ paper.SetTrading()
+ }
+
+ if !paper.IsTrading() {
+ return nil, fmt.Errorf("Paper %s:%s is not trading. Current state = %s", issuer, paperNumber, paper.GetState())
+ }
+
+ paper.Owner = newOwner
+
+ err = ctx.GetPaperList().UpdatePaper(paper)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return paper, nil
+}
+
+// Redeem updates a commercial paper status to be redeemed
+func (c *Contract) Redeem(ctx TransactionContextInterface, issuer string, paperNumber string, redeemingOwner string, redeenDateTime string) (*CommercialPaper, error) {
+ paper, err := ctx.GetPaperList().GetPaper(issuer, paperNumber)
+
+ if err != nil {
+ return nil, err
+ }
+
+ if paper.Owner != redeemingOwner {
+ return nil, fmt.Errorf("Paper %s:%s is not owned by %s", issuer, paperNumber, redeemingOwner)
+ }
+
+ if paper.IsRedeemed() {
+ return nil, fmt.Errorf("Paper %s:%s is already redeemed", issuer, paperNumber)
+ }
+
+ paper.Owner = paper.Issuer
+ paper.SetRedeemed()
+
+ err = ctx.GetPaperList().UpdatePaper(paper)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return paper, nil
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontract_test.go b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontract_test.go
new file mode 100644
index 0000000..25c429b
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/papercontract_test.go
@@ -0,0 +1,185 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+)
+
+// #########
+// HELPERS
+// #########
+type MockPaperList struct {
+ mock.Mock
+}
+
+func (mpl *MockPaperList) AddPaper(paper *CommercialPaper) error {
+ args := mpl.Called(paper)
+
+ return args.Error(0)
+}
+
+func (mpl *MockPaperList) GetPaper(issuer string, papernumber string) (*CommercialPaper, error) {
+ args := mpl.Called(issuer, papernumber)
+
+ return args.Get(0).(*CommercialPaper), args.Error(1)
+}
+
+func (mpl *MockPaperList) UpdatePaper(paper *CommercialPaper) error {
+ args := mpl.Called(paper)
+
+ return args.Error(0)
+}
+
+type MockTransactionContext struct {
+ contractapi.TransactionContext
+ paperList *MockPaperList
+}
+
+func (mtc *MockTransactionContext) GetPaperList() ListInterface {
+ return mtc.paperList
+}
+
+func resetPaper(paper *CommercialPaper) {
+ paper.Owner = "someowner"
+ paper.SetTrading()
+}
+
+// #########
+// TESTS
+// #########
+
+func TestIssue(t *testing.T) {
+ var paper *CommercialPaper
+ var err error
+
+ mpl := new(MockPaperList)
+ ctx := new(MockTransactionContext)
+ ctx.paperList = mpl
+
+ contract := new(Contract)
+
+ var sentPaper *CommercialPaper
+
+ mpl.On("AddPaper", mock.MatchedBy(func(paper *CommercialPaper) bool { sentPaper = paper; return paper.Issuer == "someissuer" })).Return(nil)
+ mpl.On("AddPaper", mock.MatchedBy(func(paper *CommercialPaper) bool { sentPaper = paper; return paper.Issuer == "someotherissuer" })).Return(errors.New("AddPaper error"))
+
+ expectedPaper := CommercialPaper{PaperNumber: "somepaper", Issuer: "someissuer", IssueDateTime: "someissuedate", FaceValue: 1000, MaturityDateTime: "somematuritydate", Owner: "someissuer", state: 1}
+ paper, err = contract.Issue(ctx, "someissuer", "somepaper", "someissuedate", "somematuritydate", 1000)
+ assert.Nil(t, err, "should not error when add paper does not error")
+ assert.Equal(t, sentPaper, paper, "should send the same paper as it returns to add paper")
+ assert.Equal(t, expectedPaper, *paper, "should correctly configure paper")
+
+ paper, err = contract.Issue(ctx, "someotherissuer", "somepaper", "someissuedate", "somematuritydate", 1000)
+ assert.EqualError(t, err, "AddPaper error", "should return error when add paper fails")
+ assert.Nil(t, paper, "should not return paper when fails")
+}
+
+func TestBuy(t *testing.T) {
+ var paper *CommercialPaper
+ var err error
+
+ mpl := new(MockPaperList)
+ ctx := new(MockTransactionContext)
+ ctx.paperList = mpl
+
+ contract := new(Contract)
+
+ wsPaper := new(CommercialPaper)
+ resetPaper(wsPaper)
+
+ var sentPaper *CommercialPaper
+ var emptyPaper *CommercialPaper
+ shouldError := false
+
+ mpl.On("GetPaper", "someissuer", "somepaper").Return(wsPaper, nil)
+ mpl.On("GetPaper", "someotherissuer", "someotherpaper").Return(emptyPaper, errors.New("GetPaper error"))
+ mpl.On("UpdatePaper", mock.MatchedBy(func(paper *CommercialPaper) bool { return shouldError })).Return(errors.New("UpdatePaper error"))
+ mpl.On("UpdatePaper", mock.MatchedBy(func(paper *CommercialPaper) bool { sentPaper = paper; return !shouldError })).Return(nil)
+
+ paper, err = contract.Buy(ctx, "someotherissuer", "someotherpaper", "someowner", "someotherowner", 100, "2019-12-10:10:00")
+ assert.EqualError(t, err, "GetPaper error", "should return error when GetPaper errors")
+ assert.Nil(t, paper, "should return nil for paper when GetPaper errors")
+
+ paper, err = contract.Buy(ctx, "someissuer", "somepaper", "someotherowner", "someowner", 100, "2019-12-10:10:00")
+ assert.EqualError(t, err, "Paper someissuer:somepaper is not owned by someotherowner", "should error when sent owner not correct")
+ assert.Nil(t, paper, "should not return paper for bad owner error")
+
+ resetPaper(wsPaper)
+ wsPaper.SetRedeemed()
+ paper, err = contract.Buy(ctx, "someissuer", "somepaper", "someowner", "someotherowner", 100, "2019-12-10:10:00")
+ assert.EqualError(t, err, "Paper someissuer:somepaper is not trading. Current state = REDEEMED")
+ assert.Nil(t, paper, "should not return paper for bad state error")
+
+ resetPaper(wsPaper)
+ shouldError = true
+ paper, err = contract.Buy(ctx, "someissuer", "somepaper", "someowner", "someotherowner", 100, "2019-12-10:10:00")
+ assert.EqualError(t, err, "UpdatePaper error", "should error when update paper fails")
+ assert.Nil(t, paper, "should not return paper for bad state error")
+ shouldError = false
+
+ resetPaper(wsPaper)
+ wsPaper.SetIssued()
+ paper, err = contract.Buy(ctx, "someissuer", "somepaper", "someowner", "someotherowner", 100, "2019-12-10:10:00")
+ assert.Nil(t, err, "should not error when good paper and owner")
+ assert.Equal(t, "someotherowner", paper.Owner, "should update the owner of the paper")
+ assert.True(t, paper.IsTrading(), "should mark issued paper as trading")
+ assert.Equal(t, sentPaper, paper, "should update same paper as it returns in the world state")
+}
+
+func TestRedeem(t *testing.T) {
+ var paper *CommercialPaper
+ var err error
+
+ mpl := new(MockPaperList)
+ ctx := new(MockTransactionContext)
+ ctx.paperList = mpl
+
+ contract := new(Contract)
+
+ var sentPaper *CommercialPaper
+ wsPaper := new(CommercialPaper)
+ resetPaper(wsPaper)
+
+ var emptyPaper *CommercialPaper
+ shouldError := false
+
+ mpl.On("GetPaper", "someissuer", "somepaper").Return(wsPaper, nil)
+ mpl.On("GetPaper", "someotherissuer", "someotherpaper").Return(emptyPaper, errors.New("GetPaper error"))
+ mpl.On("UpdatePaper", mock.MatchedBy(func(paper *CommercialPaper) bool { return shouldError })).Return(errors.New("UpdatePaper error"))
+ mpl.On("UpdatePaper", mock.MatchedBy(func(paper *CommercialPaper) bool { sentPaper = paper; return !shouldError })).Return(nil)
+
+ paper, err = contract.Redeem(ctx, "someotherissuer", "someotherpaper", "someowner", "2021-12-10:10:00")
+ assert.EqualError(t, err, "GetPaper error", "should error when GetPaper errors")
+ assert.Nil(t, paper, "should not return paper when GetPaper errors")
+
+ paper, err = contract.Redeem(ctx, "someissuer", "somepaper", "someotherowner", "2021-12-10:10:00")
+ assert.EqualError(t, err, "Paper someissuer:somepaper is not owned by someotherowner", "should error when paper owned by someone else")
+ assert.Nil(t, paper, "should not return paper when errors as owned by someone else")
+
+ resetPaper(wsPaper)
+ wsPaper.SetRedeemed()
+ paper, err = contract.Redeem(ctx, "someissuer", "somepaper", "someowner", "2021-12-10:10:00")
+ assert.EqualError(t, err, "Paper someissuer:somepaper is already redeemed", "should error when paper already redeemed")
+ assert.Nil(t, paper, "should not return paper when errors as already redeemed")
+
+ shouldError = true
+ resetPaper(wsPaper)
+ paper, err = contract.Redeem(ctx, "someissuer", "somepaper", "someowner", "2021-12-10:10:00")
+ assert.EqualError(t, err, "UpdatePaper error", "should error when update paper errors")
+ assert.Nil(t, paper, "should not return paper when UpdatePaper errors")
+ shouldError = false
+
+ resetPaper(wsPaper)
+ paper, err = contract.Redeem(ctx, "someissuer", "somepaper", "someowner", "2021-12-10:10:00")
+ assert.Nil(t, err, "should not error on good redeem")
+ assert.True(t, paper.IsRedeemed(), "should return redeemed paper")
+ assert.Equal(t, sentPaper, paper, "should update same paper as it returns in the world state")
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paperlist.go b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paperlist.go
new file mode 100644
index 0000000..9946d51
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paperlist.go
@@ -0,0 +1,55 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/magnetocorp/contract-go/ledger-api"
+
+// ListInterface defines functionality needed
+// to interact with the world state on behalf
+// of a commercial paper
+type ListInterface interface {
+ AddPaper(*CommercialPaper) error
+ GetPaper(string, string) (*CommercialPaper, error)
+ UpdatePaper(*CommercialPaper) error
+}
+
+type list struct {
+ stateList ledgerapi.StateListInterface
+}
+
+func (cpl *list) AddPaper(paper *CommercialPaper) error {
+ return cpl.stateList.AddState(paper)
+}
+
+func (cpl *list) GetPaper(issuer string, paperNumber string) (*CommercialPaper, error) {
+ cp := new(CommercialPaper)
+
+ err := cpl.stateList.GetState(CreateCommercialPaperKey(issuer, paperNumber), cp)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return cp, nil
+}
+
+func (cpl *list) UpdatePaper(paper *CommercialPaper) error {
+ return cpl.stateList.UpdateState(paper)
+}
+
+// NewList create a new list from context
+func newList(ctx TransactionContextInterface) *list {
+ stateList := new(ledgerapi.StateList)
+ stateList.Ctx = ctx
+ stateList.Name = "org.papernet.commercialpaperlist"
+ stateList.Deserialize = func(bytes []byte, state ledgerapi.StateInterface) error {
+ return Deserialize(bytes, state.(*CommercialPaper))
+ }
+
+ list := new(list)
+ list.stateList = stateList
+
+ return list
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paperlist_test.go b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paperlist_test.go
new file mode 100644
index 0000000..c13ff32
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/commercial-paper/paperlist_test.go
@@ -0,0 +1,103 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package commercialpaper
+
+import (
+ "errors"
+ "testing"
+
+ ledgerapi "github.com/hyperledger/fabric-samples/commercial-paper/organization/magnetocorp/contract-go/ledger-api"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+)
+
+// #########
+// HELPERS
+// #########
+
+type MockStateList struct {
+ mock.Mock
+}
+
+func (msl *MockStateList) AddState(state ledgerapi.StateInterface) error {
+ args := msl.Called(state)
+
+ return args.Error(0)
+}
+
+func (msl *MockStateList) GetState(key string, state ledgerapi.StateInterface) error {
+ args := msl.Called(key, state)
+
+ state.(*CommercialPaper).PaperNumber = "somepaper"
+
+ return args.Error(0)
+}
+
+func (msl *MockStateList) UpdateState(state ledgerapi.StateInterface) error {
+ args := msl.Called(state)
+
+ return args.Error(0)
+}
+
+// #########
+// TESTS
+// #########
+
+func TestAddPaper(t *testing.T) {
+ paper := new(CommercialPaper)
+
+ list := new(list)
+ msl := new(MockStateList)
+ msl.On("AddState", paper).Return(errors.New("Called add state correctly"))
+ list.stateList = msl
+
+ err := list.AddPaper(paper)
+ assert.EqualError(t, err, "Called add state correctly", "should call state list add state with paper")
+}
+
+func TestGetPaper(t *testing.T) {
+ var cp *CommercialPaper
+ var err error
+
+ list := new(list)
+ msl := new(MockStateList)
+ msl.On("GetState", CreateCommercialPaperKey("someissuer", "somepaper"), mock.MatchedBy(func(state ledgerapi.StateInterface) bool { _, ok := state.(*CommercialPaper); return ok })).Return(nil)
+ msl.On("GetState", CreateCommercialPaperKey("someotherissuer", "someotherpaper"), mock.MatchedBy(func(state ledgerapi.StateInterface) bool { _, ok := state.(*CommercialPaper); return ok })).Return(errors.New("GetState error"))
+ list.stateList = msl
+
+ cp, err = list.GetPaper("someissuer", "somepaper")
+ assert.Nil(t, err, "should not error when get state on state list does not error")
+ assert.Equal(t, cp.PaperNumber, "somepaper", "should use state list GetState to fill commercial paper")
+
+ cp, err = list.GetPaper("someotherissuer", "someotherpaper")
+ assert.EqualError(t, err, "GetState error", "should return error when state list get state errors")
+ assert.Nil(t, cp, "should not return commercial paper on error")
+}
+
+func TestUpdatePaper(t *testing.T) {
+ paper := new(CommercialPaper)
+
+ list := new(list)
+ msl := new(MockStateList)
+ msl.On("UpdateState", paper).Return(errors.New("Called update state correctly"))
+ list.stateList = msl
+
+ err := list.UpdatePaper(paper)
+ assert.EqualError(t, err, "Called update state correctly", "should call state list update state with paper")
+}
+
+func TestNewStateList(t *testing.T) {
+ ctx := new(TransactionContext)
+ list := newList(ctx)
+ stateList, ok := list.stateList.(*ledgerapi.StateList)
+
+ assert.True(t, ok, "should make statelist of type ledgerapi.StateList")
+ assert.Equal(t, ctx, stateList.Ctx, "should set the context to passed context")
+ assert.Equal(t, "org.papernet.commercialpaperlist", stateList.Name, "should set the name for the list")
+
+ expectedErr := Deserialize([]byte("bad json"), new(CommercialPaper))
+ err := stateList.Deserialize([]byte("bad json"), new(CommercialPaper))
+ assert.EqualError(t, err, expectedErr.Error(), "should call Deserialize when stateList.Deserialize called")
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/go.mod b/commercial-paper/organization/magnetocorp/contract-go/go.mod
new file mode 100644
index 0000000..97b78f6
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/go.mod
@@ -0,0 +1,13 @@
+module github.com/hyperledger/fabric-samples/commercial-paper/organization/magnetocorp/contract-go
+
+go 1.13
+
+require (
+ github.com/go-openapi/jsonreference v0.19.3 // indirect
+ github.com/hyperledger/fabric-contract-api-go v1.1.0
+ github.com/mailru/easyjson v0.7.0 // indirect
+ github.com/stretchr/testify v1.5.1
+ golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 // indirect
+ google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect
+ google.golang.org/grpc v1.24.0 // indirect
+)
diff --git a/commercial-paper/organization/magnetocorp/contract-go/go.sum b/commercial-paper/organization/magnetocorp/contract-go/go.sum
new file mode 100644
index 0000000..48c8221
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/go.sum
@@ -0,0 +1,167 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
+github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
+github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
+github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
+github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
+github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
+github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
+github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
+github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
+github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss=
+golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
+golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo=
+google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
+google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/commercial-paper/organization/magnetocorp/contract-go/ledger-api/state.go b/commercial-paper/organization/magnetocorp/contract-go/ledger-api/state.go
new file mode 100644
index 0000000..6d8c3f8
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/ledger-api/state.go
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package ledgerapi
+
+import (
+ "strings"
+)
+
+// SplitKey splits a key on colon
+func SplitKey(key string) []string {
+ return strings.Split(key, ":")
+}
+
+// MakeKey joins key parts using colon
+func MakeKey(keyParts ...string) string {
+ return strings.Join(keyParts, ":")
+}
+
+// StateInterface interface states must implement
+// for use in a list
+type StateInterface interface {
+ // GetSplitKey return components that combine to form the key
+ GetSplitKey() []string
+ Serialize() ([]byte, error)
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/ledger-api/statelist.go b/commercial-paper/organization/magnetocorp/contract-go/ledger-api/statelist.go
new file mode 100644
index 0000000..492efb3
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/ledger-api/statelist.go
@@ -0,0 +1,61 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package ledgerapi
+
+import (
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+)
+
+// StateListInterface functions that a state list
+// should have
+type StateListInterface interface {
+ AddState(StateInterface) error
+ GetState(string, StateInterface) error
+ UpdateState(StateInterface) error
+}
+
+// StateList useful for managing putting data in and out
+// of the ledger. Implementation of StateListInterface
+type StateList struct {
+ Ctx contractapi.TransactionContextInterface
+ Name string
+ Deserialize func([]byte, StateInterface) error
+}
+
+// AddState puts state into world state
+func (sl *StateList) AddState(state StateInterface) error {
+ key, _ := sl.Ctx.GetStub().CreateCompositeKey(sl.Name, state.GetSplitKey())
+ data, err := state.Serialize()
+
+ if err != nil {
+ return err
+ }
+
+ return sl.Ctx.GetStub().PutState(key, data)
+}
+
+// GetState returns state from world state. Unmarshalls the JSON
+// into passed state. Key is the split key value used in Add/Update
+// joined using a colon
+func (sl *StateList) GetState(key string, state StateInterface) error {
+ ledgerKey, _ := sl.Ctx.GetStub().CreateCompositeKey(sl.Name, SplitKey(key))
+ data, err := sl.Ctx.GetStub().GetState(ledgerKey)
+
+ if err != nil {
+ return err
+ } else if data == nil {
+ return fmt.Errorf("No state found for %s", key)
+ }
+
+ return sl.Deserialize(data, state)
+}
+
+// UpdateState puts state into world state. Same as AddState but
+// separate as semantically different
+func (sl *StateList) UpdateState(state StateInterface) error {
+ return sl.AddState(state)
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-go/main.go b/commercial-paper/organization/magnetocorp/contract-go/main.go
new file mode 100644
index 0000000..ee83834
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-go/main.go
@@ -0,0 +1,35 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/hyperledger/fabric-contract-api-go/contractapi"
+ "github.com/hyperledger/fabric-samples/commercial-paper/organization/magnetocorp/contract-go/commercial-paper"
+)
+
+func main() {
+
+ contract := new(commercialpaper.Contract)
+ contract.TransactionContextHandler = new(commercialpaper.TransactionContext)
+ contract.Name = "org.papernet.commercialpaper"
+ contract.Info.Version = "0.0.1"
+
+ chaincode, err := contractapi.NewChaincode(contract)
+
+ if err != nil {
+ panic(fmt.Sprintf("Error creating chaincode. %s", err.Error()))
+ }
+
+ chaincode.Info.Title = "CommercialPaperChaincode"
+ chaincode.Info.Version = "0.0.1"
+
+ err = chaincode.Start()
+
+ if err != nil {
+ panic(fmt.Sprintf("Error starting chaincode. %s", err.Error()))
+ }
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-java/.settings/org.eclipse.buildship.core.prefs b/commercial-paper/organization/magnetocorp/contract-java/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 0000000..e889521
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/commercial-paper/organization/magnetocorp/contract-java/build.gradle b/commercial-paper/organization/magnetocorp/contract-java/build.gradle
new file mode 100644
index 0000000..0a29887
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+plugins {
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+ id 'java'
+}
+
+
+version '0.0.1'
+
+sourceCompatibility = 1.8
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+dependencies {
+ implementation group: 'org.hyperledger.fabric-chaincode-java', name: 'fabric-chaincode-shim', version: '2.+'
+ implementation group: 'org.json', name: 'json', version: '20180813'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
+ testImplementation 'org.assertj:assertj-core:3.11.1'
+ testImplementation 'org.mockito:mockito-core:2.+'
+}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
+
+shadowJar {
+ baseName = 'chaincode'
+ version = null
+ classifier = null
+
+ manifest {
+ attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter'
+ }
+}
+
+
+tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters"
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-java/gradle/wrapper/gradle-wrapper.jar b/commercial-paper/organization/magnetocorp/contract-java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
Binary files /dev/null and b/commercial-paper/organization/magnetocorp/contract-java/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/commercial-paper/organization/magnetocorp/contract-java/gradle/wrapper/gradle-wrapper.properties b/commercial-paper/organization/magnetocorp/contract-java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..7c4388a
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/commercial-paper/organization/magnetocorp/contract-java/gradlew b/commercial-paper/organization/magnetocorp/contract-java/gradlew
new file mode 100755
index 0000000..83f2acf
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/commercial-paper/organization/magnetocorp/contract-java/gradlew.bat b/commercial-paper/organization/magnetocorp/contract-java/gradlew.bat
new file mode 100644
index 0000000..24467a1
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/commercial-paper/organization/magnetocorp/contract-java/settings.gradle b/commercial-paper/organization/magnetocorp/contract-java/settings.gradle
new file mode 100644
index 0000000..0c5f072
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'papercontract'
+
diff --git a/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaper.java b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaper.java
new file mode 100644
index 0000000..13d16b6
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaper.java
@@ -0,0 +1,183 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.example;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.example.ledgerapi.State;
+import org.hyperledger.fabric.contract.annotation.DataType;
+import org.hyperledger.fabric.contract.annotation.Property;
+import org.json.JSONObject;
+import org.json.JSONPropertyIgnore;
+
+@DataType()
+public class CommercialPaper extends State {
+
+ // Enumerate commercial paper state values
+ public final static String ISSUED = "ISSUED";
+ public final static String TRADING = "TRADING";
+ public final static String REDEEMED = "REDEEMED";
+
+ @Property()
+ private String state="";
+
+ public String getState() {
+ return state;
+ }
+
+ public CommercialPaper setState(String state) {
+ this.state = state;
+ return this;
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isIssued() {
+ return this.state.equals(CommercialPaper.ISSUED);
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isTrading() {
+ return this.state.equals(CommercialPaper.TRADING);
+ }
+
+ @JSONPropertyIgnore()
+ public boolean isRedeemed() {
+ return this.state.equals(CommercialPaper.REDEEMED);
+ }
+
+ public CommercialPaper setIssued() {
+ this.state = CommercialPaper.ISSUED;
+ return this;
+ }
+
+ public CommercialPaper setTrading() {
+ this.state = CommercialPaper.TRADING;
+ return this;
+ }
+
+ public CommercialPaper setRedeemed() {
+ this.state = CommercialPaper.REDEEMED;
+ return this;
+ }
+
+ @Property()
+ private String paperNumber;
+
+ @Property()
+ private String issuer;
+
+ @Property()
+ private String issueDateTime;
+
+ @Property()
+ private int faceValue;
+
+ @Property()
+ private String maturityDateTime;
+
+ @Property()
+ private String owner;
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public CommercialPaper setOwner(String owner) {
+ this.owner = owner;
+ return this;
+ }
+
+ public CommercialPaper() {
+ super();
+ }
+
+ public CommercialPaper setKey() {
+ this.key = State.makeKey(new String[] { this.paperNumber });
+ return this;
+ }
+
+ public String getPaperNumber() {
+ return paperNumber;
+ }
+
+ public CommercialPaper setPaperNumber(String paperNumber) {
+ this.paperNumber = paperNumber;
+ return this;
+ }
+
+ public String getIssuer() {
+ return issuer;
+ }
+
+ public CommercialPaper setIssuer(String issuer) {
+ this.issuer = issuer;
+ return this;
+ }
+
+ public String getIssueDateTime() {
+ return issueDateTime;
+ }
+
+ public CommercialPaper setIssueDateTime(String issueDateTime) {
+ this.issueDateTime = issueDateTime;
+ return this;
+ }
+
+ public int getFaceValue() {
+ return faceValue;
+ }
+
+ public CommercialPaper setFaceValue(int faceValue) {
+ this.faceValue = faceValue;
+ return this;
+ }
+
+ public String getMaturityDateTime() {
+ return maturityDateTime;
+ }
+
+ public CommercialPaper setMaturityDateTime(String maturityDateTime) {
+ this.maturityDateTime = maturityDateTime;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "Paper::" + this.key + " " + this.getPaperNumber() + " " + getIssuer() + " " + getFaceValue();
+ }
+
+ /**
+ * Deserialize a state data to commercial paper
+ *
+ * @param {Buffer} data to form back into the object
+ */
+ public static CommercialPaper deserialize(byte[] data) {
+ JSONObject json = new JSONObject(new String(data, UTF_8));
+
+ String issuer = json.getString("issuer");
+ String paperNumber = json.getString("paperNumber");
+ String issueDateTime = json.getString("issueDateTime");
+ String maturityDateTime = json.getString("maturityDateTime");
+ String owner = json.getString("owner");
+ int faceValue = json.getInt("faceValue");
+ String state = json.getString("state");
+ return createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue,owner,state);
+ }
+
+ public static byte[] serialize(CommercialPaper paper) {
+ return State.serialize(paper);
+ }
+
+ /**
+ * Factory method to create a commercial paper object
+ */
+ public static CommercialPaper createInstance(String issuer, String paperNumber, String issueDateTime,
+ String maturityDateTime, int faceValue, String owner, String state) {
+ return new CommercialPaper().setIssuer(issuer).setPaperNumber(paperNumber).setMaturityDateTime(maturityDateTime)
+ .setFaceValue(faceValue).setKey().setIssueDateTime(issueDateTime).setOwner(owner).setState(state);
+ }
+
+
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaperContext.java b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaperContext.java
new file mode 100644
index 0000000..7a946f2
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaperContext.java
@@ -0,0 +1,15 @@
+package org.example;
+
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+
+class CommercialPaperContext extends Context {
+
+ public CommercialPaperContext(ChaincodeStub stub) {
+ super(stub);
+ this.paperList = new PaperList(this);
+ }
+
+ public PaperList paperList;
+
+}
\ No newline at end of file
diff --git a/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaperContract.java b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaperContract.java
new file mode 100644
index 0000000..a781c36
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/CommercialPaperContract.java
@@ -0,0 +1,170 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+package org.example;
+
+import java.util.logging.Logger;
+
+import org.example.ledgerapi.State;
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.contract.ContractInterface;
+import org.hyperledger.fabric.contract.annotation.Contact;
+import org.hyperledger.fabric.contract.annotation.Contract;
+import org.hyperledger.fabric.contract.annotation.Default;
+import org.hyperledger.fabric.contract.annotation.Info;
+import org.hyperledger.fabric.contract.annotation.License;
+import org.hyperledger.fabric.contract.annotation.Transaction;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+
+/**
+ * A custom context provides easy access to list of all commercial papers
+ */
+
+/**
+ * Define commercial paper smart contract by extending Fabric Contract class
+ *
+ */
+@Contract(name = "org.papernet.commercialpaper", info = @Info(title = "MyAsset contract", description = "", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "java-contract@example.com", name = "java-contract", url = "http://java-contract.me")))
+@Default
+public class CommercialPaperContract implements ContractInterface {
+
+ // use the classname for the logger, this way you can refactor
+ private final static Logger LOG = Logger.getLogger(CommercialPaperContract.class.getName());
+
+ @Override
+ public Context createContext(ChaincodeStub stub) {
+ return new CommercialPaperContext(stub);
+ }
+
+ public CommercialPaperContract() {
+
+ }
+
+ /**
+ * Define a custom context for commercial paper
+ */
+
+ /**
+ * Instantiate to perform any setup of the ledger that might be required.
+ *
+ * @param {Context} ctx the transaction context
+ */
+ @Transaction
+ public void instantiate(CommercialPaperContext ctx) {
+ // No implementation required with this example
+ // It could be where data migration is performed, if necessary
+ LOG.info("No data migration to perform");
+ }
+
+ /**
+ * Issue commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} issueDateTime paper issue date
+ * @param {String} maturityDateTime paper maturity date
+ * @param {Integer} faceValue face value of paper
+ */
+ @Transaction
+ public CommercialPaper issue(CommercialPaperContext ctx, String issuer, String paperNumber, String issueDateTime,
+ String maturityDateTime, int faceValue) {
+
+ System.out.println(ctx);
+
+ // create an instance of the paper
+ CommercialPaper paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime, maturityDateTime,
+ faceValue,issuer,"");
+
+ // Smart contract, rather than paper, moves paper into ISSUED state
+ paper.setIssued();
+
+ // Newly issued paper is owned by the issuer
+ paper.setOwner(issuer);
+
+ System.out.println(paper);
+ // Add the paper to the list of all similar commercial papers in the ledger
+ // world state
+ ctx.paperList.addPaper(paper);
+
+ // Must return a serialized paper to caller of smart contract
+ return paper;
+ }
+
+ /**
+ * Buy commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} currentOwner current owner of paper
+ * @param {String} newOwner new owner of paper
+ * @param {Integer} price price paid for this paper
+ * @param {String} purchaseDateTime time paper was purchased (i.e. traded)
+ */
+ @Transaction
+ public CommercialPaper buy(CommercialPaperContext ctx, String issuer, String paperNumber, String currentOwner,
+ String newOwner, int price, String purchaseDateTime) {
+
+ // Retrieve the current paper using key fields provided
+ String paperKey = State.makeKey(new String[] { paperNumber });
+ CommercialPaper paper = ctx.paperList.getPaper(paperKey);
+
+ // Validate current owner
+ if (!paper.getOwner().equals(currentOwner)) {
+ throw new RuntimeException("Paper " + issuer + paperNumber + " is not owned by " + currentOwner);
+ }
+
+ // First buy moves state from ISSUED to TRADING
+ if (paper.isIssued()) {
+ paper.setTrading();
+ }
+
+ // Check paper is not already REDEEMED
+ if (paper.isTrading()) {
+ paper.setOwner(newOwner);
+ } else {
+ throw new RuntimeException(
+ "Paper " + issuer + paperNumber + " is not trading. Current state = " + paper.getState());
+ }
+
+ // Update the paper
+ ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ /**
+ * Redeem commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} redeemingOwner redeeming owner of paper
+ * @param {String} redeemDateTime time paper was redeemed
+ */
+ @Transaction
+ public CommercialPaper redeem(CommercialPaperContext ctx, String issuer, String paperNumber, String redeemingOwner,
+ String redeemDateTime) {
+
+ String paperKey = CommercialPaper.makeKey(new String[] { paperNumber });
+
+ CommercialPaper paper = ctx.paperList.getPaper(paperKey);
+
+ // Check paper is not REDEEMED
+ if (paper.isRedeemed()) {
+ throw new RuntimeException("Paper " + issuer + paperNumber + " already redeemed");
+ }
+
+ // Verify that the redeemer owns the commercial paper before redeeming it
+ if (paper.getOwner().equals(redeemingOwner)) {
+ paper.setOwner(paper.getIssuer());
+ paper.setRedeemed();
+ } else {
+ throw new RuntimeException("Redeeming owner does not own paper" + issuer + paperNumber);
+ }
+
+ ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/PaperList.java b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/PaperList.java
new file mode 100644
index 0000000..0ecd24c
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/PaperList.java
@@ -0,0 +1,31 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.example;
+
+import org.example.ledgerapi.StateList;
+import org.hyperledger.fabric.contract.Context;
+
+public class PaperList {
+
+ private StateList stateList;
+
+ public PaperList(Context ctx) {
+ this.stateList = StateList.getStateList(ctx, PaperList.class.getSimpleName(), CommercialPaper::deserialize);
+ }
+
+ public PaperList addPaper(CommercialPaper paper) {
+ stateList.addState(paper);
+ return this;
+ }
+
+ public CommercialPaper getPaper(String paperKey) {
+ return (CommercialPaper) this.stateList.getState(paperKey);
+ }
+
+ public PaperList updatePaper(CommercialPaper paper) {
+ this.stateList.updateState(paper);
+ return this;
+ }
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/State.java b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/State.java
new file mode 100644
index 0000000..46d35c3
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/State.java
@@ -0,0 +1,60 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+package org.example.ledgerapi;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.json.JSONObject;
+
+/**
+ * State class. States have a class, unique key, and a lifecycle current state
+ * the current state is determined by the specific subclass
+ */
+public class State {
+
+ protected String key;
+
+ /**
+ * @param {String|Object} class An identifiable class of the instance
+ * @param {keyParts[]} elements to pull together to make a key for the objects
+ */
+ public State() {
+
+ }
+
+ String getKey() {
+ return this.key;
+ }
+
+ public String[] getSplitKey() {
+ return State.splitKey(this.key);
+ }
+
+ /**
+ * Convert object to buffer containing JSON data serialization Typically used
+ * before putState()ledger API
+ *
+ * @param {Object} JSON object to serialize
+ * @return {buffer} buffer with the data to store
+ */
+ public static byte[] serialize(Object object) {
+ String jsonStr = new JSONObject(object).toString();
+ return jsonStr.getBytes(UTF_8);
+ }
+
+ /**
+ * Join the keyParts to make a unififed string
+ *
+ * @param (String[]) keyParts
+ */
+ public static String makeKey(String[] keyParts) {
+ return String.join(":", keyParts);
+ }
+
+ public static String[] splitKey(String key) {
+ System.out.println("splitting key " + key + " " + java.util.Arrays.asList(key.split(":")));
+ return key.split(":");
+ }
+
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/StateDeserializer.java b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/StateDeserializer.java
new file mode 100644
index 0000000..891788e
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/StateDeserializer.java
@@ -0,0 +1,6 @@
+package org.example.ledgerapi;
+
+@FunctionalInterface
+public interface StateDeserializer {
+ State deserialize(byte[] buffer);
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/StateList.java b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/StateList.java
new file mode 100644
index 0000000..d672586
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/StateList.java
@@ -0,0 +1,48 @@
+package org.example.ledgerapi;
+
+import org.example.ledgerapi.impl.StateListImpl;
+import org.hyperledger.fabric.contract.Context;
+
+public interface StateList {
+
+ /*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+ /**
+ * StateList provides a named virtual container for a set of ledger states. Each
+ * state has a unique key which associates it with the container, rather than
+ * the container containing a link to the state. This minimizes collisions for
+ * parallel transactions on different states.
+ */
+
+ /**
+ * Store Fabric context for subsequent API access, and name of list
+ */
+ static StateList getStateList(Context ctx, String listName, StateDeserializer deserializer) {
+ return new StateListImpl(ctx, listName, deserializer);
+ }
+
+ /**
+ * Add a state to the list. Creates a new state in worldstate with appropriate
+ * composite key. Note that state defines its own key. State object is
+ * serialized before writing.
+ */
+ public StateList addState(State state);
+
+ /**
+ * Get a state from the list using supplied keys. Form composite keys to
+ * retrieve state from world state. State data is deserialized into JSON object
+ * before being returned.
+ */
+ public State getState(String key);
+
+ /**
+ * Update a state in the list. Puts the new state in world state with
+ * appropriate composite key. Note that state defines its own key. A state is
+ * serialized before writing. Logic is very similar to addState() but kept
+ * separate becuase it is semantically distinct.
+ */
+ public StateList updateState(State state);
+
+}
diff --git a/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/impl/StateListImpl.java b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/impl/StateListImpl.java
new file mode 100644
index 0000000..78a4293
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract-java/src/main/java/org/example/ledgerapi/impl/StateListImpl.java
@@ -0,0 +1,100 @@
+package org.example.ledgerapi.impl;
+
+import java.util.Arrays;
+
+import org.example.ledgerapi.State;
+import org.example.ledgerapi.StateDeserializer;
+import org.example.ledgerapi.StateList;
+import org.hyperledger.fabric.contract.Context;
+import org.hyperledger.fabric.shim.ChaincodeStub;
+import org.hyperledger.fabric.shim.ledger.CompositeKey;
+
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+/**
+ * StateList provides a named virtual container for a set of ledger states. Each
+ * state has a unique key which associates it with the container, rather than
+ * the container containing a link to the state. This minimizes collisions for
+ * parallel transactions on different states.
+ */
+public class StateListImpl implements StateList {
+
+ private Context ctx;
+ private String name;
+ private Object supportedClasses;
+ private StateDeserializer deserializer;
+
+ /**
+ * Store Fabric context for subsequent API access, and name of list
+ *
+ * @param deserializer
+ */
+ public StateListImpl(Context ctx, String listName, StateDeserializer deserializer) {
+ this.ctx = ctx;
+ this.name = listName;
+ this.deserializer = deserializer;
+
+ }
+
+ /**
+ * Add a state to the list. Creates a new state in worldstate with appropriate
+ * composite key. Note that state defines its own key. State object is
+ * serialized before writing.
+ */
+ @Override
+ public StateList addState(State state) {
+ System.out.println("Adding state " + this.name);
+ ChaincodeStub stub = this.ctx.getStub();
+ System.out.println("Stub=" + stub);
+ String[] splitKey = state.getSplitKey();
+ System.out.println("Split key " + Arrays.asList(splitKey));
+
+ CompositeKey ledgerKey = stub.createCompositeKey(this.name, splitKey);
+ System.out.println("ledgerkey is ");
+ System.out.println(ledgerKey);
+
+ byte[] data = State.serialize(state);
+ System.out.println("ctx" + this.ctx);
+ System.out.println("stub" + this.ctx.getStub());
+ this.ctx.getStub().putState(ledgerKey.toString(), data);
+
+ return this;
+ }
+
+ /**
+ * Get a state from the list using supplied keys. Form composite keys to
+ * retrieve state from world state. State data is deserialized into JSON object
+ * before being returned.
+ */
+ @Override
+ public State getState(String key) {
+
+ CompositeKey ledgerKey = this.ctx.getStub().createCompositeKey(this.name, State.splitKey(key));
+
+ byte[] data = this.ctx.getStub().getState(ledgerKey.toString());
+ if (data != null) {
+ State state = this.deserializer.deserialize(data);
+ return state;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Update a state in the list. Puts the new state in world state with
+ * appropriate composite key. Note that state defines its own key. A state is
+ * serialized before writing. Logic is very similar to addState() but kept
+ * separate becuase it is semantically distinct.
+ */
+ @Override
+ public StateList updateState(State state) {
+ CompositeKey ledgerKey = this.ctx.getStub().createCompositeKey(this.name, state.getSplitKey());
+ byte[] data = State.serialize(state);
+ this.ctx.getStub().putState(ledgerKey.toString(), data);
+
+ return this;
+ }
+
+}
diff --git a/commercial-paper/organization/magnetocorp/contract/.editorconfig b/commercial-paper/organization/magnetocorp/contract/.editorconfig
new file mode 100755
index 0000000..75a13be
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/.editorconfig
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/commercial-paper/organization/magnetocorp/contract/.eslintignore b/commercial-paper/organization/magnetocorp/contract/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/commercial-paper/organization/magnetocorp/contract/.eslintrc.js b/commercial-paper/organization/magnetocorp/contract/.eslintrc.js
new file mode 100644
index 0000000..6772c66
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 4],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-tabs': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/commercial-paper/organization/magnetocorp/contract/.npmignore b/commercial-paper/organization/magnetocorp/contract/.npmignore
new file mode 100644
index 0000000..a00ca94
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/.npmignore
@@ -0,0 +1,77 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
diff --git a/commercial-paper/organization/magnetocorp/contract/index.js b/commercial-paper/organization/magnetocorp/contract/index.js
new file mode 100644
index 0000000..e0ea2c7
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/index.js
@@ -0,0 +1,10 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+const cpcontract = require('./lib/papercontract.js');
+module.exports.contracts = [cpcontract];
diff --git a/commercial-paper/organization/magnetocorp/contract/ledger-api/state.js b/commercial-paper/organization/magnetocorp/contract/ledger-api/state.js
new file mode 100644
index 0000000..f63e792
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/ledger-api/state.js
@@ -0,0 +1,105 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+/**
+ * State class. States have a class, unique key, and a lifecycle current state
+ * the current state is determined by the specific subclass
+ */
+class State {
+
+ /**
+ * @param {String|Object} class An indentifiable class of the instance
+ * @param {keyParts[]} elements to pull together to make a key for the objects
+ */
+ constructor(stateClass, keyParts) {
+ this.class = stateClass;
+ this.key = State.makeKey(keyParts);
+ this.currentState = null;
+ }
+
+ getClass() {
+ return this.class;
+ }
+
+ getKey() {
+ return this.key;
+ }
+
+ getSplitKey(){
+ return State.splitKey(this.key);
+ }
+
+ getCurrentState(){
+ return this.currentState;
+ }
+
+ // not used
+/* serialize() {
+
+ return State.serialize(this);
+ } */
+
+ /**
+ * Convert object to buffer containing JSON data serialization
+ * Typically used before putState()ledger API
+ * @param {Object} JSON object to serialize
+ * @return {buffer} buffer with the data to store
+ */
+ static serialize(object) {
+ // don't write the key:value passed in - we already have a real composite key, issuer and paper Number.
+ delete object.key;
+ return Buffer.from(JSON.stringify(object));
+ }
+
+ /**
+ * Deserialize object into one of a set of supported JSON classes
+ * i.e. Covert serialized data to JSON object
+ * Typically used after getState() ledger API
+ * @param {data} data to deserialize into JSON object
+ * @param (supportedClasses) the set of classes data can be serialized to
+ * @return {json} json with the data to store
+ */
+ static deserialize(data, supportedClasses) {
+ let json = JSON.parse(data.toString());
+ let objClass = supportedClasses[json.class];
+ if (!objClass) {
+ throw new Error(`Unknown class of ${json.class}`);
+ }
+ let object = new (objClass)(json);
+
+ return object;
+ }
+
+ /**
+ * Deserialize object into specific object class
+ * Typically used after getState() ledger API
+ * @param {data} data to deserialize into JSON object
+ * @return {json} json with the data to store
+ */
+ static deserializeClass(data, objClass) {
+ let json = JSON.parse(data.toString());
+ let object = new (objClass)(json);
+ return object;
+ }
+
+ /**
+ * Join the keyParts to make a unififed string
+ * @param (String[]) keyParts
+ */
+ static makeKey(keyParts) {
+ // return keyParts.map(part => JSON.stringify(part)).join(':');
+ return keyParts.map(part => part).join(':');
+ }
+
+ static splitKey(key){
+ return key.split(':');
+ }
+
+}
+
+module.exports = State;
diff --git a/commercial-paper/organization/magnetocorp/contract/ledger-api/statelist.js b/commercial-paper/organization/magnetocorp/contract/ledger-api/statelist.js
new file mode 100644
index 0000000..15ec837
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/ledger-api/statelist.js
@@ -0,0 +1,74 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+const State = require('./state.js');
+
+/**
+ * StateList provides a named virtual container for a set of ledger states.
+ * Each state has a unique key which associates it with the container, rather
+ * than the container containing a link to the state. This minimizes collisions
+ * for parallel transactions on different states.
+ */
+class StateList {
+
+ /**
+ * Store Fabric context for subsequent API access, and name of list
+ */
+ constructor(ctx, listName) {
+ this.ctx = ctx;
+ this.name = listName;
+ this.supportedClasses = {};
+
+ }
+
+ /**
+ * Add a state to the list. Creates a new state in worldstate with
+ * appropriate composite key. Note that state defines its own key.
+ * State object is serialized before writing.
+ */
+ async addState(state) {
+ let key = this.ctx.stub.createCompositeKey(this.name, state.getSplitKey());
+ let data = State.serialize(state);
+ await this.ctx.stub.putState(key, data);
+ }
+
+ /**
+ * Get a state from the list using supplied keys. Form composite
+ * keys to retrieve state from world state. State data is deserialized
+ * into JSON object before being returned.
+ */
+ async getState(key) {
+ let ledgerKey = this.ctx.stub.createCompositeKey(this.name, State.splitKey(key));
+ let data = await this.ctx.stub.getState(ledgerKey);
+ if (data && data.toString('utf8')) {
+ let state = State.deserialize(data, this.supportedClasses);
+ return state;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Update a state in the list. Puts the new state in world state with
+ * appropriate composite key. Note that state defines its own key.
+ * A state is serialized before writing. Logic is very similar to
+ * addState() but kept separate becuase it is semantically distinct.
+ */
+ async updateState(state) {
+ let key = this.ctx.stub.createCompositeKey(this.name, state.getSplitKey());
+ let data = State.serialize(state);
+ await this.ctx.stub.putState(key, data);
+ }
+
+ /** Stores the class for future deserialization */
+ use(stateClass) {
+ this.supportedClasses[stateClass.getClass()] = stateClass;
+ }
+
+}
+
+module.exports = StateList;
diff --git a/commercial-paper/organization/magnetocorp/contract/lib/paper.js b/commercial-paper/organization/magnetocorp/contract/lib/paper.js
new file mode 100644
index 0000000..0629ba7
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/lib/paper.js
@@ -0,0 +1,121 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+// Utility class for ledger state
+const State = require('./../ledger-api/state.js');
+
+// Enumerate commercial paper state values
+const cpState = {
+ ISSUED: 1,
+ PENDING: 2,
+ TRADING: 3,
+ REDEEMED: 4
+};
+
+/**
+ * CommercialPaper class extends State class
+ * Class will be used by application and smart contract to define a paper
+ */
+class CommercialPaper extends State {
+
+ constructor(obj) {
+ super(CommercialPaper.getClass(), [obj.issuer, obj.paperNumber]);
+ Object.assign(this, obj);
+ }
+
+ /**
+ * Basic getters and setters
+ */
+ getIssuer() {
+ return this.issuer;
+ }
+
+ setIssuer(newIssuer) {
+ this.issuer = newIssuer;
+ }
+
+ getOwner() {
+ return this.owner;
+ }
+
+ setOwnerMSP(mspid) {
+ this.mspid = mspid;
+ }
+
+ getOwnerMSP() {
+ return this.mspid;
+ }
+
+ setOwner(newOwner) {
+ this.owner = newOwner;
+ }
+
+ /**
+ * Useful methods to encapsulate commercial paper states
+ */
+ setIssued() {
+ this.currentState = cpState.ISSUED;
+ }
+
+ setTrading() {
+ this.currentState = cpState.TRADING;
+ }
+
+ setRedeemed() {
+ this.currentState = cpState.REDEEMED;
+ }
+
+ setPending() {
+ this.currentState = cpState.PENDING;
+ }
+
+ isIssued() {
+ return this.currentState === cpState.ISSUED;
+ }
+
+ isTrading() {
+ return this.currentState === cpState.TRADING;
+ }
+
+ isRedeemed() {
+ return this.currentState === cpState.REDEEMED;
+ }
+
+ isPending() {
+ return this.currentState === cpState.PENDING;
+ }
+
+ static fromBuffer(buffer) {
+ return CommercialPaper.deserialize(buffer);
+ }
+
+ toBuffer() {
+ return Buffer.from(JSON.stringify(this));
+ }
+
+ /**
+ * Deserialize a state data to commercial paper
+ * @param {Buffer} data to form back into the object
+ */
+ static deserialize(data) {
+ return State.deserializeClass(data, CommercialPaper);
+ }
+
+ /**
+ * Factory method to create a commercial paper object
+ */
+ static createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
+ return new CommercialPaper({ issuer, paperNumber, issueDateTime, maturityDateTime, faceValue });
+ }
+
+ static getClass() {
+ return 'org.papernet.commercialpaper';
+ }
+}
+
+module.exports = CommercialPaper;
diff --git a/commercial-paper/organization/magnetocorp/contract/lib/papercontract.js b/commercial-paper/organization/magnetocorp/contract/lib/papercontract.js
new file mode 100644
index 0000000..c3d2cdb
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/lib/papercontract.js
@@ -0,0 +1,339 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+// Fabric smart contract classes
+const { Contract, Context } = require('fabric-contract-api');
+
+// PaperNet specifc classes
+const CommercialPaper = require('./paper.js');
+const PaperList = require('./paperlist.js');
+const QueryUtils = require('./queries.js');
+
+/**
+ * A custom context provides easy access to list of all commercial papers
+ */
+class CommercialPaperContext extends Context {
+
+ constructor() {
+ super();
+ // All papers are held in a list of papers
+ this.paperList = new PaperList(this);
+ }
+
+}
+
+/**
+ * Define commercial paper smart contract by extending Fabric Contract class
+ *
+ */
+class CommercialPaperContract extends Contract {
+
+ constructor() {
+ // Unique namespace when multiple contracts per chaincode file
+ super('org.papernet.commercialpaper');
+ }
+
+ /**
+ * Define a custom context for commercial paper
+ */
+ createContext() {
+ return new CommercialPaperContext();
+ }
+
+ /**
+ * Instantiate to perform any setup of the ledger that might be required.
+ * @param {Context} ctx the transaction context
+ */
+ async instantiate(ctx) {
+ // No implementation required with this example
+ // It could be where data migration is performed, if necessary
+ console.log('Instantiate the contract');
+ }
+
+ /**
+ * Issue commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} issueDateTime paper issue date
+ * @param {String} maturityDateTime paper maturity date
+ * @param {Integer} faceValue face value of paper
+ */
+ async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
+
+ // create an instance of the paper
+ let paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, parseInt(faceValue));
+
+ // Smart contract, rather than paper, moves paper into ISSUED state
+ paper.setIssued();
+
+ // save the owner's MSP
+ let mspid = ctx.clientIdentity.getMSPID();
+ paper.setOwnerMSP(mspid);
+
+ // Newly issued paper is owned by the issuer to begin with (recorded for reporting purposes)
+ paper.setOwner(issuer);
+
+ // Add the paper to the list of all similar commercial papers in the ledger world state
+ await ctx.paperList.addPaper(paper);
+
+ // Must return a serialized paper to caller of smart contract
+ return paper;
+ }
+
+ /**
+ * Buy commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} currentOwner current owner of paper
+ * @param {String} newOwner new owner of paper
+ * @param {Integer} price price paid for this paper // transaction input - not written to asset
+ * @param {String} purchaseDateTime time paper was purchased (i.e. traded) // transaction input - not written to asset
+ */
+ async buy(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) {
+
+ // Retrieve the current paper using key fields provided
+ let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
+ let paper = await ctx.paperList.getPaper(paperKey);
+
+ // Validate current owner
+ if (paper.getOwner() !== currentOwner) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not owned by ' + currentOwner);
+ }
+
+ // First buy moves state from ISSUED to TRADING (when running )
+ if (paper.isIssued()) {
+ paper.setTrading();
+ }
+
+ // Check paper is not already REDEEMED
+ if (paper.isTrading()) {
+ paper.setOwner(newOwner);
+ // save the owner's MSP
+ let mspid = ctx.clientIdentity.getMSPID();
+ paper.setOwnerMSP(mspid);
+ } else {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not trading. Current state = ' + paper.getCurrentState());
+ }
+
+ // Update the paper
+ await ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ /**
+ * Buy request: (2-phase confirmation: Commercial paper is 'PENDING' subject to completion of transfer by owning org)
+ * Alternative to 'buy' transaction
+ * Note: 'buy_request' puts paper in 'PENDING' state - subject to transfer confirmation [below].
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} currentOwner current owner of paper
+ * @param {String} newOwner new owner of paper // transaction input - not written to asset per se - but written to block
+ * @param {Integer} price price paid for this paper // transaction input - not written to asset per se - but written to block
+ * @param {String} purchaseDateTime time paper was requested // transaction input - ditto.
+ */
+ async buy_request(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) {
+
+
+ // Retrieve the current paper using key fields provided
+ let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
+ let paper = await ctx.paperList.getPaper(paperKey);
+
+ // Validate current owner - this is really information for the user trying the sample, rather than any 'authorisation' check per se FYI
+ if (paper.getOwner() !== currentOwner) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not owned by ' + currentOwner + ' provided as a paraneter');
+ }
+ // paper set to 'PENDING' - can only be transferred (confirmed) by identity from owning org (MSP check).
+ paper.setPending();
+
+ // Update the paper
+ await ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ /**
+ * transfer commercial paper: only the owning org has authority to execute. It is the complement to the 'buy_request' transaction. '[]' is optional below.
+ * eg. issue -> buy_request -> transfer -> [buy ...n | [buy_request...n | transfer ...n] ] -> redeem
+ * this transaction 'pair' is an alternative to the straight issue -> buy -> [buy....n] -> redeem ...path
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} newOwner new owner of paper
+ * @param {String} newOwnerMSP MSP id of the transferee
+ * @param {String} confirmDateTime confirmed transfer date.
+ */
+ async transfer(ctx, issuer, paperNumber, newOwner, newOwnerMSP, confirmDateTime) {
+
+ // Retrieve the current paper using key fields provided
+ let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
+ let paper = await ctx.paperList.getPaper(paperKey);
+
+ // Validate current owner's MSP in the paper === invoking transferor's MSP id - can only transfer if you are the owning org.
+
+ if (paper.getOwnerMSP() !== ctx.clientIdentity.getMSPID()) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not owned by the current invoking Organisation, and not authorised to transfer');
+ }
+
+ // Paper needs to be 'pending' - which means you need to have run 'buy_pending' transaction first.
+ if ( ! paper.isPending()) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' is not currently in state: PENDING for transfer to occur: \n must run buy_request transaction first');
+ }
+ // else all good
+
+ paper.setOwner(newOwner);
+ // set the MSP of the transferee (so that, that org may also pass MSP check, if subsequently transferred/sold on)
+ paper.setOwnerMSP(newOwnerMSP);
+ paper.setTrading();
+ paper.confirmDateTime = confirmDateTime;
+
+ // Update the paper
+ await ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ /**
+ * Redeem commercial paper
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ * @param {String} redeemingOwner redeeming owner of paper
+ * @param {String} issuingOwnerMSP the MSP of the org that the paper will be redeemed with.
+ * @param {String} redeemDateTime time paper was redeemed
+ */
+ async redeem(ctx, issuer, paperNumber, redeemingOwner, issuingOwnerMSP, redeemDateTime) {
+
+ let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
+
+ let paper = await ctx.paperList.getPaper(paperKey);
+
+ // Check paper is not alread in a state of REDEEMED
+ if (paper.isRedeemed()) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' has already been redeemed');
+ }
+
+ // Validate current redeemer's MSP matches the invoking redeemer's MSP id - can only redeem if you are the owning org.
+
+ if (paper.getOwnerMSP() !== ctx.clientIdentity.getMSPID()) {
+ throw new Error('\nPaper ' + issuer + paperNumber + ' cannot be redeemed by ' + ctx.clientIdentity.getMSPID() + ', as it is not the authorised owning Organisation');
+ }
+
+ // As this is just a sample, can show additional verification check: that the redeemer provided matches that on record, before redeeming it
+ if (paper.getOwner() === redeemingOwner) {
+ paper.setOwner(paper.getIssuer());
+ paper.setOwnerMSP(issuingOwnerMSP);
+ paper.setRedeemed();
+ paper.redeemDateTime = redeemDateTime; // record redemption date against the asset (the complement to 'issue date')
+ } else {
+ throw new Error('\nRedeeming owner: ' + redeemingOwner + ' organisation does not currently own paper: ' + issuer + paperNumber);
+ }
+
+ await ctx.paperList.updatePaper(paper);
+ return paper;
+ }
+
+ // Query transactions
+
+ /**
+ * Query history of a commercial paper
+ * @param {Context} ctx the transaction context
+ * @param {String} issuer commercial paper issuer
+ * @param {Integer} paperNumber paper number for this issuer
+ */
+ async queryHistory(ctx, issuer, paperNumber) {
+
+ // Get a key to be used for History query
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let results = await query.getAssetHistory(issuer, paperNumber); // (cpKey);
+ return results;
+
+ }
+
+ /**
+ * queryOwner commercial paper: supply name of owning org, to find list of papers based on owner field
+ * @param {Context} ctx the transaction context
+ * @param {String} owner commercial paper owner
+ */
+ async queryOwner(ctx, owner) {
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let owner_results = await query.queryKeyByOwner(owner);
+
+ return owner_results;
+ }
+
+ /**
+ * queryPartial commercial paper - provide a prefix eg. "DigiBank" will list all papers _issued_ by DigiBank etc etc
+ * @param {Context} ctx the transaction context
+ * @param {String} prefix asset class prefix (added to paperlist namespace) eg. org.papernet.paperMagnetoCorp asset listing: papers issued by MagnetoCorp.
+ */
+ async queryPartial(ctx, prefix) {
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let partial_results = await query.queryKeyByPartial(prefix);
+
+ return partial_results;
+ }
+
+ /**
+ * queryAdHoc commercial paper - supply a custom mango query
+ * eg - as supplied as a param:
+ * ex1: ["{\"selector\":{\"faceValue\":{\"$lt\":8000000}}}"]
+ * ex2: ["{\"selector\":{\"faceValue\":{\"$gt\":4999999}}}"]
+ *
+ * @param {Context} ctx the transaction context
+ * @param {String} queryString querystring
+ */
+ async queryAdhoc(ctx, queryString) {
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let querySelector = JSON.parse(queryString);
+ let adhoc_results = await query.queryByAdhoc(querySelector);
+
+ return adhoc_results;
+ }
+
+
+ /**
+ * queryNamed - supply named query - 'case' statement chooses selector to build (pre-canned for demo purposes)
+ * @param {Context} ctx the transaction context
+ * @param {String} queryname the 'named' query (built here) - or - the adHoc query string, provided as a parameter
+ */
+ async queryNamed(ctx, queryname) {
+ let querySelector = {};
+ switch (queryname) {
+ case "redeemed":
+ querySelector = { "selector": { "currentState": 4 } }; // 4 = redeemd state
+ break;
+ case "trading":
+ querySelector = { "selector": { "currentState": 3 } }; // 3 = trading state
+ break;
+ case "value":
+ // may change to provide as a param - fixed value for now in this sample
+ querySelector = { "selector": { "faceValue": { "$gt": 4000000 } } }; // to test, issue CommPapers with faceValue <= or => this figure.
+ break;
+ default: // else, unknown named query
+ throw new Error('invalid named query supplied: ' + queryname + '- please try again ');
+ }
+
+ let query = new QueryUtils(ctx, 'org.papernet.paper');
+ let adhoc_results = await query.queryByAdhoc(querySelector);
+
+ return adhoc_results;
+ }
+
+}
+
+module.exports = CommercialPaperContract;
diff --git a/commercial-paper/organization/magnetocorp/contract/lib/paperlist.js b/commercial-paper/organization/magnetocorp/contract/lib/paperlist.js
new file mode 100644
index 0000000..73528b6
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/lib/paperlist.js
@@ -0,0 +1,35 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+
+'use strict';
+
+// Utility class for collections of ledger states -- a state list
+const StateList = require('./../ledger-api/statelist.js');
+
+const CommercialPaper = require('./paper.js');
+
+class PaperList extends StateList {
+
+ constructor(ctx) {
+ super(ctx, 'org.papernet.paper');
+ this.use(CommercialPaper);
+ }
+
+ async addPaper(paper) {
+ return this.addState(paper);
+ }
+
+ async getPaper(paperKey) {
+ return this.getState(paperKey);
+ }
+
+ async updatePaper(paper) {
+ return this.updateState(paper);
+ }
+}
+
+
+module.exports = PaperList;
diff --git a/commercial-paper/organization/magnetocorp/contract/lib/queries.js b/commercial-paper/organization/magnetocorp/contract/lib/queries.js
new file mode 100644
index 0000000..a7cd40a
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/lib/queries.js
@@ -0,0 +1,215 @@
+
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+'use strict';
+
+const State = require('../ledger-api/state.js');
+//const CommercialPaper = require('./paper.js');
+/**
+ * Query Class for query functions such as history etc
+ *
+ */
+class QueryUtils {
+
+ constructor(ctx, listName) {
+ this.ctx = ctx;
+ this.name = listName;
+ //this.supportedTypes = {};
+ }
+
+ // =========================================================================================
+ // getAssetHistory takes the composite key as arg, gets returns results as JSON to 'main contract'
+ // =========================================================================================
+ /**
+ * Get Asset History for a commercial paper
+ * @param {String} issuer the CP issuer
+ * @param {String} paperNumber commercial paper number
+ */
+ async getAssetHistory(issuer, paperNumber) {
+
+ let ledgerKey = await this.ctx.stub.createCompositeKey(this.name, [issuer, paperNumber]);
+ const resultsIterator = await this.ctx.stub.getHistoryForKey(ledgerKey);
+ let results = await this.getAllResults(resultsIterator, true);
+
+ return results;
+ }
+
+ // ===========================================================================================
+ // queryKeyByPartial performs a partial query based on the namespace and asset key prefix provided
+
+ // Read-only function results are not typically submitted to ordering. If the read-only
+ // results are submitted to ordering, or if the query is used in an update transaction
+ // and submitted to ordering, then the committing peers will re-execute to guarantee that
+ // result sets are stable between endorsement time and commit time. The transaction is
+ // invalidated by the committing peers if the result set has changed between endorsement
+ // time and commit time.
+ //
+ // ===========================================================================================
+ /**
+ * queryOwner commercial paper
+ * @param {String} assetspace the asset space (eg MagnetoCorp's assets)
+ */
+ async queryKeyByPartial(assetspace) {
+
+ if (arguments.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting 1');
+ }
+ // ie namespace + prefix to assets etc eg
+ // "Key":"org.papernet.paperMagnetoCorp0001" (0002, etc)
+ // "Partial":'org.papernet.paperlistMagnetoCorp"' (using partial key, find keys "0001", "0002" etc)
+ const resultsIterator = await this.ctx.stub.getStateByPartialCompositeKey(this.name, [assetspace]);
+ let method = this.getAllResults;
+ let results = await method(resultsIterator, false);
+
+ return results;
+ }
+
+
+ // ===== Example: Parameterized rich query =================================================
+ // queryKeyByOwner queries for assets based on a passed in owner.
+ // This is an example of a parameterized query accepting a single query parameter (owner).
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ // =========================================================================================
+ /**
+ * queryKeyByOwner commercial paper
+ * @param {String} owner commercial paper owner
+ */
+ async queryKeyByOwner(owner) {
+ //
+ let self = this;
+ if (arguments.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting owner name.');
+ }
+ let queryString = {};
+ queryString.selector = {};
+ // queryString.selector.docType = 'indexOwnerDoc';
+ queryString.selector.owner = owner;
+ // set to (eg) '{selector:{owner:MagnetoCorp}}'
+ let method = self.getQueryResultForQueryString;
+ let queryResults = await method(this.ctx, self, JSON.stringify(queryString));
+ return queryResults;
+ }
+
+ // ===== Example: Ad hoc rich query ========================================================
+ // queryAdhoc uses a query string to perform a query for marbles..
+ // Query string matching state database syntax is passed in and executed as is.
+ // Supports ad hoc queries that can be defined at runtime by the client.
+ // If this is not desired, follow the queryKeyByOwner example for parameterized queries.
+ // Only available on state databases that support rich query (e.g. CouchDB)
+ // example passed using VS Code ext: ["{\"selector\": {\"owner\": \"MagnetoCorp\"}}"]
+ // =========================================================================================
+ /**
+ * query By AdHoc string (commercial paper)
+ * @param {String} queryString actual MangoDB query string (escaped)
+ */
+ async queryByAdhoc(queryString) {
+
+ if (arguments.length < 1) {
+ throw new Error('Incorrect number of arguments. Expecting ad-hoc string, which gets stringified for mango query');
+ }
+ let self = this;
+
+ if (!queryString) {
+ throw new Error('queryString must not be empty');
+ }
+ let method = self.getQueryResultForQueryString;
+ let queryResults = await method(this.ctx, self, JSON.stringify(queryString));
+ return queryResults;
+ }
+
+ // WORKER functions are below this line: these are called by the above functions, where iterator is passed in
+
+ // =========================================================================================
+ // getQueryResultForQueryString woerk function executes the passed-in query string.
+ // Result set is built and returned as a byte array containing the JSON results.
+ // =========================================================================================
+ /**
+ * Function getQueryResultForQueryString
+ * @param {Context} ctx the transaction context
+ * @param {any} self within scope passed in
+ * @param {String} the query string created prior to calling this fn
+ */
+ async getQueryResultForQueryString(ctx, self, queryString) {
+
+ // console.log('- getQueryResultForQueryString queryString:\n' + queryString);
+
+ const resultsIterator = await ctx.stub.getQueryResult(queryString);
+ let results = await self.getAllResults(resultsIterator, false);
+
+ return results;
+
+ }
+
+ /**
+ * Function getAllResults
+ * @param {resultsIterator} iterator within scope passed in
+ * @param {Boolean} isHistory query string created prior to calling this fn
+ */
+ async getAllResults(iterator, isHistory) {
+ let allResults = [];
+ let res = { done: false, value: null };
+
+ while (true) {
+ res = await iterator.next();
+ let jsonRes = {};
+ if (res.value && res.value.value.toString()) {
+ if (isHistory && isHistory === true) {
+ //jsonRes.TxId = res.value.tx_id;
+ jsonRes.TxId = res.value.txId;
+ jsonRes.Timestamp = res.value.timestamp;
+ jsonRes.Timestamp = new Date((res.value.timestamp.seconds.low * 1000));
+ let ms = res.value.timestamp.nanos / 1000000;
+ jsonRes.Timestamp.setMilliseconds(ms);
+ if (res.value.is_delete) {
+ jsonRes.IsDelete = res.value.is_delete.toString();
+ } else {
+ try {
+ jsonRes.Value = JSON.parse(res.value.value.toString('utf8'));
+ // report the commercial paper states during the asset lifecycle, just for asset history reporting
+ switch (jsonRes.Value.currentState) {
+ case 1:
+ jsonRes.Value.currentState = 'ISSUED';
+ break;
+ case 2:
+ jsonRes.Value.currentState = 'PENDING';
+ break;
+ case 3:
+ jsonRes.Value.currentState = 'TRADING';
+ break;
+ case 4:
+ jsonRes.Value.currentState = 'REDEEMED';
+ break;
+ default: // else, unknown named query
+ jsonRes.Value.currentState = 'UNKNOWN';
+ }
+
+ } catch (err) {
+ console.log(err);
+ jsonRes.Value = res.value.value.toString('utf8');
+ }
+ }
+ } else { // non history query ..
+ jsonRes.Key = res.value.key;
+ try {
+ jsonRes.Record = JSON.parse(res.value.value.toString('utf8'));
+ } catch (err) {
+ console.log(err);
+ jsonRes.Record = res.value.value.toString('utf8');
+ }
+ }
+ allResults.push(jsonRes);
+ }
+ // check to see if we have reached the end
+ if (res.done) {
+ // explicitly close the iterator
+ console.log('iterator is done');
+ await iterator.close();
+ return allResults;
+ }
+
+ } // while true
+ }
+
+}
+module.exports = QueryUtils;
\ No newline at end of file
diff --git a/commercial-paper/organization/magnetocorp/contract/package.json b/commercial-paper/organization/magnetocorp/contract/package.json
new file mode 100644
index 0000000..2c4a644
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "papernet-js",
+ "version": "0.0.1",
+ "description": "Papernet Contract",
+ "main": "index.js",
+ "engines": {
+ "node": ">=8",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "test": "nyc mocha test --recursive",
+ "start": "fabric-chaincode-node start",
+ "mocha": "mocha test --recursive"
+ },
+ "engineStrict": true,
+ "author": "hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-contract-api": "^2.0.0",
+ "fabric-shim": "^2.0.0"
+ },
+ "devDependencies": {
+ "chai": "^4.1.2",
+ "chai-as-promised": "^7.1.1",
+ "eslint": "^4.19.1",
+ "mocha": "^5.2.0",
+ "nyc": "^12.0.2",
+ "sinon": "^6.0.0",
+ "sinon-chai": "^3.2.0"
+ },
+ "nyc": {
+ "exclude": [
+ "coverage/**",
+ "test/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/commercial-paper/organization/magnetocorp/contract/test/contract.js b/commercial-paper/organization/magnetocorp/contract/test/contract.js
new file mode 100644
index 0000000..63576ee
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/contract/test/contract.js
@@ -0,0 +1,43 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+*/
+'use strict';
+
+const Chaincode = require('../lib/chaincode');
+const { Stub } = require('fabric-shim');
+
+require('chai').should();
+const sinon = require('sinon');
+
+describe('Chaincode', () => {
+
+ describe('#Init', () => {
+
+ it('should work', async () => {
+ const cc = new Chaincode();
+ const stub = sinon.createStubInstance(Stub);
+ stub.getFunctionAndParameters.returns({ fcn: 'initFunc', params: [] });
+ const res = await cc.Init(stub);
+ res.status.should.equal(Stub.RESPONSE_CODE.OK);
+ });
+
+ });
+
+ describe('#Invoke', async () => {
+
+ it('should work', async () => {
+ const cc = new Chaincode();
+ const stub = sinon.createStubInstance(Stub);
+ stub.getFunctionAndParameters.returns({ fcn: 'initFunc', params: [] });
+ let res = await cc.Init(stub);
+ res.status.should.equal(Stub.RESPONSE_CODE.OK);
+ stub.getFunctionAndParameters.returns({ fcn: 'invokeFunc', params: [] });
+ res = await cc.Invoke(stub);
+ res.status.should.equal(Stub.RESPONSE_CODE.OK);
+ });
+
+ });
+
+});
diff --git a/commercial-paper/organization/magnetocorp/gateway/.gitkeep b/commercial-paper/organization/magnetocorp/gateway/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/commercial-paper/organization/magnetocorp/magnetocorp.sh b/commercial-paper/organization/magnetocorp/magnetocorp.sh
new file mode 100755
index 0000000..784602c
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/magnetocorp.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: Apache-2.0
+shopt -s extglob
+
+function _exit(){
+ printf "Exiting:%s\n" "$1"
+ exit -1
+}
+
+
+: ${CHANNEL_NAME:="mychannel"}
+: ${DELAY:="3"}
+: ${MAX_RETRY:="5"}
+: ${VERBOSE:="false"}
+
+# Where am I?
+DIR=${PWD}
+
+# Locate the test-network
+cd "${DIR}/../../../test-network"
+env | sort > /tmp/env.orig
+
+OVERRIDE_ORG="2"
+. ./scripts/envVar.sh
+
+
+parsePeerConnectionParameters 1 2
+export PEER_PARMS="${PEER_CONN_PARMS##*( )}"
+
+# set the fabric config path
+export FABRIC_CFG_PATH="${DIR}/../../../config"
+export PATH="${DIR}/../../../bin:${PWD}:$PATH"
+
+env | sort | comm -1 -3 /tmp/env.orig - | sed -E 's/(.*)=(.*)/export \1="\2"/'
+rm /tmp/env.orig
+
+cd ${DIR}
diff --git a/commercial-paper/organization/magnetocorp/t.js b/commercial-paper/organization/magnetocorp/t.js
new file mode 100644
index 0000000..031abd3
--- /dev/null
+++ b/commercial-paper/organization/magnetocorp/t.js
@@ -0,0 +1 @@
+console.log(process.argv);
\ No newline at end of file
diff --git a/fabcar/.gitignore b/fabcar/.gitignore
new file mode 100644
index 0000000..1e3d188
--- /dev/null
+++ b/fabcar/.gitignore
@@ -0,0 +1,3 @@
+/node_modules/
+/package-lock.json
+/hfc-key-store/
diff --git a/fabcar/go/.gitignore b/fabcar/go/.gitignore
new file mode 100755
index 0000000..076e1be
--- /dev/null
+++ b/fabcar/go/.gitignore
@@ -0,0 +1,2 @@
+wallet
+!wallet/.gitkeep
\ No newline at end of file
diff --git a/fabcar/go/fabcar.go b/fabcar/go/fabcar.go
new file mode 100644
index 0000000..0b1ffb9
--- /dev/null
+++ b/fabcar/go/fabcar.go
@@ -0,0 +1,141 @@
+/*
+Copyright 2020 IBM All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package main
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/hyperledger/fabric-sdk-go/pkg/core/config"
+ "github.com/hyperledger/fabric-sdk-go/pkg/gateway"
+)
+
+func main() {
+ os.Setenv("DISCOVERY_AS_LOCALHOST", "true")
+ wallet, err := gateway.NewFileSystemWallet("wallet")
+ if err != nil {
+ fmt.Printf("Failed to create wallet: %s\n", err)
+ os.Exit(1)
+ }
+
+ if !wallet.Exists("appUser") {
+ err = populateWallet(wallet)
+ if err != nil {
+ fmt.Printf("Failed to populate wallet contents: %s\n", err)
+ os.Exit(1)
+ }
+ }
+
+ ccpPath := filepath.Join(
+ "..",
+ "..",
+ "test-network",
+ "organizations",
+ "peerOrganizations",
+ "org1.example.com",
+ "connection-org1.yaml",
+ )
+
+ gw, err := gateway.Connect(
+ gateway.WithConfig(config.FromFile(filepath.Clean(ccpPath))),
+ gateway.WithIdentity(wallet, "appUser"),
+ )
+ if err != nil {
+ fmt.Printf("Failed to connect to gateway: %s\n", err)
+ os.Exit(1)
+ }
+ defer gw.Close()
+
+ network, err := gw.GetNetwork("mychannel")
+ if err != nil {
+ fmt.Printf("Failed to get network: %s\n", err)
+ os.Exit(1)
+ }
+
+ contract := network.GetContract("fabcar")
+
+ result, err := contract.EvaluateTransaction("queryAllCars")
+ if err != nil {
+ fmt.Printf("Failed to evaluate transaction: %s\n", err)
+ os.Exit(1)
+ }
+ fmt.Println(string(result))
+
+ result, err = contract.SubmitTransaction("createCar", "CAR10", "VW", "Polo", "Grey", "Mary")
+ if err != nil {
+ fmt.Printf("Failed to submit transaction: %s\n", err)
+ os.Exit(1)
+ }
+ fmt.Println(string(result))
+
+ result, err = contract.EvaluateTransaction("queryCar", "CAR10")
+ if err != nil {
+ fmt.Printf("Failed to evaluate transaction: %s\n", err)
+ os.Exit(1)
+ }
+ fmt.Println(string(result))
+
+ _, err = contract.SubmitTransaction("changeCarOwner", "CAR10", "Archie")
+ if err != nil {
+ fmt.Printf("Failed to submit transaction: %s\n", err)
+ os.Exit(1)
+ }
+
+ result, err = contract.EvaluateTransaction("queryCar", "CAR10")
+ if err != nil {
+ fmt.Printf("Failed to evaluate transaction: %s\n", err)
+ os.Exit(1)
+ }
+ fmt.Println(string(result))
+}
+
+func populateWallet(wallet *gateway.Wallet) error {
+ credPath := filepath.Join(
+ "..",
+ "..",
+ "test-network",
+ "organizations",
+ "peerOrganizations",
+ "org1.example.com",
+ "users",
+ "User1@org1.example.com",
+ "msp",
+ )
+
+ certPath := filepath.Join(credPath, "signcerts", "cert.pem")
+ // read the certificate pem
+ cert, err := ioutil.ReadFile(filepath.Clean(certPath))
+ if err != nil {
+ return err
+ }
+
+ keyDir := filepath.Join(credPath, "keystore")
+ // there's a single file in this dir containing the private key
+ files, err := ioutil.ReadDir(keyDir)
+ if err != nil {
+ return err
+ }
+ if len(files) != 1 {
+ return errors.New("keystore folder should have contain one file")
+ }
+ keyPath := filepath.Join(keyDir, files[0].Name())
+ key, err := ioutil.ReadFile(filepath.Clean(keyPath))
+ if err != nil {
+ return err
+ }
+
+ identity := gateway.NewX509Identity("Org1MSP", string(cert), string(key))
+
+ err = wallet.Put("appUser", identity)
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/fabcar/go/go.mod b/fabcar/go/go.mod
new file mode 100644
index 0000000..d23a5bc
--- /dev/null
+++ b/fabcar/go/go.mod
@@ -0,0 +1,5 @@
+module fabcar
+
+go 1.14
+
+require github.com/hyperledger/fabric-sdk-go v1.0.0-rc1
diff --git a/fabcar/go/go.sum b/fabcar/go/go.sum
new file mode 100644
index 0000000..4d2133c
--- /dev/null
+++ b/fabcar/go/go.sum
@@ -0,0 +1,126 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
+github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
+github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/certificate-transparency-go v0.0.0-20180222191210-5ab67e519c93 h1:qdfmdGwtm13OVx+AxguOWUTbgmXGn2TbdUHipo3chMg=
+github.com/google/certificate-transparency-go v0.0.0-20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
+github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hyperledger/fabric-lib-go v1.0.0 h1:UL1w7c9LvHZUSkIvHTDGklxFv2kTeva1QI2emOVc324=
+github.com/hyperledger/fabric-lib-go v1.0.0/go.mod h1:H362nMlunurmHwkYqR5uHL2UDWbQdbfz74n8kbCFsqc=
+github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85 h1:bNgEcCg5NVRWs/T+VUEfhgh5Olx/N4VB+0+ybW+oSuA=
+github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta1.0.20200526155846-219a09aadc0f h1:eAkJx0+8PBbfP6xZxVRD2agk9W7oDbqllxO+ERgnKJk=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta1.0.20200526155846-219a09aadc0f/go.mod h1:/s224b8NLvOJOCIqBvWd9O6u7GE33iuIOT6OfcTE1OE=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta2 h1:FBYygns0Qga+mQ4PXycyTU5m4N9KAZM+Ttf7agiV7M8=
+github.com/hyperledger/fabric-sdk-go v1.0.0-beta2/go.mod h1:/s224b8NLvOJOCIqBvWd9O6u7GE33iuIOT6OfcTE1OE=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.7.6 h1:U+1DqNen04MdEPgFiIwdOUiqZ8qPa37xgogX/sd3+54=
+github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/pkcs11 v0.0.0-20190329070431-55f3fac3af27/go.mod h1:WCBAbTOdfhHhz7YXujeZMF7owC4tPb1naKFsgfUISjo=
+github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238 h1:+MZW2uvHgN8kYvksEN3f7eFL2wpzk0GxmlFsMybWc7E=
+github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/pelletier/go-toml v1.1.0 h1:cmiOvKzEunMsAxyhXSzpL5Q1CRKpVv0KQsnAIcSEVYM=
+github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1 h1:osmNoEW2SCW3L7EX0km2LYM8HKpNWRiouxjE3XHkyGc=
+github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/procfs v0.0.0-20180705121852-ae68e2d4c00f h1:c9M4CCa6g8WURSsbrl3lb/w/G1Z5xZpYvhhjdcVDOkE=
+github.com/prometheus/procfs v0.0.0-20180705121852-ae68e2d4c00f/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/spf13/afero v1.1.0 h1:bopulORc2JeYaxfHLvJa5NzxviA9PoWhpiiJkru7Ji4=
+github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
+github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
+github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig=
+github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso=
+github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d h1:XB2jc5XQ9uhizGTS2vWcN01bc4dI6z3C4KY5MQm8SS8=
+google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/fabcar/go/runfabcar.sh b/fabcar/go/runfabcar.sh
new file mode 100755
index 0000000..1379252
--- /dev/null
+++ b/fabcar/go/runfabcar.sh
@@ -0,0 +1,15 @@
+
+ENV_DAL=`echo $DISCOVERY_AS_LOCALHOST`
+
+echo "ENV_DAL:"$DISCOVERY_AS_LOCALHOST
+
+if [ "$ENV_DAL" != "true" ]
+then
+ export DISCOVERY_AS_LOCALHOST=true
+fi
+
+echo "DISCOVERY_AS_LOCALHOST="$DISCOVERY_AS_LOCALHOST
+
+echo "run fabcar..."
+
+go run fabcar.go
diff --git a/fabcar/java/.gitignore b/fabcar/java/.gitignore
new file mode 100755
index 0000000..3aa04dc
--- /dev/null
+++ b/fabcar/java/.gitignore
@@ -0,0 +1,7 @@
+/bin/
+/target/
+.settings/
+.classpath
+.project
+wallet
+!wallet/.gitkeep
\ No newline at end of file
diff --git a/fabcar/java/pom.xml b/fabcar/java/pom.xml
new file mode 100644
index 0000000..9bdcb65
--- /dev/null
+++ b/fabcar/java/pom.xml
@@ -0,0 +1,85 @@
+
+ 4.0.0
+ fabcar-java
+ fabcar-java
+ 2.1.1
+
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.0
+
+
+
+ package
+
+ shade
+
+
+
+
+ false
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+
+
+
+
+ oss-sonatype
+ OSS Sonatype
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+
+
+ org.hyperledger.fabric
+ fabric-gateway-java
+ 2.1.1
+
+
+ org.junit.platform
+ junit-platform-launcher
+ 1.4.2
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.4.1
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ 5.4.2
+
+
+ org.assertj
+ assertj-core
+ 3.12.2
+ test
+
+
+
diff --git a/fabcar/java/src/main/java/org/example/ClientApp.java b/fabcar/java/src/main/java/org/example/ClientApp.java
new file mode 100755
index 0000000..d4c6fee
--- /dev/null
+++ b/fabcar/java/src/main/java/org/example/ClientApp.java
@@ -0,0 +1,56 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.example;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.hyperledger.fabric.gateway.Contract;
+import org.hyperledger.fabric.gateway.Gateway;
+import org.hyperledger.fabric.gateway.Network;
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+
+public class ClientApp {
+
+ static {
+ System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
+ }
+
+ public static void main(String[] args) throws Exception {
+ // Load a file system based wallet for managing identities.
+ Path walletPath = Paths.get("wallet");
+ Wallet wallet = Wallets.newFileSystemWallet(walletPath);
+ // load a CCP
+ Path networkConfigPath = Paths.get("..", "..", "test-network", "organizations", "peerOrganizations", "org1.example.com", "connection-org1.yaml");
+
+ Gateway.Builder builder = Gateway.createBuilder();
+ builder.identity(wallet, "appUser").networkConfig(networkConfigPath).discovery(true);
+
+ // create a gateway connection
+ try (Gateway gateway = builder.connect()) {
+
+ // get the network and contract
+ Network network = gateway.getNetwork("mychannel");
+ Contract contract = network.getContract("fabcar");
+
+ byte[] result;
+
+ result = contract.evaluateTransaction("queryAllCars");
+ System.out.println(new String(result));
+
+ contract.submitTransaction("createCar", "CAR10", "VW", "Polo", "Grey", "Mary");
+
+ result = contract.evaluateTransaction("queryCar", "CAR10");
+ System.out.println(new String(result));
+
+ contract.submitTransaction("changeCarOwner", "CAR10", "Archie");
+
+ result = contract.evaluateTransaction("queryCar", "CAR10");
+ System.out.println(new String(result));
+ }
+ }
+
+}
diff --git a/fabcar/java/src/main/java/org/example/EnrollAdmin.java b/fabcar/java/src/main/java/org/example/EnrollAdmin.java
new file mode 100644
index 0000000..8b3e2f5
--- /dev/null
+++ b/fabcar/java/src/main/java/org/example/EnrollAdmin.java
@@ -0,0 +1,55 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.example;
+
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.hyperledger.fabric.gateway.Identities;
+import org.hyperledger.fabric.gateway.Identity;
+import org.hyperledger.fabric.sdk.Enrollment;
+import org.hyperledger.fabric.sdk.security.CryptoSuite;
+import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
+import org.hyperledger.fabric_ca.sdk.EnrollmentRequest;
+import org.hyperledger.fabric_ca.sdk.HFCAClient;
+
+public class EnrollAdmin {
+
+ static {
+ System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ // Create a CA client for interacting with the CA.
+ Properties props = new Properties();
+ props.put("pemFile",
+ "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
+ props.put("allowAllHostNames", "true");
+ HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
+ CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
+ caClient.setCryptoSuite(cryptoSuite);
+
+ // Create a wallet for managing identities
+ Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));
+
+ // Check to see if we've already enrolled the admin user.
+ if (wallet.get("admin") != null) {
+ System.out.println("An identity for the admin user \"admin\" already exists in the wallet");
+ return;
+ }
+
+ // Enroll the admin user, and import the new identity into the wallet.
+ final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
+ enrollmentRequestTLS.addHost("localhost");
+ enrollmentRequestTLS.setProfile("tls");
+ Enrollment enrollment = caClient.enroll("admin", "adminpw", enrollmentRequestTLS);
+ Identity user = Identities.newX509Identity("Org1MSP", enrollment);
+ wallet.put("admin", user);
+ System.out.println("Successfully enrolled user \"admin\" and imported it into the wallet");
+ }
+}
diff --git a/fabcar/java/src/main/java/org/example/RegisterUser.java b/fabcar/java/src/main/java/org/example/RegisterUser.java
new file mode 100644
index 0000000..c116df4
--- /dev/null
+++ b/fabcar/java/src/main/java/org/example/RegisterUser.java
@@ -0,0 +1,111 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.example;
+
+import java.nio.file.Paths;
+import java.security.PrivateKey;
+import java.util.Properties;
+import java.util.Set;
+
+import org.hyperledger.fabric.gateway.Wallet;
+import org.hyperledger.fabric.gateway.Wallets;
+import org.hyperledger.fabric.gateway.Identities;
+import org.hyperledger.fabric.gateway.Identity;
+import org.hyperledger.fabric.gateway.X509Identity;
+import org.hyperledger.fabric.sdk.Enrollment;
+import org.hyperledger.fabric.sdk.User;
+import org.hyperledger.fabric.sdk.security.CryptoSuite;
+import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
+import org.hyperledger.fabric_ca.sdk.HFCAClient;
+import org.hyperledger.fabric_ca.sdk.RegistrationRequest;
+
+public class RegisterUser {
+
+ static {
+ System.setProperty("org.hyperledger.fabric.sdk.service_discovery.as_localhost", "true");
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ // Create a CA client for interacting with the CA.
+ Properties props = new Properties();
+ props.put("pemFile",
+ "../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem");
+ props.put("allowAllHostNames", "true");
+ HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
+ CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
+ caClient.setCryptoSuite(cryptoSuite);
+
+ // Create a wallet for managing identities
+ Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));
+
+ // Check to see if we've already enrolled the user.
+ if (wallet.get("appUser") != null) {
+ System.out.println("An identity for the user \"appUser\" already exists in the wallet");
+ return;
+ }
+
+ X509Identity adminIdentity = (X509Identity)wallet.get("admin");
+ if (adminIdentity == null) {
+ System.out.println("\"admin\" needs to be enrolled and added to the wallet first");
+ return;
+ }
+ User admin = new User() {
+
+ @Override
+ public String getName() {
+ return "admin";
+ }
+
+ @Override
+ public Set getRoles() {
+ return null;
+ }
+
+ @Override
+ public String getAccount() {
+ return null;
+ }
+
+ @Override
+ public String getAffiliation() {
+ return "org1.department1";
+ }
+
+ @Override
+ public Enrollment getEnrollment() {
+ return new Enrollment() {
+
+ @Override
+ public PrivateKey getKey() {
+ return adminIdentity.getPrivateKey();
+ }
+
+ @Override
+ public String getCert() {
+ return Identities.toPemString(adminIdentity.getCertificate());
+ }
+ };
+ }
+
+ @Override
+ public String getMspId() {
+ return "Org1MSP";
+ }
+
+ };
+
+ // Register the user, enroll the user, and import the new identity into the wallet.
+ RegistrationRequest registrationRequest = new RegistrationRequest("appUser");
+ registrationRequest.setAffiliation("org1.department1");
+ registrationRequest.setEnrollmentID("appUser");
+ String enrollmentSecret = caClient.register(registrationRequest, admin);
+ Enrollment enrollment = caClient.enroll("appUser", enrollmentSecret);
+ Identity user = Identities.newX509Identity("Org1MSP", adminIdentity.getCertificate(), adminIdentity.getPrivateKey());
+ wallet.put("appUser", user);
+ System.out.println("Successfully enrolled user \"appUser\" and imported it into the wallet");
+ }
+
+}
diff --git a/fabcar/java/src/test/java/org/example/ClientTest.java b/fabcar/java/src/test/java/org/example/ClientTest.java
new file mode 100644
index 0000000..4bf2d83
--- /dev/null
+++ b/fabcar/java/src/test/java/org/example/ClientTest.java
@@ -0,0 +1,17 @@
+/*
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package org.example;
+
+import org.junit.Test;
+
+public class ClientTest {
+
+ @Test
+ public void testFabCar() throws Exception {
+ EnrollAdmin.main(null);
+ RegisterUser.main(null);
+ ClientApp.main(null);
+ }
+}
diff --git a/fabcar/javascript/.editorconfig b/fabcar/javascript/.editorconfig
new file mode 100755
index 0000000..75a13be
--- /dev/null
+++ b/fabcar/javascript/.editorconfig
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/fabcar/javascript/.eslintignore b/fabcar/javascript/.eslintignore
new file mode 100644
index 0000000..1595847
--- /dev/null
+++ b/fabcar/javascript/.eslintignore
@@ -0,0 +1,5 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+coverage
diff --git a/fabcar/javascript/.eslintrc.js b/fabcar/javascript/.eslintrc.js
new file mode 100644
index 0000000..8b83df7
--- /dev/null
+++ b/fabcar/javascript/.eslintrc.js
@@ -0,0 +1,37 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+module.exports = {
+ env: {
+ node: true,
+ mocha: true
+ },
+ parserOptions: {
+ ecmaVersion: 8,
+ sourceType: 'script'
+ },
+ extends: "eslint:recommended",
+ rules: {
+ indent: ['error', 4],
+ 'linebreak-style': ['error', 'unix'],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'no-unused-vars': ['error', { args: 'none' }],
+ 'no-console': 'off',
+ curly: 'error',
+ eqeqeq: 'error',
+ 'no-throw-literal': 'error',
+ strict: 'error',
+ 'no-var': 'error',
+ 'dot-notation': 'error',
+ 'no-tabs': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-use-before-define': 'error',
+ 'no-useless-call': 'error',
+ 'no-with': 'error',
+ 'operator-linebreak': 'error',
+ yoda: 'error',
+ 'quote-props': ['error', 'as-needed']
+ }
+};
diff --git a/fabcar/javascript/.gitignore b/fabcar/javascript/.gitignore
new file mode 100644
index 0000000..1dcaf1f
--- /dev/null
+++ b/fabcar/javascript/.gitignore
@@ -0,0 +1,80 @@
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
+
+wallet
+!wallet/.gitkeep
diff --git a/fabcar/javascript/EncryptDecrypt-v2.js b/fabcar/javascript/EncryptDecrypt-v2.js
new file mode 100644
index 0000000..df125dc
--- /dev/null
+++ b/fabcar/javascript/EncryptDecrypt-v2.js
@@ -0,0 +1,274 @@
+const crypto = require("crypto")
+const fs = require('fs');
+
+// Including generateKeyPair from crypto module
+const { generateKeyPair } = require('crypto');
+
+// first we need two keys here - index key and private key. Private key will
+// be generated only once and then somehow store it
+
+let indexKey = "9uZlGXa0o64kdbQ6Gb96qw=="
+
+let publicKeyPath = './keys/publicKey.key'
+let privateKeyPath = './keys/privateKey.key'
+
+
+// Calling generateKeyPair() method
+// with its parameters
+exports.generateKeys = () => {
+
+ return new Promise( (resolve, reject) => {
+
+ generateKeyPair('rsa', {
+ modulusLength: 2048, // options
+ publicExponent: 0x10101,
+ publicKeyEncoding: {
+ type: 'pkcs1',
+ format: 'pem'
+ },
+ privateKeyEncoding: {
+ type: 'pkcs8',
+ format: 'pem',
+ cipher: 'aes-192-cbc',
+ passphrase: 'sse'
+ }
+ }, (err, PublicKey, PrivateKey) => { // Callback function
+ if(!err)
+ {
+ fs.writeFile(publicKeyPath, PublicKey.toString('hex'), (er) => {
+ if (er) console.log(er);
+ console.log("Key Successfully Saved.");
+ });
+
+ fs.writeFile(privateKeyPath, PrivateKey.toString('hex'), (er) => {
+ if (er) console.log(er);
+ console.log("Key Successfully Saved.");
+ });
+ resolve();
+
+ }
+ else
+ {
+ // Prints error if any
+ console.log("Errr is: ", err);
+ reject();
+ }
+ });
+
+ })
+
+};
+
+
+exports.getKeywordHash = (keyword) => {
+ let keyword_hash = crypto.createHmac('sha256', indexKey)
+ .update(keyword)
+ .digest('hex')
+ return keyword_hash
+ };
+
+exports.getEncryptionKeyword = (pbKey, keyword) => {
+ //Also pass publicKey.key the file here as a parameter
+ //let pbKey = fs.readFileSync('./keys/publicKey.key');
+
+ const encryptedKeyword = crypto.publicEncrypt(
+ {
+ key: pbKey,
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
+ oaepHash: "sha256",
+ },
+
+ Buffer.from(keyword)
+ )
+
+ //console.log("encypted data: ", encryptedKeyword.toString('hex'));
+ return encryptedKeyword.toString('hex');
+}
+
+
+exports.getDecryptionKeyword = (encryptedKeyword) => {
+ let prKey = fs.readFileSync('./keys/privateKey.key');
+
+ const keyword = crypto.privateDecrypt(
+ {
+ key: prKey,
+ passphrase: 'sse',
+ padding: crypto.constants.RSA_PKCS8_OAEP_PADDING,
+ oaepHash: "sha256",
+ },
+ Buffer.from(encryptedKeyword, 'hex')
+ )
+
+ //console.log("decrypted data: ", keyword.toString());
+ return keyword.toString();
+};
+
+exports.getEncryptFile = (pbKey, filePath) => {
+ //Store publicKey.key as key file
+ //Also pass publicKey.key the file here as a parameter
+ //let pbKey = fs.readFileSync('./keys/publicKey.key');
+
+ fs.readFile(filePath, "utf-8", (err, data) => {
+
+ const encryptedData = crypto.publicEncrypt(
+ {
+ key: pbKey,
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
+ oaepHash: "sha256",
+ },
+
+ Buffer.from(data)
+ )
+
+ fs.writeFile(filePath, encryptedData, (error) => {
+ if (error) console.log(error);
+ //console.log(encryptedData);
+ console.log("File Successfully Encrypted.");
+ });
+ });
+};
+
+//It is for encrypting owner's file data with requester's public key -also output is in HEX
+exports.getEncryptFileV2 = (pbKey, filePath ) => {
+ return new Promise( (resolve) => {
+
+ //Store publicKey.key as key file
+ //Also pass publicKey.key the file here as a parameter
+ //let pbKey = fs.readFileSync('./keys/publicKey.key');
+ let againEncryptedfileP = './temp-file/againEncryptFile.txt';
+ console.log("Inside encryptFileV2: ");
+ console.log("file data: ", fs.readFileSync(filePath).toString('hex'));
+ fs.readFile(filePath, "utf-8" , (err, data) => {
+
+
+
+ const encryptedData = crypto.publicEncrypt(
+ {
+ key: pbKey,
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
+ oaepHash: "sha256",
+ },
+
+ Buffer.from(data)
+ )
+
+ fs.writeFile(againEncryptedfileP, encryptedData.toString('hex'), (error) => {
+ if (error) console.log(error);
+ console.log("File Successfully Encrypted.");
+ resolve(againEncryptedfileP);
+ });
+ //resolve(againEncryptedfileP);
+ });
+ });
+};
+
+exports.getDecryptFile = (filePath) => {
+ return new Promise( (resolve) => {
+
+ const plainDataFileP = './temp-file/decryptedFile.txt';
+ //let prKey = fs.readFileSync('./keys/privateKey.key'); //toString??
+ fs.readFile('./keys/privateKey.key', 'utf8', (err, prKey)=> {
+
+ console.log('In the getDecryptFile(). ');
+ console.log(filePath);
+
+ const tempPath = './public/files/'+ filePath;
+
+ fs.readFile(tempPath, (err, encryptedData) => {
+
+ const decryptedData = crypto.privateDecrypt(
+ {
+ key: prKey,
+ passphrase: 'sse',
+ padding: crypto.constants.RSA_PKCS8_OAEP_PADDING,
+ oaepHash: "sha256",
+ },
+ encryptedData
+ )
+ fs.writeFile(plainDataFileP, decryptedData , (error) => {
+ console.log('In the getDecryptFile(). writeFile ');
+ if (error) console.log(error);
+ console.log(decryptedData);
+ console.log("Successfully decrypted.");
+ resolve(plainDataFileP);
+ });
+ //resolve(plainDataFileP);
+ });
+ });
+ });
+ //return plainDataFilePath;
+};
+
+exports.getDecryptFileContent = (fileContent) => {
+ return new Promise((resolve)=> {
+ console.log("File Content: "+ fileContent);
+ fs.readFile('./keys/privateKey.key', 'utf8', (err, prKey) => {
+ console.log('prKey = ', prKey, typeof(prKey));
+
+ const plainData = crypto.privateDecrypt(
+ {
+ key: prKey,
+ passphrase: 'sse',
+ padding: crypto.constants.RSA_PKCS8_OAEP_PADDING,
+ oaepHash: "sha256",
+ },
+ Buffer.from(fileContent,'hex')
+ )
+
+ //console.log("decrypted data: ", keyword.toString());
+ resolve(plainData.toString() );
+ })
+ });
+};
+
+
+exports.generateNonce = () => {
+ return new Promise((resolve)=> {
+
+ crypto.randomInt(0, 1000000, (err, n) => {
+ if (err) throw err;
+
+ resolve(n.toString());
+ })
+ });
+};
+
+exports.getEncryptNonce = (pbKey, plainNonce) => {
+ return new Promise( (resolve) => {
+ console.log("At Nonce Encrypt: plainNonce: " + plainNonce +" tpy: "+ typeof(plainNonce));
+
+ const encryptedNonce = crypto.publicEncrypt(
+ {
+ key: pbKey,
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
+ oaepHash: "sha256",
+ },
+
+ Buffer.from(plainNonce)
+ )
+ console.log("At Nonce Encrypt: encryptNonce: ", encryptedNonce.toString('hex') + " typ: " + typeof(encryptedNonce.toString('hex')));
+ resolve(encryptedNonce.toString('hex'));
+ });
+};
+
+exports.getDecryptNonce = (encryptedNonce) => {
+ return new Promise( (resolve) => {
+ console.log("At Nonce decrypt: encryptedNonce: " + encryptedNonce +" tpy: "+ typeof(encryptedNonce));
+ console.log("At Nonce decrypt: encryptedNonce: buffer convert " + Buffer.from(encryptedNonce, 'hex') +" tpy: "+ typeof(Buffer.from(encryptedNonce, 'hex')));
+
+ fs.readFile('./keys/privateKey.key', 'utf8', (err, prKey)=> {
+
+ const decryptedNonce = crypto.privateDecrypt(
+ {
+ key: prKey,
+ passphrase: 'sse',
+ padding: crypto.constants.RSA_PKCS8_OAEP_PADDING,
+ oaepHash: "sha256",
+ },
+ Buffer.from(encryptedNonce, 'hex') //should use buffer???
+ )
+ console.log("At Nonce decrypt: decryptedNonce: " + decryptedNonce.toString() +" tpy: "+ typeof(decryptedNonce.toString()));
+ resolve(decryptedNonce.toString());
+ });
+ });
+}
\ No newline at end of file
diff --git a/fabcar/javascript/app.js b/fabcar/javascript/app.js
new file mode 100644
index 0000000..7c42ac0
--- /dev/null
+++ b/fabcar/javascript/app.js
@@ -0,0 +1,142 @@
+const express = require('express')
+const bodyParser = require('body-parser')
+const path = require('path')
+const multer = require('multer');
+const mongoose = require('mongoose');
+
+const app = express()
+const port = 5000;
+
+
+///
+const session = require('express-session');
+const MongoDBStore = require('connect-mongodb-session')(session);
+const MONGODB_URI = "mongodb+srv://arnobkumarsaha:sustcse16@cluster0.nj6lk.mongodb.net/searchableEncryption?retryWrites=true&w=majority";
+
+const csrf = require('csurf'); // to prevent csrf attack
+const flash = require('connect-flash'); // to help users by showing what error occured.
+
+const User = require('./models/user');
+
+
+const encDec = require('./EncryptDecrypt-v2');
+const fs = require('fs');
+
+
+const fileStorage = multer.diskStorage({
+ destination: (req, file, cb) => {
+ cb(null, 'public/files');
+ },
+ filename: (req, file, cb) => {
+ cb(null, new Date().toISOString() + '-*-' + file.originalname);
+ }
+});
+
+//encDec.getEncryptFile('rosalind.txt'/* '2021-05-27T18:21:22.710Z-*-solive.txt' */);
+
+app.set("view engine", "ejs");
+app.use(express.urlencoded({ extended: false }));
+app.use(bodyParser.urlencoded({ extended: false }));
+app.use(bodyParser.json());
+
+app.use(multer({storage: fileStorage}).single('file'));
+app.use(express.static(path.join(__dirname, "public")));
+
+
+// Specifying the session storage & then use the middleware
+const store = new MongoDBStore({
+ uri: MONGODB_URI,
+ collection: 'sessions'
+});
+const csrfProtection = csrf();
+
+
+// session, crsf(cross site request forgery) & flash Middlewares
+app.use(
+ session({
+ httpOnly: false,
+ secret: 'my secret',
+ resave: false,
+ saveUninitialized: false,
+ store: store
+ })
+);
+app.use(csrfProtection);
+app.use(flash());
+
+
+
+// Check if the requester is authenticated or not
+app.use((req, res, next) => {
+ if (!req.session.user) {
+ return next();
+ }
+ User.findById(req.session.user._id)
+ .then(user => {
+ req.user = user;
+ next();
+ })
+ .catch(err => console.log(err));
+});
+
+// res.locals variables are passed to every views.
+app.use((req, res, next) => {
+ res.locals.isAuthenticated = req.session.isLoggedIn;
+ res.locals.csrfToken = req.csrfToken();
+
+ if(!req.user){
+ res.locals.profileName = "";
+ }
+ else res.locals.profileName = req.user.name;
+ next();
+});
+
+
+
+
+// About Routes.
+var usersRouter = require("./routes/user");
+var indexRouter = require("./routes/index");
+
+const errorController = require('./controllers/error');
+
+/*
+app.get('',(req,res) =>{
+ res.render('index')
+})
+app.get('/form',(req,res) =>{
+ res.render('form')
+})*/
+
+/*
+app.post('/form', urlencodedParser, (req,res) => {
+ res.json(req.body)
+})*/
+
+
+app.use("/user", usersRouter);
+
+app.use("/", indexRouter);
+
+app.use(errorController.get404);
+
+
+mongoose
+ .connect(
+ MONGODB_URI,
+ {
+ useNewUrlParser: true,
+ useUnifiedTopology: true
+ }
+ )
+ .then(result =>{
+ app.listen(port, ()=> console.info("App listening on port: " , port))
+ console.log("Yesss ! MongoDb is connected.");
+ })
+ .catch(err =>{
+ console.log(err);
+ });
+
+
+
+// app.listen(port, ()=> console.info("App listening on port: " , port))
\ No newline at end of file
diff --git a/fabcar/javascript/bob.txt b/fabcar/javascript/bob.txt
new file mode 100644
index 0000000..58200e3
--- /dev/null
+++ b/fabcar/javascript/bob.txt
@@ -0,0 +1 @@
+Now Bob sends a msg to Alice!!!!
diff --git a/fabcar/javascript/controllers/error.js b/fabcar/javascript/controllers/error.js
new file mode 100644
index 0000000..376d091
--- /dev/null
+++ b/fabcar/javascript/controllers/error.js
@@ -0,0 +1,7 @@
+exports.get404 = (req, res, next) => {
+ res.status(404).render('404', {
+ pageTitle: 'Page Not Found',
+ path: '/404'
+ });
+ };
+
\ No newline at end of file
diff --git a/fabcar/javascript/controllers/index.js b/fabcar/javascript/controllers/index.js
new file mode 100644
index 0000000..7b7d04b
--- /dev/null
+++ b/fabcar/javascript/controllers/index.js
@@ -0,0 +1,278 @@
+const bcrypt = require('bcryptjs');
+
+const { validationResult } = require('express-validator');
+
+const User = require('../models/user');
+
+const encDec = require('../EncryptDecrypt-v2');
+
+const fs = require('fs');
+const path = require('path');
+
+let publicKeyPath = path.join(__dirname, '..', 'keys', 'publicKey.key');
+
+const init = require('../util');
+
+
+
+exports.getFrontPage = (req,res, next) => {
+ res.redirect("/user");
+}
+
+exports.getLogin = (req,res, next) => {
+ let message = req.flash('error');
+ if (message.length > 0) { // if there is an error-flash
+ message = message[0];
+ } else {
+ message = null;
+ }
+ res.render('auth/login', {
+ path: '/login',
+ pageTitle: 'Login Page',
+ errorMessage: message,
+ oldInput: {
+ email: '',
+ password: ''
+ },
+ validationErrors: []
+ });
+}
+
+function loginHelper(req,res,errors){
+ res.status(422).render('auth/login', {
+ path: '/login',
+ pageTitle: 'Login',
+ errorMessage: errors.array()[0].msg,
+ oldInput: {
+ email: req.body.email,
+ password: req.body.password,
+ typeOfUser: req.body.typeOfUser
+ },
+ validationErrors: errors.array()
+ });
+ };
+
+exports.postLogin = (req,res, next) => {
+ const email = req.body.email;
+ const password = req.body.password;
+
+ const errors = validationResult(req);
+ // if there are errors , render the same page (with user-entered info.)
+ if (!errors.isEmpty()) {
+ return loginHelper(req,res,errors);
+ }
+
+ User.findOne({ email: email })
+ .then(user => {
+ // user exists with this email , bcz we checked it already in auth route.
+ bcrypt
+ .compare(password, user.password)
+ .then(doMatch => {
+ if (doMatch) { // password validation
+ req.session.isLoggedIn = true;
+ req.session.user = user;
+ return req.session.save(err => {
+ console.log(err);
+ res.redirect('/');
+ });
+ }
+ // Incorrect password entered.
+
+ return res.status(422).render('auth/login', {
+ path: '/login',
+ pageTitle: 'Login Page',
+ errorMessage: 'Invalid email or password',
+ oldInput: {
+ email: req.body.email,
+ password: req.body.password
+ },
+ validationErrors: errors.array()
+ });
+ })
+ .catch(err => {
+ console.log(err);
+ res.redirect('/login');
+ });
+ })
+ .catch(err => console.log(err));
+}
+
+
+exports.getSignup = (req,res, next) => {
+ let message = req.flash('error');
+ if (message.length > 0) {
+ message = message[0];
+ } else {
+ message = null;
+ }
+ res.render('auth/signup', {
+ path: '/signup',
+ pageTitle: 'Signup',
+ errorMessage: message,
+ oldInput: { // for better user experience... So that user don't have to enter all the fields again.
+ email: '',
+ password: '',
+ confirmPassword: '',
+ name: ''
+ },
+ validationErrors: []
+ });
+}
+
+// Make a function in index.js controller.
+function fudai(){
+ return new Promise((resolve, reject) => {
+ fs.readFile(publicKeyPath, 'utf8', (err, data)=> {
+ console.log('Inside fudai.');
+ resolve(data);
+ });
+ });
+}
+
+// postSignup function from two-host branch
+/*
+exports.postSignup = async (req,res, next) => {
+ const email = req.body.email;
+ const password = req.body.password;
+ const confirmPassword = req.body.confirmPassword;
+ const name = req.body.name;
+
+ const errors = validationResult(req);
+ // if there are errors , render the same page (with user-entered info.)
+ if (!errors.isEmpty()) {
+ return res.status(422).render('auth/signup', {
+ path: '/signup',
+ pageTitle: 'Signup',
+ errorMessage: errors.array()[0].msg,
+ oldInput: {
+ email: email,
+ password: password,
+ confirmPassword: confirmPassword,
+ name: name
+ },
+ validationErrors: errors.array()
+ });
+ }
+ // If no error, encrypt the password, and save the user into database.
+ const hashedPassword = await bcrypt.hash(password, 12);
+
+ await encDec.generateKeys();
+
+ let pbKey = await fudai();
+ //let pbKey = await encDec.generateKeys();
+
+ console.log(pbKey, typeof(pbKey) ); // This is buffer Object
+
+ const user = new User({
+ email: email,
+ password: hashedPassword,
+ name: name,
+ cart: {
+ myFiles: []
+ },
+ reqs: {
+ notifications: []
+ },
+ publicKey: pbKey
+ });
+
+ try{
+ const result = await user.save();
+ res.redirect('/login');
+ }
+ catch(err){
+ console.log(err);
+ };
+}
+*/
+
+
+//FABRIC PART STARTS
+exports.postSignup = async (req,res, next) => {
+ const email = req.body.email;
+ const password = req.body.password;
+ const confirmPassword = req.body.confirmPassword;
+ const name = req.body.name;
+
+ const errors = validationResult(req);
+ // if there are errors , render the same page (with user-entered info.)
+ if (!errors.isEmpty()) {
+ return res.status(422).render('auth/signup', {
+ path: '/signup',
+ pageTitle: 'Signup',
+ errorMessage: errors.array()[0].msg,
+ oldInput: {
+ email: email,
+ password: password,
+ confirmPassword: confirmPassword,
+ name: name
+ },
+ validationErrors: errors.array()
+ });
+ }
+
+ // If no error, encrypt the password, and save the user into database.
+ const hashedPassword = await bcrypt.hash(password, 12);
+
+ await encDec.generateKeys();
+
+ let pbKey = await fudai();
+ //let pbKey = await encDec.generateKeys();
+
+ console.log(pbKey, typeof(pbKey) ); // This is buffer Object
+
+ const user = new User({
+ email: email,
+ password: hashedPassword,
+ name: name,
+ cart: {
+ myFiles: []
+ },
+ reqs: {
+ notifications: []
+ },
+ publicKey: pbKey
+ });
+
+ try{
+ const result = await user.save();
+
+ // =========================================================================================
+ // FABRIC PART STARTS
+ // =========================================================================================
+ try {
+ var contract = (await init).getInput();
+
+ var ct = contract.contact;
+ var way = contract.gateway;
+ // Submit the specified transaction
+ await ct.submitTransaction(
+ "Register", //must declare in capital letter in both js and Go
+ result._id,
+ email,
+ name
+ );
+ //await ct.submitTransaction('CreateMed', 'MED12', 'Beximco', 'NapaVX', 'fever,common cold and influenza.');
+
+ console.log("UserRegister Transaction has been submitted");
+ // res.redirect("/login");
+ } catch (error) {
+ console.error(`Failed to submit UserRegister transaction: ${error}`);
+ process.exit(1);
+ }
+ // =========================================================================================
+ // FABRIC PART ENDS
+ // =========================================================================================
+
+ res.redirect('/login');
+ }
+ catch(err){
+ console.log(err);
+ };
+}
+exports.postLogout = (req, res, next) => {
+ req.session.destroy(err => {
+ console.log(err);
+ res.redirect('/');
+ });
+};
diff --git a/fabcar/javascript/controllers/userShowReq.js b/fabcar/javascript/controllers/userShowReq.js
new file mode 100644
index 0000000..20d485f
--- /dev/null
+++ b/fabcar/javascript/controllers/userShowReq.js
@@ -0,0 +1,417 @@
+const User = require('../models/user');
+const KeywordIndex = require('../models/keyword_index');
+const File = require('../models/file');
+
+const path = require('path');
+const fs = require('fs');
+const encDec = require('../EncryptDecrypt-v2');
+
+const { ObjectID } = require('bson');
+const documents = require('../controllers/userUpDownDel').getDocuments();
+
+
+const init = require('../util');
+
+exports.showFileById = (req, res, next) =>{
+ const fileId = req.params.myFileId;
+
+ console.log("showFileById : fileId = ", fileId);
+
+ File.findById(ObjectID(fileId)).then((theFile) => {
+ const filePath = theFile.filePath;
+ let content;
+
+ fs.readFile(path.dirname(process.mainModule.filename) + '/public/files/'+filePath, 'utf8' , (err, data) => {
+ if (err) {
+ console.error(err);
+ return;
+ }
+ console.log(data);
+
+ res.render("updown/showFile", {
+ pageTitle: "Show File",
+ path: "/user/uploaded",
+ fileContent: data
+ });
+ });
+ })
+}
+
+exports.requestFile = async (req, res, next) =>{
+ const ownerId = req.params.ownerId;
+ const fileName = req.params.fileName; // the file path actually (without /public/files.)
+ console.log("requesting file begins");
+
+
+
+ //If it is his file.
+ if(req.user._id == ownerId){
+ return res.render('updown/searchResult', {
+ pageTitle: "Search result",
+ path: "/user/searchResult",
+ docs: documents,
+ errorMessage: "This is your file man! "
+ });
+ }
+
+
+ const theFile = await File.findOne({filePath: fileName});
+ let owner = await User.findOne({_id: ownerId});
+
+
+
+
+ //here is pushing nonce --starts
+ let nonce = await encDec.generateNonce(); //use random number or string -- but output must be in string
+ const updatedRequestedItems = [...req.user.dcart.allRequests];
+ updatedRequestedItems.push({
+ isAccept: 0,
+ ownerId: ownerId,
+ requestedFileId: theFile._id,
+ noncePlain: encDec.getKeywordHash(nonce),
+ fileContent: null,
+ nonceGet: null
+ });
+ const updatedAllReqs = {
+ allRequests: updatedRequestedItems
+ };
+ //console.log(updatedAllReqs);
+ req.user.dcart = updatedAllReqs;
+ await req.user.save();
+ //here is pushing nonce --ends
+
+
+
+ // Update the Owner's notification array.
+ const updatedNotificationItems = [...owner.reqs.notifications];
+ console.log("theFile = ", theFile, "owner = ", owner, "updatedNoti = ", updatedNotificationItems);
+ updatedNotificationItems.push({
+ requesterId: req.user._id,
+ requestedFileId: theFile._id,
+ decided: false,
+ nonce: await encDec.getEncryptNonce(owner.publicKey, nonce)
+ });
+ const updatedReqs = {
+ notifications: updatedNotificationItems
+ };
+ console.log("updated noti = ", updatedNotificationItems, "updated reqs = ", updatedReqs);
+ owner.reqs = updatedReqs;
+ owner = await owner.save();
+
+ console.log('Done with reques file.');
+
+
+ // =========================================================================================
+ // FABRIC PART STARTS
+ // =========================================================================================
+ try {
+ var contract = (await init).getInput();
+
+ var ct = contract.contact;
+ var way = contract.gateway;
+ // Submit the specified transaction
+ await ct.submitTransaction(
+ "FileRequestNotification", //must declare in capital letter in both js and Go
+ theFile._id,
+ owner._id,
+ req.user._id,
+ 'queued'
+ );
+ //await ct.submitTransaction('CreateMed', 'MED12', 'Beximco', 'NapaVX', 'fever,common cold and influenza.');
+
+ console.log(" Request-for-file-transaction has been submitted");
+ // res.redirect("/login");
+ } catch (error) {
+ console.error(`Failed to submit Request-for-file-transaction: ${error}`);
+ process.exit(1);
+ }
+ // =========================================================================================
+ // FABRIC PART ENDS
+ // =========================================================================================
+
+
+
+ // Show the search results to the User.
+ res.render('updown/searchResult', {
+ pageTitle: "Search result",
+ path: "/user/searchResult",
+ docs: documents,
+ errorMessage: "Requset has been sent to the DataOwner. "
+ });
+ }
+
+function fudai(p){
+ return new Promise((resolve, reject) => {
+ fs.readFile(p, (err, data)=> { //can not be utf8
+ console.log('Inside fudai.. data:'+ data.toString());
+ resolve(data);
+ });
+ });
+}
+
+function findNonce(notifications,requesterId,requestedFileId){
+ return new Promise((resolve) => {
+ for(let item of notifications){
+ console.log('item -> ', item);
+ if(item.requesterId.equals(requesterId) && item.requestedFileId.equals(requestedFileId) && item.decided == false){
+ console.log('Nonce -> ', item.nonce);
+ resolve(item.nonce);
+ }
+ }
+ });
+}
+
+exports.grantPermission = async (req, res, next) =>{
+ const requesterId = req.params.requesterId;
+ const requestedFileId = req.params.requestedFileId;
+ const theFile = await File.findOne({_id: requestedFileId});
+ const requester = await User.findOne({_id: requesterId});
+ console.log("theFile = ", theFile, "requester = ", requester, "requester public key = ", requester.publicKey);
+
+
+
+
+ //const plainDataFilePath = await encDec.getDecryptFile(theFile.filePath);
+ //await encDec.getEncryptFileV2(requester.publicKey.toString(), plainDataFilePath);
+ const plainDataFilePath = await encDec.getDecryptFile(theFile.filePath);
+ const fpath = await encDec.getEncryptFileV2(requester.publicKey, plainDataFilePath);
+
+
+
+ // =========================================================================================
+ // FABRIC PART STARTS
+ // =========================================================================================
+ try {
+ var contract = (await init).getInput();
+
+ var ct = contract.contact;
+ var way = contract.gateway;
+ // Submit the specified transaction
+ await ct.submitTransaction(
+ "FileRequestAccept", //must declare in capital letter in both js and Go
+ theFile._id,
+ req.user._id,
+ requester._id,
+ 'accepted'
+ );
+ //await ct.submitTransaction('CreateMed', 'MED12', 'Beximco', 'NapaVX', 'fever,common cold and influenza.');
+
+ console.log(" Accept-request-transaction has been submitted");
+ // res.redirect("/login");
+ } catch (error) {
+ console.error(`Failed to submit Accept-request-transaction: ${error}`);
+ process.exit(1);
+ }
+ // =========================================================================================
+ // FABRIC PART ENDS
+ // =========================================================================================
+
+
+
+
+ //let encryptedContent = await fudai(fpath);
+
+ //for nonce
+ //just for test:
+ //let N = await encDec.getDecryptNonce(await encDec.getEncryptNonce(req.user.publicKey, "122"));
+ //console.log("Nonce Testing: " + N);
+
+ let currentUser = await User.findOne({_id: req.user._id});
+ const notifications = [...currentUser.reqs.notifications];
+ const nonce = await findNonce(notifications, requesterId, requestedFileId);
+
+ let againEncryptedNonce = await encDec.getEncryptNonce(requester.publicKey, await encDec.getDecryptNonce(nonce));
+
+ //object already EXISTS......Dont push UPDATE::::STARTS
+ const queryRequest = {
+ 'dcart.allRequests': {
+ $elemMatch: {
+ isAccept: 0,
+ ownerId: req.user._id,
+ requestedFileId: requestedFileId
+ }
+ }
+ };
+ console.log("Result for queryRequest: ", await User.findOne(queryRequest));
+ await User.update(queryRequest, {$set: {
+ 'dcart.allRequests.$.isAccept': 2,
+ 'dcart.allRequests.$.fileContent': await fudai(fpath),
+ 'dcart.allRequests.$.nonceGet': againEncryptedNonce
+ }} );
+//object already EXISTS......Dont push UPDATE::::ENDS
+
+
+
+//cmnt starts here for nonce addinf purpose
+/* const updatedRequestedItems = [...requester.dcart.allRequests];
+
+ updatedRequestedItems.push({
+ isAccept: 2,
+ ownerId: req.user._id,
+ requestedFileId: theFile._id,
+ nonceSent: againEncryptedNonce,
+ fileContent: await fudai(fpath) //encryptedContent
+ });
+ const updatedAllReqs = {
+ allRequests: updatedRequestedItems
+ };
+ //console.log(updatedAllReqs);
+ requester.dcart = updatedAllReqs;
+ await requester.save();
+ */
+//cmt ends here --- for nonce adding purpuse
+
+ console.log(requester);
+
+ //need to change
+
+ /*
+ let currentUser = await User.findOne({_id: req.user._id});
+ const updatedNotificationItems = [...currentUser.reqs.notifications];
+ let itemToBeSaved;
+ for(let item of updatedNotificationItems){
+ console.log('item -> ', item);
+ if(item.requesterId.equals(requesterId) && item.requestedFileId.equals(requestedFileId)){
+ item.decided = true;
+ itemToBeSaved = item;
+ break;
+ }
+ }
+ */
+
+ const query = {
+ 'reqs.notifications': {
+ $elemMatch: {
+ requesterId: requesterId,
+ requestedFileId: requestedFileId
+ }
+ }
+ };
+
+
+ await User.updateOne(query, {$set: {
+ 'reqs.notifications.$.decided': true
+ }} );
+
+ console.log('Done with Grant Permission.');
+ res.redirect('/user/notification');
+}
+
+exports.denyPermission = async (req, res, next) =>{
+ const requesterId = req.params.requesterId;
+ const requestedFileId = req.params.requestedFileId;
+
+ const requester = await User.findOne({_id: requesterId});
+
+ const updatedRequestedItems = [...requester.dcart.allRequests];
+
+ //for nonce
+ let currentUser = await User.findOne({_id: req.user._id});
+ const notifications = [...currentUser.reqs.notifications];
+ const nonce = await findNonce(notifications, requesterId, requestedFileId);
+
+ let againEncryptedNonce = await encDec.getEncryptNonce(requester.publicKey, await encDec.getDecryptNonce(nonce));
+
+ //object already EXISTS......Dont push UPDATE::::STARTS
+ const queryRequest = {
+ 'dcart.allRequests': {
+ $elemMatch: {
+ isAccept: 0,
+ ownerId: req.user._id,
+ requestedFileId: requestedFileId
+ }
+ }
+ };
+ console.log("Result for queryRequest: ", await User.findOne(queryRequest));
+ await User.update(queryRequest, {$set: {
+ 'dcart.allRequests.$.isAccept': 1,
+ 'dcart.allRequests.$.nonceGet': againEncryptedNonce
+ }} );
+ //object already EXISTS......Dont push UPDATE::::ENDS
+
+ // For using Nonce - we dont push already added object - we update it -- start
+ //console.log(updatedRequestedItems);
+/* updatedRequestedItems.push({
+ isAccept: 1,
+ ownerId: req.user._id,
+ nonceSent: againEncryptedNonce
+ });
+ const updatedAllReqs = {
+ allRequests: updatedRequestedItems
+ };
+
+ requester.dcart = updatedAllReqs;
+ await requester.save(); */
+ // For using Nonce - we dont push already added object - we update it -- end
+
+ console.log('\n', requester, '\n');
+
+ //need to change
+ const query = {
+ 'reqs.notifications': {
+ $elemMatch: {
+ requesterId: requesterId,
+ requestedFileId: requestedFileId
+ }
+ }
+ };
+ await User.updateOne(query, {$set: {
+ 'reqs.notifications.$.decided': true
+ }} );
+
+ console.log('Done with Deny Permission.');
+ res.redirect('/user/notification');
+}
+
+exports.getAllNotifications = (req, res, next) =>{
+ User.findOne({_id: req.user._id})
+ .then(user =>{
+ const notifications = user.reqs.notifications;
+ res.render("updown/notification",{
+ pageTitle: "Notifications",
+ path: "/user/notification",
+ notifications: notifications
+ })
+ })
+}
+
+exports.getAllRequests = (req, res, next) =>{
+ User.findOne({_id: req.user._id})
+ .then(user =>{
+ const requests = user.dcart.allRequests;
+ res.render("updown/request",{
+ pageTitle: "All Requests",
+ path: "/user/request",
+ requests: requests
+ })
+ })
+}
+
+function verifyNonce(noncePlain, plainNonce){
+ let verify = false;
+ if(JSON.stringify(noncePlain) == JSON.stringify(encDec.getKeywordHash(plainNonce))){
+ verify = true;
+ }
+ return verify;
+}
+
+exports.showDecryptedFileContent = async(req, res, next) =>{
+ const content = req.params.fileContent;
+ const noncePlain = req.params.noncePlain;
+ const nonceGet = req.params.nonceGet;
+ //console.log(fs.readFileSync('./public/files/rosalind.txt').toString());
+ //console.log(nonceGet);
+
+ let plainNonce = await encDec.getDecryptNonce(nonceGet);
+ let verify = verifyNonce(noncePlain, plainNonce);
+ console.log(verify);
+
+ const plainData = await encDec.getDecryptFileContent(content);
+
+ res.render("updown/showDecryptedContent",{
+ pageTitle: "File Content",
+ path: "/user/request",
+ documents: plainData,
+ verification: verify
+ })
+}
\ No newline at end of file
diff --git a/fabcar/javascript/controllers/userUpDownDel.js b/fabcar/javascript/controllers/userUpDownDel.js
new file mode 100644
index 0000000..b19d95d
--- /dev/null
+++ b/fabcar/javascript/controllers/userUpDownDel.js
@@ -0,0 +1,393 @@
+const User = require('../models/user');
+const KeywordIndex = require('../models/keyword_index');
+const File = require('../models/file');
+
+const path = require('path');
+const fs = require('fs');
+const encDec = require('../EncryptDecrypt-v2');
+const { ObjectID } = require('bson');
+
+const init = require('../util');
+
+exports.getFrontPage = (req, res, next) => {
+ res.render("users", {
+ pageTitle: "User",
+ path: "/user",
+ editing: false,
+ });
+};
+
+
+
+// About Upload file ..............................................................
+
+exports.getUploadFile = (req, res, next) =>{
+ res.render("updown/upload", {
+ pageTitle: "Upload a file",
+ path: "/user/upload",
+ errorMessage: ""
+ });
+}
+
+var matchCount = 0;
+var bestMatchFiles = {};
+
+exports.postUploadFile = async (req, res, next) =>{
+ const name = req.body.name;
+ const keyword = req.body.keyword;
+ const file = req.file;
+ let tempPath = file.path;
+
+
+ // encrypt the file, store it to the same path
+ encDec.getEncryptFile(req.user.publicKey, tempPath);
+ tempPath = file.path.split('/')[2];
+ // this is to store in the user.cart.myFiles
+ console.log("Here in PostUplod public key of owner: " + req.user.publicKey);
+ let encryptedKeyword = encDec.getEncryptionKeyword(req.user.publicKey, keyword);
+ let space_separated_keywords = keyword.split(' ');
+
+
+
+ const numberOfKeyword = space_separated_keywords.length;
+ let HashedKeywordSet = [];
+ matchCount = 0;
+ // In this for loop, I have just counted the files-frequency (those are related with keywords) in bestMatchFiles container.
+ for(let tmpKey of space_separated_keywords){
+ const keyHash = encDec.getKeywordHash(tmpKey);
+ HashedKeywordSet.push(keyHash);
+ const keyDoc = await KeywordIndex.findOne({index_hash:keyHash});
+ if(keyDoc){
+ // which files contain this key ..
+ let myfiles = [...keyDoc.whereItIs.myFiles];
+ for(var fl of myfiles){
+ var f = fl.filePath;
+ if(bestMatchFiles[f]>=1) {bestMatchFiles[f]++;}
+ else {bestMatchFiles[f]=1;}
+ }
+ }
+ }
+
+
+ // which file has most common keyword set with the currently uploaded file.
+ for (let [key,value] of Object.entries(bestMatchFiles) ){
+ //console.log('printing :', key, value);
+ matchCount = Math.max(value, matchCount);
+ }
+ let decide = false;
+ for (let [key,value] of Object.entries(bestMatchFiles) ){
+ if(matchCount == value){
+ const matchedFile = await File.findOne({filePath: key});
+ if(matchedFile && matchedFile.numberOfKeyword == HashedKeywordSet.length){
+ decide = true;
+ }
+ }
+ }
+
+
+ if(matchCount == HashedKeywordSet.length && decide){
+ //keyword string fully matched
+ return res.status(422).render('updown/upload', {
+ path: '/upload',
+ pageTitle: 'Upload a file',
+ errorMessage: "Keyword fully Matched!!!",
+ oldInput: {
+ name: name,
+ keyword: keyword,
+ file: file
+ }
+ });
+ }
+
+
+
+ // Code is here ... That means all keywords didn't matched.
+ // In this for loop, Just saving or updating the KewordIndex table.
+ //*****************************Upper part is about checking, Lower part is about saving/updating*******************************
+
+
+ // Part 1: Just create a new File
+ let newSavedFile = new File({
+ filePath: tempPath,
+ keyword: encryptedKeyword,
+ numberOfKeyword: numberOfKeyword,
+ store: {
+ keywordsList: []
+ }
+ });
+ newSavedFile = await newSavedFile.save();
+ //console.log("\n newSavedFile1 = ", newSavedFile,"\n");
+
+
+
+
+ // Part 2: In this for loop, Just saving or updating the KewordIndex table.
+ let keyList = []
+
+ for(let keyHash of HashedKeywordSet){
+ //const keyDoc = await KeywordIndex.findOne({index_hash: keyHash});
+ let keyDoc = await KeywordIndex.findOne({index_hash: keyHash});
+ if(keyDoc){
+ // If the index is already there. Just update the keyDoc.whereItIs.myFiles array.
+ let newMyFiles = [...keyDoc.whereItIs.myFiles];
+ newMyFiles.push({
+ //filePath: tempPath
+ fileId: newSavedFile._id
+ });
+
+ const updatedList = {
+ myFiles: newMyFiles
+ };
+ keyDoc.whereItIs = updatedList;
+ keyDoc.save();
+ }
+ else{
+ // If the index is not in the db, then create a new one.
+ let newKey = new KeywordIndex({
+ index_hash: keyHash,
+ whereItIs: {
+ //myFiles: [ {filePath: tempPath} ]
+ myFiles: [ {fileId: newSavedFile._id} ]
+ }
+ });
+ //newKey.save();
+ newKey = await newKey.save();
+ keyDoc = newKey;
+ }
+ keyList.push({keyHashId: keyDoc._id});
+ }
+ //console.log("\n keyList = ", keyList, "\n");
+
+
+
+ // Part 3: Updating the Files's keyword list.
+ const updatedStore = {
+ keywordsList: keyList
+ };
+ //console.log("updatedStore = ", updatedStore);
+
+ const toCheckList = [...newSavedFile.store.keywordsList];
+
+ //console.log("ToCheckList 1 = ", toCheckList);
+
+ for(let obj of keyList){
+ toCheckList.push(obj);
+ }
+
+ //console.log("ToCheckList 2 = ", toCheckList);
+
+ //console.log("newSavedFile.store = ", newSavedFile.store);
+ newSavedFile.store = updatedStore;
+ //console.log("newSavedFile.store = ", newSavedFile.store);
+ //console.log("\n newSavedFile2 = ", newSavedFile,"\n");
+
+ newSavedFile = await newSavedFile.save();
+ //console.log("\n newSavedFile3 = ", newSavedFile,"\n");
+
+
+ // =========================================================================================
+ // FABRIC PART STARTS
+ // =========================================================================================
+ try {
+ var contract = (await init).getInput();
+
+ var ct = contract.contact;
+ var way = contract.gateway;
+ // Submit the specified transaction
+ await ct.submitTransaction(
+ "FileUpload", //must declare in capital letter in both js and Go
+ newSavedFile._id,
+ name,
+ tempPath,
+ encryptedKeyword,
+ numberOfKeyword
+ );
+ //await ct.submitTransaction('CreateMed', 'MED12', 'Beximco', 'NapaVX', 'fever,common cold and influenza.');
+
+ console.log("FileUplaod Transaction has been submitted");
+ // res.redirect("/login");
+ } catch (error) {
+ console.error(`Failed to submit FileUpload transaction: ${error}`);
+ process.exit(1);
+ }
+ // =========================================================================================
+ // FABRIC PART ENDS
+ // =========================================================================================
+
+
+
+// Part 4: Updating to User table.
+ return req.user.addToCart(newSavedFile._id)
+ .then(result=>{
+ res.redirect('/user/uploaded');
+ });
+
+};
+
+// About Download file ..................................................................
+
+exports.getDownloadFile = (req, res, next) =>{
+ res.render("updown/download", {
+ pageTitle: "Download a file",
+ path: "/user/download"
+ });
+}
+
+// It finds out the entries from DB those are matched with searched keywords.
+// Then finds out the file path, And count the frequencies.
+var freqTable = {};
+async function calculate1(space_separated_keywords){
+
+ freqTable = {};
+
+ for(let tmpKey of space_separated_keywords){
+ const keyHash = await encDec.getKeywordHash(tmpKey);
+
+ const keyDoc = await KeywordIndex.findOne({index_hash: keyHash});
+ if(keyDoc){
+ let myfiles = [...keyDoc.whereItIs.myFiles];
+
+ for(let fl of myfiles){
+
+ let temp = await File.findOne({_id: fl.fileId});
+ let fp = temp.filePath;
+ if(freqTable[fp]==1) {freqTable[fp]++;}
+ else { freqTable[fp] = 1;}
+ }
+
+ }
+ else{
+ console.log("Inside else block!");
+ }
+ }
+}
+
+let sortable = [];
+let documents = [];
+// It makes the array sorted in descending order. To show more matched files before the less matched files.
+function calculate2(){
+ sortable = [];
+ documents = [];
+ return new Promise( (resolve, reject) =>{
+
+ for (var file in freqTable) {
+ sortable.push([file, freqTable[file]]);
+ }
+ sortable.sort(function(a, b) {
+ return b[1] - a[1];
+ });
+
+ resolve(sortable);
+ });
+}
+
+async function intermediateFunction(space_separated_keywords){
+ console.log("At the start of intermediate function.");
+ await calculate1(space_separated_keywords);
+ await calculate2();
+
+ console.log('sortable = ', sortable);
+
+ for(let doc of sortable){
+ //const userDoc = await User.findOne({ "cart.myFiles.filePath": doc[0]}, {name: 1} );
+ const fileDoc = await File.findOne({ filePath: doc[0]});
+ console.log(fileDoc._id);
+ const userDoc = await User.findOne({ "cart.myFiles.myFileId": fileDoc._id} );
+ console.log("useDoc = ", userDoc);
+ doc.push(userDoc._id);
+ documents.push(doc);
+ }
+ return;
+}
+
+exports.postDownloadFile = async (req, res, next) =>{
+
+ const keyword = req.body.keyword;
+
+ let space_separated_keywords = keyword.split(' ');
+
+ await intermediateFunction(space_separated_keywords);
+
+ res.render('updown/searchResult', {
+ pageTitle: "Search result",
+ path: "/user/searchResult",
+ docs: documents,
+ errorMessage: ""
+ });
+}
+
+
+// Rendering Uploaded and downloaded files...................................................
+
+exports.getUploadedFiles = (req, res, next) =>{
+ User.findOne({_id: req.user._id})
+ .then(user =>{
+ const files = user.cart.myFiles;
+ res.render("updown/uploaded",{
+ pageTitle: "Uploaded Files",
+ path: "/user/uploaded",
+ files: files
+ })
+ })
+}
+exports.getDownloadedFiles = (req, res, next) =>{
+ res.render("updown/downloaded", {
+ pageTitle: "Downloaded Files",
+ path: "/user/downloaded"
+ });
+}
+
+
+
+
+//Delete part........................................................
+
+exports.deleteFile = async (req, res, next) => {
+ const fileId = req.params.myFileId;
+
+ const theFile = await File.findOne({_id: fileId});
+
+ //Going through the keyword list of this file, and update them accordingly.
+ for(let hashId of theFile.store.keywordsList){
+ //console.log("_id & hashId from file.store are = ", hashId);
+ let keyHash = await KeywordIndex.findOne({_id: hashId.keyHashId});
+
+ //console.log("\n keyHash = ", keyHash, "\n");
+
+ let updatedList = [];
+ for(let something of keyHash.whereItIs.myFiles){
+ if(something.fileId.equals(theFile._id) ){
+ //console.log('\n If statement\n');
+ }
+ else{
+ updatedList.push(something);
+ }
+ }
+ console.log(updatedList);
+ const updatedWhereItIs = {
+ myFiles: updatedList
+ }
+ keyHash.whereItIs = updatedWhereItIs;
+ keyHash = await keyHash.save();
+
+ if(updatedList.length == 0){
+ await KeywordIndex.deleteOne({_id: hashId.keyHashId});
+ }
+ }
+
+ // Now, delete from fileSystem and then the file itself
+ fs.unlinkSync(path.resolve('./') + '/public/files/' + theFile.filePath);
+
+ await File.deleteOne({_id: theFile._id});
+ console.log('successfully deleted !');
+
+ return req.user.deleteFromCart(fileId)
+ .then(result =>{
+ res.redirect('/user/uploaded');
+ });
+}
+
+
+exports.getDocuments = function(){
+ return documents;
+}
\ No newline at end of file
diff --git a/fabcar/javascript/database.js b/fabcar/javascript/database.js
new file mode 100644
index 0000000..4b9d7c4
--- /dev/null
+++ b/fabcar/javascript/database.js
@@ -0,0 +1,28 @@
+const mongodb = require("mongodb");
+const MongoClient = mongodb.MongoClient;
+
+let _db;
+
+const mongoConnect = (callback) =>{
+
+ MongoClient.connect(
+ 'mongodb+srv://arnobkumarsaha:sustcse16@cluster0.nj6lk.mongodb.net/searchableEncryption?retryWrites=true&w=majority'
+ ).then( (client)=>{
+ console.log("MongoDB connected !");
+ _db = client.db();
+ callback();
+ }).catch( (err)=>{
+ console.log(err);
+ throw err;
+ });
+}
+
+const getDb = () =>{
+ if(_db){
+ return _db;
+ }
+ throw 'No Database found.'
+}
+
+exports.mongoConnect = mongoConnect;
+exports.getDb = getDb;
\ No newline at end of file
diff --git a/fabcar/javascript/encryption-decryption.js b/fabcar/javascript/encryption-decryption.js
new file mode 100644
index 0000000..9368e64
--- /dev/null
+++ b/fabcar/javascript/encryption-decryption.js
@@ -0,0 +1,145 @@
+const crypto = require("crypto")
+const fs = require('fs');
+
+// first we need two keys here - index key and private key. Private key will
+// be generated only once and then somehow store it
+
+indexKey = "9uZlGXa0o64kdbQ6Gb96qw=="
+privateKey = "vOVH6sdmpNWjRRIqCc7rdxs01lwHzfr3"
+
+//here we are search only by keywords section of a file.
+// suppose a keyword value for a file is this -
+
+//keyword = "office management file"
+
+// for index of keyword in database table - we store the HMAC value
+// Like this
+
+//keyword_hash = crypto.createHmac('sha256', indexKey)
+// .update(keyword)
+// .digest('hex')
+
+// we store this hash value in keyword_index column in database table
+
+//Now for file and other thing storation with encryption
+//skip file for this tym - will surely add later
+
+//suppose we will store keyword and username as encrypted
+//for this
+
+//username = "getThisFromSignUpForm"
+
+// Now we use AES for this
+
+const algorithm = 'aes-256-cbc';
+//const key = crypto.randomBytes(32);
+//const iv = crypto.randomBytes(16);
+
+//let cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv);
+
+//let encryptedUserName = cipher.update(username);
+//let encryptedKeyword = cipher.update(keyword);
+
+// Now store it to the database
+// works almost same for file
+
+exports.getKeywordHash = (keyword) => {
+ keyword_hash = crypto.createHmac('sha256', indexKey)
+ .update(keyword)
+ .digest('hex')
+ return keyword_hash
+ };
+
+exports.getEncryptionKeyword = (keyword) => {
+ const iv = crypto.randomBytes(16);
+ var ivstring = iv.toString('hex').slice(0, 16);
+
+ let cipher = crypto.createCipheriv(algorithm, privateKey, ivstring);
+
+ let encryptedKeyword = cipher.update(keyword, 'utf8', 'hex');
+ encryptedKeyword += cipher.final('hex');
+
+ //let encryptedKeywordIV = hex_to_ascii(ivstring/* iv.toString('hex') */).concat("//").concat(hex_to_ascii(encryptedKeyword));
+ let encryptedKeywordIV = ivstring.concat("//").concat(encryptedKeyword);
+
+ return encryptedKeywordIV;
+};
+
+exports.getDecryptionKeyword = (keyword) => {
+ let splitString = keyword.split("//");
+ let extractIV = splitString[0];
+ let extractKeyword = splitString[1];
+
+ let decipher = crypto.createDecipheriv(algorithm, privateKey, extractIV);
+
+ let decryptedKeyword = decipher.update(extractKeyword, 'hex', 'utf-8')
+ decryptedKeyword += decipher.final('utf8');
+
+ return decryptedKeyword;
+};
+
+exports.getEncryptFile = (filePath) => {
+ const iv = crypto.randomBytes(16);
+ var ivstring = iv.toString('hex').slice(0, 16);
+
+ let cipher = crypto.createCipheriv(algorithm, privateKey, ivstring);
+
+ fs.readFile(filePath, "utf-8", (err, data) => {
+ //console.log(data);
+ encryptdata = cipher.update(data, 'utf8', 'hex');
+ encryptdata += cipher.final('hex');
+
+ let encryptedDataIV = ivstring.concat("//").concat(encryptdata);
+ fs.writeFile(filePath, encryptedDataIV, (err) => {
+ if (err) console.log(err);
+ console.log(encryptedDataIV);
+ console.log("Successfully Encrypted.");
+ });
+ });
+};
+
+exports.getDecryptFile = (filePath) => {
+
+ fs.readFile(filePath, "binary", (err, data) => {
+ let splitData = data.split("//");
+ let extractIV = splitData[0];
+ let extractFileData = splitData[1];
+
+ const decipher = crypto.createDecipheriv(algorithm, privateKey, extractIV);
+
+ let decrypted = decipher.update(extractFileData, 'hex', 'utf-8')
+ decrypted += decipher.final('utf8');
+ //decoded = decipher.update(data, 'binary', 'binary');
+ //decoded += decipher.final('binary');
+ //var plaintext = new Buffer(decrypted, 'binary').toString('utf8');
+ fs.writeFile(filePath, decrypted , (err) => {
+ if (err) console.log(err);
+ console.log(decrypted);
+ console.log("Successfully decrypted.");
+ });
+ });
+
+
+};
+
+function hex_to_ascii(str1)
+ {
+ var hex = str1.toString();
+ var str = '';
+ for (var n = 0; n < hex.length; n += 2) {
+ str += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
+ }
+ return str;
+ }
+
+ function ascii_to_hex(str)
+ {
+ var arr1 = [];
+ for (var n = 0, l = str.length; n < l; n ++)
+ {
+ var hex = Number(str.charCodeAt(n)).toString(16);
+ arr1.push(hex);
+ }
+ return arr1.join('');
+ }
+
diff --git a/fabcar/javascript/enrollAdmin.js b/fabcar/javascript/enrollAdmin.js
new file mode 100644
index 0000000..49b710a
--- /dev/null
+++ b/fabcar/javascript/enrollAdmin.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const FabricCAServices = require('fabric-ca-client');
+const { Wallets } = require('fabric-network');
+const fs = require('fs');
+const path = require('path');
+
+async function main() {
+ try {
+ // load the network configuration
+ const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
+ const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
+
+ // Create a new CA client for interacting with the CA.
+ const caInfo = ccp.certificateAuthorities['ca.org1.example.com'];
+ const caTLSCACerts = caInfo.tlsCACerts.pem;
+ const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName);
+
+ // Create a new file system based wallet for managing identities.
+ const walletPath = path.join(process.cwd(), 'wallet');
+ const wallet = await Wallets.newFileSystemWallet(walletPath);
+ console.log(`Wallet path: ${walletPath}`);
+
+ // Check to see if we've already enrolled the admin user.
+ const identity = await wallet.get('admin');
+ if (identity) {
+ console.log('An identity for the admin user "admin" already exists in the wallet');
+ return;
+ }
+
+ // Enroll the admin user, and import the new identity into the wallet.
+ const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' });
+ const x509Identity = {
+ credentials: {
+ certificate: enrollment.certificate,
+ privateKey: enrollment.key.toBytes(),
+ },
+ mspId: 'Org1MSP',
+ type: 'X.509',
+ };
+ await wallet.put('admin', x509Identity);
+ console.log('Successfully enrolled admin user "admin" and imported it into the wallet');
+
+ } catch (error) {
+ console.error(`Failed to enroll admin user "admin": ${error}`);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/fabcar/javascript/invoke.js b/fabcar/javascript/invoke.js
new file mode 100644
index 0000000..228aceb
--- /dev/null
+++ b/fabcar/javascript/invoke.js
@@ -0,0 +1,57 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const fs = require('fs');
+const path = require('path');
+
+async function main() {
+ try {
+ // load the network configuration
+ const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
+ let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
+
+ // Create a new file system based wallet for managing identities.
+ const walletPath = path.join(process.cwd(), 'wallet');
+ const wallet = await Wallets.newFileSystemWallet(walletPath);
+ console.log(`Wallet path: ${walletPath}`);
+
+ // Check to see if we've already enrolled the user.
+ const identity = await wallet.get('appUser');
+ if (!identity) {
+ console.log('An identity for the user "appUser" does not exist in the wallet');
+ console.log('Run the registerUser.js application before retrying');
+ return;
+ }
+
+ // Create a new gateway for connecting to our peer node.
+ const gateway = new Gateway();
+ await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } });
+
+ // Get the network (channel) our contract is deployed to.
+ const network = await gateway.getNetwork('mychannel');
+
+ // Get the contract from the network.
+ const contract = network.getContract('fabcar');
+
+ // Submit the specified transaction.
+ // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom')
+ // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR12', 'Dave')
+ await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');
+ console.log('Transaction has been submitted');
+
+ // Disconnect from the gateway.
+ await gateway.disconnect();
+
+ } catch (error) {
+ console.error(`Failed to submit transaction: ${error}`);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/fabcar/javascript/keys/privateKey.key b/fabcar/javascript/keys/privateKey.key
new file mode 100644
index 0000000..58ed6f9
--- /dev/null
+++ b/fabcar/javascript/keys/privateKey.key
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI8ydU0zxN+9gCAggA
+MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEWBBDy15KaHXXd2VDVtMoC+GvNBIIE
+0FysCfVgrtbSsPZpPgrq40LYJAZkUMcdvNsoGB7knbq5zCiLgveD9OgQ/vtL8Mqt
+MxG9dfgoWBI7SNVm8/tij3ZiN1jYDgG4VAnnJ5de8iANVaRgYXU46aNp38MtHKo/
+CWpZ0wKkqSBlLWMBXPAl31Ll/trGjgoruvJCinAmjnCwUgQuTlfnJPEDXPauv3WV
+mu7LWxyF2rYg9TZC/uQqIezKR0oud5jUeM5xotQvMStLa+qUoE/Frfx6hktu99Qu
+l4hLsmyGP+A8SdVMWwf0APwtpv6ujQVBnZXu/xn+oa+AKOyQxr2/ko2ZAbzbc+1j
+qSnKIuVjaEs63BmGACxIpQ+VTigaUWKm7xVpfDrpnzZLCJZRfJREUlraARfnuSMQ
+IB2lXIKGeqX2gYOWZtUlzpUIwtTit4ARJ60sP2jGgoDhL1fbhAv5VpsktyGvwmla
+yxgtC7i95G3jAtnumyTlq3hOyZ0psGYeQQXxcMmwbT2ILM6Q1q6RQF/npak6pyZg
+0EgYVOXEO+gE324skI+3Coc01jH2YBE7+QQSazcTWBMIOIWS0k6lsIxj4xzIoeop
+h0s+5vvUfY1gq9zfWdG0sRZn3dJ9UwAl0xZhBauIX/76/Fu6UT2nw9N1lHIh3y1H
+16Kor1+cvnLB3sA1U4pX7s9hIpE96K6yXbtTfWYflBEKfY71ld2qHizYfhMjzght
+lfEo/mvKGY8nqn8O2dM94aq8uGojrWAF+4qtAoySQl3NAhZCxYcyjwf00/hVdj5V
+SDA13y5umHfrfuUBCAZ7kKY5Md0aGfg6yV9maWRiTxFtW8cfPj+WuIE7D865Ixcu
+kZR0UkxUBlQcnw7+PULZPly3PNNl0UaJzIRsK4gutWhTCy+xvEgOejTiD/3oCGzR
+wu1QxnPg0utZ414KDeGxdgS9fh08VxDR6lvo+7wxW1KzSn6jq3UNTbsn1wSzBxwT
+rDFVaoKIv+VY1omQXs8ujS41WomVOrlA0WBE2pDUqrsboPU1EfHu8f5FgFN2AeXG
+XtJwPr2UqCETO/Q/xkFhyg176z8INVqKnu1LpgRe5zaqzRSujMHskArjJEwHumxs
+MyXb6mb+fpeN9VZpLP3RuwdGQk5uFRkiTpDL1wRg5NHOdDOZG1GWTHZW704Q5sIA
+LEfFd/SI9fVEHLv/Z3WajQyUVHifJ1DrrrrxmvCznN1GdtRPBmnDvPvs8uNkuDCx
+tcPXQfj+brrQ4MdCxJYC2h8xBgsfbeZ53TkyGmx2zM9IzzVAoc6rJE6Msj6f3+E3
+pr7vXOMots+6CFRN92ZD/iZKDT5jKuW2q139Uqg7aLo2bLYqvcTis+uZ5YFAprTb
+6MwfnjDWBvccQe4yyjJZO7SqMcQTCckqG+kHnpKD+32kifmdLtvtWayf0KiHJRd/
+UCdiECMIr6F25K8BqeeDelAhKmE0yDkL7X4gjeK06UHQz0Eqqql0WVLnicVsQfOI
+ZKq+bttgdq+hyss4h5RVwfi3gQBlPiRTtEaMB0Vau9OCTdUHV8LEh+U0IcAH6jPb
+wZE6rJuUOwAN3SaftK71NPH0LkT88RwL3K5Qs83E08qawgKCWfCXm79e3YAldqdV
++z/g0/0eI8wKn8T+zm2tC7HAQ7N+LNrb6GlC+mNC9ayI
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/fabcar/javascript/keys/publicKey.key b/fabcar/javascript/keys/publicKey.key
new file mode 100644
index 0000000..dc66806
--- /dev/null
+++ b/fabcar/javascript/keys/publicKey.key
@@ -0,0 +1,8 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIIBCgKCAQEA6k2cS7q7qPmrN1CghuaejHb0HvfMSMDPeVs444s8Yf5BLjva5OgF
+mDm6BStnxwWQeg7iokSfGDsEWx7J87tGN3ruALjfUSXSQlQ9Z/wsQ+AmHnrD+l3M
+Vi3R9663hOEkAVVWAFIJfvyH/TdALzXhMsexryxXopv6fEqcFUqVLotycSqcutVd
+KAcoaku4KOmvIV0z95zSnKpgz7bG6PggnZ+zOsK5HHv+ctsNDB6i4pFio2xcmr+G
+QNW7gMcZ0/zkkb4Swo+B66Zk3xuLXb+30DrBsdSx38PtvFdgXRf4ge1EnNLuargb
+T1RxOfxwSp1SxD2oh24SE/NkGYGtH5jUoQIDAQEB
+-----END RSA PUBLIC KEY-----
diff --git a/fabcar/javascript/middleware/is-auth.js b/fabcar/javascript/middleware/is-auth.js
new file mode 100644
index 0000000..50f0afb
--- /dev/null
+++ b/fabcar/javascript/middleware/is-auth.js
@@ -0,0 +1,7 @@
+
+module.exports = (req, res, next) => {
+ if (!req.session.isLoggedIn) { // isLoggedIn was set on authController/postLogin route.
+ return res.redirect('/login'); // If No loggedIn user found , then promt user to log in first.
+ }
+ next();
+}
\ No newline at end of file
diff --git a/fabcar/javascript/models/file.js b/fabcar/javascript/models/file.js
new file mode 100644
index 0000000..ea70df2
--- /dev/null
+++ b/fabcar/javascript/models/file.js
@@ -0,0 +1,29 @@
+const mongoose = require('mongoose');
+
+const Schema = mongoose.Schema;
+
+
+const fileSchema = new Schema({
+ filePath: {
+ type: String,
+ required: true
+ },
+ store: {
+ keywordsList: [
+ {
+ keyHashId: {type: Schema.Types.ObjectId, ref: 'Keyword-Index', required: true}
+ }
+ ]
+ },
+ keyword: {
+ type: String,
+ required: true
+ },
+ numberOfKeyword: {
+ type: Number,
+ required: true
+ }
+});
+
+
+module.exports = mongoose.model('File', fileSchema);
\ No newline at end of file
diff --git a/fabcar/javascript/models/keyword_index.js b/fabcar/javascript/models/keyword_index.js
new file mode 100644
index 0000000..972707f
--- /dev/null
+++ b/fabcar/javascript/models/keyword_index.js
@@ -0,0 +1,19 @@
+const mongoose = require('mongoose');
+
+const Schema = mongoose.Schema;
+
+const indexSchema = new Schema({
+ index_hash: {
+ type: String,
+ required: true
+ },
+ whereItIs: {
+ myFiles: [
+ {
+ fileId: { type: Schema.Types.ObjectId, ref: 'File', required: true }
+ }
+ ]
+ }
+});
+
+module.exports = mongoose.model('Keyword-Index', indexSchema);
\ No newline at end of file
diff --git a/fabcar/javascript/models/user.js b/fabcar/javascript/models/user.js
new file mode 100644
index 0000000..467319a
--- /dev/null
+++ b/fabcar/javascript/models/user.js
@@ -0,0 +1,77 @@
+const mongoose = require('mongoose');
+
+const Schema = mongoose.Schema;
+
+
+const userSchema = new Schema({
+ name: {
+ type: String,
+ required: true
+ },
+ email: {
+ type: String,
+ required: true
+ },
+ password: {
+ type: String,
+ required: true
+ },
+ publicKey: {
+ type: String,
+ required: true
+ },
+ cart: {
+ myFiles: [
+ {
+ myFileId: {type: Schema.Types.ObjectId, ref: 'File', required: true}
+ }
+ ]
+ },
+ reqs: {
+ notifications: [
+ {
+ requesterId: {type: Schema.Types.ObjectId, required: true},
+ requestedFileId: {type: Schema.Types.ObjectId, required: true},
+ decided: {type: Boolean, required: true},
+ nonce: {type: String, required: false},
+ }
+ ]
+ },
+ dcart: {
+ allRequests: [
+ {
+ isAccept: {type: Number, required: true},
+ ownerId: {type: Schema.Types.ObjectId, required: false},
+ requestedFileId: {type: Schema.Types.ObjectId, required: false},
+ fileContent: {type: String, required: false},
+ noncePlain: {type: String, required: false}, //plain random number as string
+ nonceGet: {type: String, required: false}
+ }
+ ]
+ }
+});
+
+
+userSchema.methods.addToCart = function(fileId) {
+ const updatedFileItems = [...this.cart.myFiles];
+
+ updatedFileItems.push({
+ myFileId: fileId
+ });
+ const updatedCart = {
+ myFiles: updatedFileItems
+ };
+ this.cart = updatedCart;
+
+ return this.save();
+};
+
+userSchema.methods.deleteFromCart = function(fileId){
+ const updatedCartItems = this.cart.myFiles.filter(item => {
+ return item.myFileId != fileId;
+ });
+ this.cart.myFiles = updatedCartItems;
+ return this.save();
+}
+
+module.exports = mongoose.model('User', userSchema);
\ No newline at end of file
diff --git a/fabcar/javascript/package.json b/fabcar/javascript/package.json
new file mode 100644
index 0000000..caa608d
--- /dev/null
+++ b/fabcar/javascript/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "fabcar",
+ "version": "1.0.0",
+ "description": "FabCar application implemented in JavaScript",
+ "engines": {
+ "node": ">=8",
+ "npm": ">=5"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "pretest": "npm run lint",
+ "test": "nyc mocha --recursive"
+ },
+ "engineStrict": true,
+ "author": "Hyperledger",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fabric-ca-client": "^2.2.4",
+ "fabric-network": "^2.2.4",
+ "bcryptjs": "^2.4.3",
+ "body-parser": "^1.19.0",
+ "connect-flash": "^0.1.1",
+ "connect-mongodb-session": "^2.4.1",
+ "csurf": "^1.11.0",
+ "ejs": "^3.1.6",
+ "express": "^4.17.1",
+ "express-session": "^1.17.1",
+ "express-validator": "^6.10.1",
+ "fs": "*",
+ "mongodb": "^3.6.6",
+ "mongoose": "^5.12.7",
+ "multer": "^1.4.2",
+ "mysql2": "^2.2.5",
+ "nodemon": "^2.0.7"
+ },
+ "devDependencies": {
+ "chai": "^4.2.0",
+ "eslint": "^5.9.0",
+ "mocha": "^5.2.0",
+ "nyc": "^14.1.1",
+ "sinon": "^7.1.1",
+ "sinon-chai": "^3.3.0"
+ },
+ "nyc": {
+ "exclude": [
+ "coverage/**",
+ "test/**"
+ ],
+ "reporter": [
+ "text-summary",
+ "html"
+ ],
+ "all": true,
+ "check-coverage": true,
+ "statements": 100,
+ "branches": 100,
+ "functions": 100,
+ "lines": 100
+ }
+}
diff --git a/fabcar/javascript/public/css/forms.css b/fabcar/javascript/public/css/forms.css
new file mode 100644
index 0000000..20343be
--- /dev/null
+++ b/fabcar/javascript/public/css/forms.css
@@ -0,0 +1,32 @@
+.form-control {
+ margin: 1rem 0;
+ align-self: center;
+}
+
+.form-control label,
+.form-control input,
+.form-control textarea {
+ display: block;
+ width: 100%;
+ margin-bottom: 0.25rem;
+ align-items: center;
+}
+
+.form-control input,
+.form-control textarea {
+ border: 1px solid #a1a1a1;
+ font: inherit;
+ border-radius: 2px;
+}
+
+.form-control input:focus,
+.form-control textarea:focus {
+ outline-color: #00695c;
+}
+
+.login-form {
+ width: 20rem;
+ max-width: 90%;
+ margin: auto;
+ display: block;
+}
diff --git a/fabcar/javascript/public/css/main.css b/fabcar/javascript/public/css/main.css
new file mode 100644
index 0000000..3ce2f2b
--- /dev/null
+++ b/fabcar/javascript/public/css/main.css
@@ -0,0 +1,271 @@
+@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700');
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ padding: 0;
+ margin: 0;
+ font-family: 'Open Sans', sans-serif;
+}
+
+main {
+ padding: 1rem;
+ margin: auto;
+}
+
+form {
+ display: inline;
+}
+
+.centered {
+ text-align: center;
+}
+
+.image {
+ height: 20rem;
+}
+
+.image img {
+ height: 100%;
+}
+
+.main-header {
+ width: 100%;
+ height: 3.5rem;
+ background-color: #00695c;
+ padding: 0 1.5rem;
+ display: flex;
+ align-items: center;
+}
+
+.main-header__nav {
+ height: 100%;
+ width: 100%;
+ display: none;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.main-header__item-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+}
+
+.main-header__item {
+ margin: 0 1rem;
+ padding: 0;
+}
+
+.main-header__item a,
+.main-header__item button {
+ font: inherit;
+ background: transparent;
+ border: none;
+ text-decoration: none;
+ color: white;
+ cursor: pointer;
+}
+
+.main-header__item a:hover,
+.main-header__item a:active,
+.main-header__item a.active,
+.main-header__item button:hover,
+.main-header__item button:active {
+ color: #ffeb3b;
+}
+
+.mobile-nav {
+ width: 30rem;
+ height: 100vh;
+ max-width: 90%;
+ position: fixed;
+ left: 0;
+ top: 0;
+ background: white;
+ z-index: 10;
+ padding: 2rem 1rem 1rem 2rem;
+ transform: translateX(-100%);
+ transition: transform 0.3s ease-out;
+}
+
+.mobile-nav.open {
+ transform: translateX(0);
+}
+
+.mobile-nav__item-list {
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ margin: 0;
+ padding: 0;
+}
+
+.mobile-nav__item {
+ margin: 1rem;
+ padding: 0;
+}
+
+.mobile-nav__item a,
+.mobile-nav__item button {
+ font: inherit;
+ text-decoration: none;
+ color: black;
+ font-size: 1.5rem;
+ padding: 0.5rem 2rem;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+}
+
+.mobile-nav__item a:active,
+.mobile-nav__item a:hover,
+.mobile-nav__item a.active,
+.mobile-nav__item button:hover,
+.mobile-nav__item button:active {
+ background: #00695c;
+ color: white;
+ border-radius: 3px;
+}
+
+#side-menu-toggle {
+ border: 1px solid white;
+ font: inherit;
+ padding: 0.5rem;
+ display: block;
+ background: transparent;
+ color: white;
+ cursor: pointer;
+}
+
+#side-menu-toggle:focus {
+ outline: none;
+}
+
+#side-menu-toggle:active,
+#side-menu-toggle:hover {
+ color: #ffeb3b;
+ border-color: #ffeb3b;
+}
+
+.backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100vh;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 5;
+ display: none;
+}
+
+.grid {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-around;
+ align-items: stretch;
+}
+
+.card {
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
+}
+
+.card__header,
+.card__content {
+ padding: 1rem;
+}
+
+.card__header h1,
+.card__content h1,
+.card__content h2,
+.card__content p {
+ margin: 0;
+}
+
+.card__image {
+ width: 100%;
+}
+
+.card__image img {
+ width: 100%;
+}
+
+.card__actions {
+ padding: 1rem;
+ text-align: center;
+}
+
+.card__actions button,
+.card__actions a {
+ margin: 0 0.25rem;
+}
+
+.btn {
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-decoration: none;
+ font: inherit;
+ border: 1px solid #00695c;
+ color: #00695c;
+ background: white;
+ border-radius: 3px;
+ cursor: pointer;
+}
+
+#logout{
+ top: 30%;
+ position: relative;
+ align-items: center;
+ align-self: center;
+ color: #ffeb3b;
+}
+
+.btn:hover,
+.btn:active {
+ background-color: #00695c;
+ color: white;
+}
+
+.btn.danger {
+ color: red;
+ border-color: red;
+}
+
+.btn.danger:hover,
+.btn.danger:active {
+ background: red;
+ color: white;
+}
+
+.user-message {
+ margin: auto;
+ width: 90%;
+ border: 1px solid #4771fa;
+ padding: 0.5rem;
+ border-radius: 3px;
+ background: #b9c9ff;
+ text-align: center;
+}
+
+.user-message--error {
+ border-color: red;
+ background: rgb(255, 176, 176);
+ color: red;
+}
+
+@media (min-width: 768px) {
+ .main-header__nav {
+ display: flex;
+ }
+
+ #side-menu-toggle {
+ display: none;
+ }
+
+ .user-message {
+ width: 30rem;
+ }
+}
\ No newline at end of file
diff --git a/fabcar/javascript/public/css/user.css b/fabcar/javascript/public/css/user.css
new file mode 100644
index 0000000..7b4afad
--- /dev/null
+++ b/fabcar/javascript/public/css/user.css
@@ -0,0 +1,30 @@
+.user-form {
+ width: 20rem;
+ max-width: 90%;
+ margin: auto;
+ display: block;
+}
+
+.user-item {
+ width: 20rem;
+ max-width: 95%;
+ margin: 1rem;
+ background-color: darkslategray;
+ color: chocolate;
+}
+
+.user__name {
+ font-size: 1.2rem;
+ text-align: center;
+ color: green
+}
+
+.user__email {
+ text-align: center;
+ color: #d36b6b;
+ margin-bottom: 0.5rem;
+}
+
+.user__description {
+ text-align: center;
+}
\ No newline at end of file
diff --git a/fabcar/javascript/public/files/2021-05-30T09:10:32.248Z-*-Thank Bob.txt b/fabcar/javascript/public/files/2021-05-30T09:10:32.248Z-*-Thank Bob.txt
new file mode 100644
index 0000000..7e4998f
Binary files /dev/null and b/fabcar/javascript/public/files/2021-05-30T09:10:32.248Z-*-Thank Bob.txt differ
diff --git a/fabcar/javascript/public/files/2021-05-30T15:26:42.220Z-*-Angry Bob.txt b/fabcar/javascript/public/files/2021-05-30T15:26:42.220Z-*-Angry Bob.txt
new file mode 100644
index 0000000..c421b45
Binary files /dev/null and b/fabcar/javascript/public/files/2021-05-30T15:26:42.220Z-*-Angry Bob.txt differ
diff --git a/fabcar/javascript/public/files/2021-05-30T20:15:31.073Z-*-BobNew1.txt b/fabcar/javascript/public/files/2021-05-30T20:15:31.073Z-*-BobNew1.txt
new file mode 100644
index 0000000..951eb47
Binary files /dev/null and b/fabcar/javascript/public/files/2021-05-30T20:15:31.073Z-*-BobNew1.txt differ
diff --git a/fabcar/javascript/public/files/2021-05-31T05:16:11.316Z-*-Management.txt b/fabcar/javascript/public/files/2021-05-31T05:16:11.316Z-*-Management.txt
new file mode 100644
index 0000000..9603e13
Binary files /dev/null and b/fabcar/javascript/public/files/2021-05-31T05:16:11.316Z-*-Management.txt differ
diff --git a/fabcar/javascript/public/files/2021-05-31T05:18:32.110Z-*-Management-Repy.txt b/fabcar/javascript/public/files/2021-05-31T05:18:32.110Z-*-Management-Repy.txt
new file mode 100644
index 0000000..7ec135a
Binary files /dev/null and b/fabcar/javascript/public/files/2021-05-31T05:18:32.110Z-*-Management-Repy.txt differ
diff --git a/fabcar/javascript/public/files/2021-05-31T14:18:08.451Z-*-test.txt b/fabcar/javascript/public/files/2021-05-31T14:18:08.451Z-*-test.txt
new file mode 100644
index 0000000..ecc2111
Binary files /dev/null and b/fabcar/javascript/public/files/2021-05-31T14:18:08.451Z-*-test.txt differ
diff --git a/fabcar/javascript/public/files/2021-05-31T14:43:36.269Z-*-Gupi File1.txt b/fabcar/javascript/public/files/2021-05-31T14:43:36.269Z-*-Gupi File1.txt
new file mode 100644
index 0000000..05e2694
Binary files /dev/null and b/fabcar/javascript/public/files/2021-05-31T14:43:36.269Z-*-Gupi File1.txt differ
diff --git a/fabcar/javascript/public/files/2021-05-31T14:46:10.484Z-*-BaghaFile1.txt b/fabcar/javascript/public/files/2021-05-31T14:46:10.484Z-*-BaghaFile1.txt
new file mode 100644
index 0000000..b45d4e4
Binary files /dev/null and b/fabcar/javascript/public/files/2021-05-31T14:46:10.484Z-*-BaghaFile1.txt differ
diff --git a/fabcar/javascript/public/files/2021-07-03T21:41:24.262Z-*-bob.txt b/fabcar/javascript/public/files/2021-07-03T21:41:24.262Z-*-bob.txt
new file mode 100644
index 0000000..5d53ca3
--- /dev/null
+++ b/fabcar/javascript/public/files/2021-07-03T21:41:24.262Z-*-bob.txt
@@ -0,0 +1 @@
+ÓT>U¿Æí‰dï÷(©ã!PP£ ‚KÞ'µw’<4`çžVð"/Ù\oê5á§ñ’ûà/Ÿ*\ÈR’½¾~
ÿ¦o«q’êí¢è±_DMgí¿*¢cýYámû‹C0b¥Œ5;M«ål;wŸÔÐA„¹$å@f?yä1Ñ=‰éßh4>ãßö]‰j§}fØ=¢Žzëwì¦[s¬?Åú#<ïךTïzôñúŨ:G›çÏ,¶½ÑTÿaHú·|´ï-†‘/H{¸¤¬.Oƒ„*ó.ñtêPp£[}xv›‡Ù··ß½i«þ)ÚÃ]c¾ÞTÑO×Çkø·î
\ No newline at end of file
diff --git a/fabcar/javascript/public/files/2021-07-04T11:04:16.743Z-*-random.txt b/fabcar/javascript/public/files/2021-07-04T11:04:16.743Z-*-random.txt
new file mode 100644
index 0000000..4610381
--- /dev/null
+++ b/fabcar/javascript/public/files/2021-07-04T11:04:16.743Z-*-random.txt
@@ -0,0 +1,2 @@
+ ¾“ñÐou¿¹‘á-¹L¥äypøØX^Ȳ-hUÁs3h¶‰y{ðηȾĢG9z•ñK’Wˆùé®)_Mû*Âʨܡõ†ßÔè·Æ.f{ÊfgLê´#æF9 „©@H£.mqi¬VÎËmDÑ·§D:WÔ”SpVõÝiü*iÍΛ»üU*¦ºÏ
+iCn8/žeMû·C–0pdÊ€|Öí¹?C?p
\ No newline at end of file
diff --git a/fabcar/javascript/query.js b/fabcar/javascript/query.js
new file mode 100644
index 0000000..82f5367
--- /dev/null
+++ b/fabcar/javascript/query.js
@@ -0,0 +1,58 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Gateway, Wallets } = require('fabric-network');
+const path = require('path');
+const fs = require('fs');
+
+
+async function main() {
+ try {
+ // load the network configuration
+ const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
+ const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
+
+ // Create a new file system based wallet for managing identities.
+ const walletPath = path.join(process.cwd(), 'wallet');
+ const wallet = await Wallets.newFileSystemWallet(walletPath);
+ console.log(`Wallet path: ${walletPath}`);
+
+ // Check to see if we've already enrolled the user.
+ const identity = await wallet.get('appUser');
+ if (!identity) {
+ console.log('An identity for the user "appUser" does not exist in the wallet');
+ console.log('Run the registerUser.js application before retrying');
+ return;
+ }
+
+ // Create a new gateway for connecting to our peer node.
+ const gateway = new Gateway();
+ await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } });
+
+ // Get the network (channel) our contract is deployed to.
+ const network = await gateway.getNetwork('mychannel');
+
+ // Get the contract from the network.
+ const contract = network.getContract('fabcar');
+
+ // Evaluate the specified transaction.
+ // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4')
+ // queryAllCars transaction - requires no arguments, ex: ('queryAllCars')
+ const result = await contract.evaluateTransaction('queryAllCars');
+ console.log(`Transaction has been evaluated, result is: ${result.toString()}`);
+
+ // Disconnect from the gateway.
+ await gateway.disconnect();
+
+ } catch (error) {
+ console.error(`Failed to evaluate transaction: ${error}`);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/fabcar/javascript/registerUser.js b/fabcar/javascript/registerUser.js
new file mode 100644
index 0000000..f8fdd04
--- /dev/null
+++ b/fabcar/javascript/registerUser.js
@@ -0,0 +1,75 @@
+/*
+ * Copyright IBM Corp. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { Wallets } = require('fabric-network');
+const FabricCAServices = require('fabric-ca-client');
+const fs = require('fs');
+const path = require('path');
+
+async function main() {
+ try {
+ // load the network configuration
+ const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
+ const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
+
+ // Create a new CA client for interacting with the CA.
+ const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url;
+ const ca = new FabricCAServices(caURL);
+
+ // Create a new file system based wallet for managing identities.
+ const walletPath = path.join(process.cwd(), 'wallet');
+ const wallet = await Wallets.newFileSystemWallet(walletPath);
+ console.log(`Wallet path: ${walletPath}`);
+
+ // Check to see if we've already enrolled the user.
+ const userIdentity = await wallet.get('appUser');
+ if (userIdentity) {
+ console.log('An identity for the user "appUser" already exists in the wallet');
+ return;
+ }
+
+ // Check to see if we've already enrolled the admin user.
+ const adminIdentity = await wallet.get('admin');
+ if (!adminIdentity) {
+ console.log('An identity for the admin user "admin" does not exist in the wallet');
+ console.log('Run the enrollAdmin.js application before retrying');
+ return;
+ }
+
+ // build a user object for authenticating with the CA
+ const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type);
+ const adminUser = await provider.getUserContext(adminIdentity, 'admin');
+
+ // Register the user, enroll the user, and import the new identity into the wallet.
+ const secret = await ca.register({
+ affiliation: 'org1.department1',
+ enrollmentID: 'appUser',
+ role: 'client'
+ }, adminUser);
+ const enrollment = await ca.enroll({
+ enrollmentID: 'appUser',
+ enrollmentSecret: secret
+ });
+ const x509Identity = {
+ credentials: {
+ certificate: enrollment.certificate,
+ privateKey: enrollment.key.toBytes(),
+ },
+ mspId: 'Org1MSP',
+ type: 'X.509',
+ };
+ await wallet.put('appUser', x509Identity);
+ console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet');
+
+ } catch (error) {
+ console.error(`Failed to register user "appUser": ${error}`);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/fabcar/javascript/rosalind.txt b/fabcar/javascript/rosalind.txt
new file mode 100644
index 0000000..b124513
Binary files /dev/null and b/fabcar/javascript/rosalind.txt differ
diff --git a/fabcar/javascript/routes/index.js b/fabcar/javascript/routes/index.js
new file mode 100644
index 0000000..8dfa238
--- /dev/null
+++ b/fabcar/javascript/routes/index.js
@@ -0,0 +1,74 @@
+var express = require('express');
+const indexController = require('../controllers/index');
+var router = express.Router();
+
+const { check, body } = require("express-validator");
+const User = require("../models/user");
+
+
+router.get('/', indexController.getFrontPage);
+
+router.get('/login', indexController.getLogin);
+
+
+router.post('/login',
+[
+ check('email')
+ .isEmail()
+ .withMessage('Please enter a valid email address.')
+ .custom((value, { req }) => {
+ return User.findOne({ email: value }).then(stuDoc => {
+ if (!stuDoc) {
+ return Promise.reject(
+ 'No user with this email. Do you want to signUp ?'
+ );
+ }
+ });
+ })
+ .normalizeEmail(),
+ body('password', 'Password has to be valid.')
+ .isLength({ min: 4 })
+ ],
+indexController.postLogin);
+
+
+
+
+
+router.get('/signup', indexController.getSignup);
+router.post('/signup',
+[
+ check('email')
+ .isEmail()
+ .withMessage('Please enter a valid email.')
+ .custom((value, { req }) => {
+ return User.findOne({ email: value }).then(stuDoc => {
+ if (stuDoc) {
+ return Promise.reject(
+ 'E-Mail exists already, please pick a different one.'
+ );
+ }
+ });
+ })
+ .normalizeEmail(),
+ body('name').isString(),
+ body(
+ 'password',
+ 'Please enter a password with at least 4 characters.'
+ )
+ .isLength({ min: 4 }),
+ body('confirmPassword')
+ .custom((value, { req }) => {
+ if (value !== req.body.password) {
+ throw new Error('Passwords have to match!');
+ }
+ return true;
+ })
+ ],
+ indexController.postSignup);
+
+
+router.post("/logout", indexController.postLogout);
+
+
+module.exports = router;
\ No newline at end of file
diff --git a/fabcar/javascript/routes/user.js b/fabcar/javascript/routes/user.js
new file mode 100644
index 0000000..f914453
--- /dev/null
+++ b/fabcar/javascript/routes/user.js
@@ -0,0 +1,45 @@
+var express = require('express');
+const userController1 = require('../controllers/userUpDownDel');
+const userController2 = require('../controllers/userShowReq');
+const isAuth = require('../middleware/is-auth');
+var router = express.Router();
+
+// Upload - Download related.
+router.get('/', userController1.getFrontPage);
+
+router.get('/upload', userController1.getUploadFile);
+router.post('/upload', userController1.postUploadFile);
+
+router.get('/download', userController1.getDownloadFile);
+router.post('/download', userController1.postDownloadFile);
+
+
+router.get('/uploaded', isAuth, userController1.getUploadedFiles);
+router.get('/downloaded', userController1.getDownloadedFiles);
+
+router.post('/delete-file/:myFileId', userController1.deleteFile);
+
+
+
+
+// Show and request-handling related.
+
+
+router.get('/show-file/:myFileId', userController2.showFileById);
+
+router.get('/notification', userController2.getAllNotifications);
+
+
+router.get('/show-decrypted-content/:fileContent/:noncePlain/:nonceGet', userController2.showDecryptedFileContent);
+
+router.get('/request', userController2.getAllRequests);
+
+
+
+router.post('/request-file/:ownerId/:fileName', userController2.requestFile);
+
+router.post('/grant-permission/:requesterId/:requestedFileId', userController2.grantPermission);
+
+router.post('/deny-permission/:requesterId/:requestedFileId', userController2.denyPermission);
+
+module.exports = router;
\ No newline at end of file
diff --git a/fabcar/javascript/temp-file/againEncryptFile.txt b/fabcar/javascript/temp-file/againEncryptFile.txt
new file mode 100644
index 0000000..abf7573
--- /dev/null
+++ b/fabcar/javascript/temp-file/againEncryptFile.txt
@@ -0,0 +1 @@
+19a6c9db8f17fad0864923ad3a83fef7532c0c2652d95d5283fa450aed3257732bc43453c496b6587e3dcf8aecbfa6551275ab27dccb5ad7dc9e7f42e606e9c42e6e2ae32e06665311b69b792a3bcdcbcf13cc0861dfefa3b45d81730ebf2ad8e3bdbd233562f6e6e1bc3e85c6262c48f619a6f08175679be9a7d5b96160e58b83bd44e249c301b6a5aaff5662a314a95c065b392441d3a6756f32ba44a1056316858c42d70755bb6e1b5dd8fc079a4c04e45da6c82a042e14b88c69da34425df80ab668c23e8c9216cf74897fc17b026ba2f3a52571d59d5d68cbe6e87a90fe496d4197d070acd093b2e0fc31256ce0053034de449b08046a41b2d996a52ead
\ No newline at end of file
diff --git a/fabcar/javascript/temp-file/decryptedFile.txt b/fabcar/javascript/temp-file/decryptedFile.txt
new file mode 100644
index 0000000..58200e3
--- /dev/null
+++ b/fabcar/javascript/temp-file/decryptedFile.txt
@@ -0,0 +1 @@
+Now Bob sends a msg to Alice!!!!
diff --git a/fabcar/javascript/util.js b/fabcar/javascript/util.js
new file mode 100644
index 0000000..7bddce2
--- /dev/null
+++ b/fabcar/javascript/util.js
@@ -0,0 +1,57 @@
+const path = require('path');
+const fs = require('fs');
+
+// FABRIC INCLUDES STARTS
+const { Gateway, Wallets } = require('fabric-network');
+const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
+const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
+// FABRIC INCLUDES ENDS
+
+var init = (async () => {
+ // Create a new file system based wallet for managing identities.
+ // const walletPath = path.join(process.cwd(), "wallet");
+ // const wallet = new FileSystemWallet(walletPath);
+ // console.log(`Wallet path: ${walletPath}`);
+ const walletPath = path.join(__dirname, 'wallet');
+ const wallet = await Wallets.newFileSystemWallet(walletPath);
+ console.log(`Wallet path: ${walletPath}`);
+
+ // Check to see if we've already enrolled the user.
+ // const userExists = await wallet.exists("user1");
+ // if (!userExists) {
+ // console.log(
+ // 'An identity for the user "user1" does not exist in the wallet'
+ // );
+ // console.log("Run the registerUser.js application before retrying");
+ // return;
+ // }
+ const identity = await wallet.get('appUser');
+ if (!identity) {
+ console.log('An identity for the user "appUser" does not exist in the wallet');
+ console.log('Run the registerUser.js application before retrying');
+ return;
+ }
+
+ // Create a new gateway for connecting to our peer node.
+ const gateway = new Gateway();
+ await gateway.connect(ccp, { wallet, identity: 'appUser' });
+ // , discovery: { enabled: true, asLocalhost: true } // After indentity: 'appUser'
+
+
+ // Get the network (channel) our contract is deployed to.
+ const network = await gateway.getNetwork("mychannel");
+
+ // Get the contract from the network.
+ const contract = network.getContract("fabcar");
+
+ return {
+ getInput: function () {
+ return {
+ contact: contract, // Will be either inc or exp
+ gateway: gateway,
+ };
+ },
+ };
+ })();
+
+ module.exports = init;
\ No newline at end of file
diff --git a/fabcar/javascript/views/404.ejs b/fabcar/javascript/views/404.ejs
new file mode 100644
index 0000000..3aaa94b
--- /dev/null
+++ b/fabcar/javascript/views/404.ejs
@@ -0,0 +1,10 @@
+<%- include('includes/head.ejs') %>
+
+
+
+ <%- include('includes/navigation.ejs') %>
+ Page Not Found!
+ No such route.
+
+
+<%- include('includes/end.ejs') %>
\ No newline at end of file
diff --git a/fabcar/javascript/views/auth/login.ejs b/fabcar/javascript/views/auth/login.ejs
new file mode 100644
index 0000000..2b6c351
--- /dev/null
+++ b/fabcar/javascript/views/auth/login.ejs
@@ -0,0 +1,32 @@
+<%- include('../includes/head.ejs') %>
+
+
+
+
+
+ <%- include('../includes/navigation.ejs') %>
+
+
+ <% if (errorMessage) { %>
+ <%= errorMessage %>
+ <% } %>
+
+
+<%- include('../includes/end.ejs') %>
\ No newline at end of file
diff --git a/fabcar/javascript/views/auth/signup.ejs b/fabcar/javascript/views/auth/signup.ejs
new file mode 100644
index 0000000..7ef360f
--- /dev/null
+++ b/fabcar/javascript/views/auth/signup.ejs
@@ -0,0 +1,45 @@
+<%- include('../includes/head.ejs') %>
+
+
+
+
+
+ <%- include('../includes/navigation.ejs') %>
+
+
+ <% if (errorMessage) { %>
+ <%= errorMessage %>
+ <% } %>
+
+
+<%- include('../includes/end.ejs') %>
\ No newline at end of file
diff --git a/fabcar/javascript/views/form.ejs b/fabcar/javascript/views/form.ejs
new file mode 100644
index 0000000..9e55bf7
--- /dev/null
+++ b/fabcar/javascript/views/form.ejs
@@ -0,0 +1,36 @@
+
+
+
+
+
+ Register
+
+
+
+
+
Upload File Form
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fabcar/javascript/views/includes/end.ejs b/fabcar/javascript/views/includes/end.ejs
new file mode 100644
index 0000000..7cbf3b0
--- /dev/null
+++ b/fabcar/javascript/views/includes/end.ejs
@@ -0,0 +1,4 @@
+
+
+
+