├── contracts
├── asset-transfer-ts
│ ├── README.md
│ ├── .gitignore
│ ├── dist
│ │ ├── index.d.ts
│ │ ├── asset.d.ts
│ │ ├── assetTransfer.d.ts
│ │ ├── index.js
│ │ ├── asset.js
│ │ └── assetTransfer.js
│ ├── .prettierrc.js
│ ├── .eslintignore
│ ├── src
│ │ ├── index.ts
│ │ ├── asset.ts
│ │ └── assetTransfer.ts
│ ├── target
│ │ └── npmlist.json
│ ├── .editorconfig
│ ├── .npmignore
│ ├── docker
│ │ └── docker-entrypoint.sh
│ ├── .vscode
│ │ └── launch.json
│ ├── .eslintrc.js
│ ├── tsconfig.json
│ ├── Dockerfile
│ └── package.json
├── chaincode-java
│ ├── .gitignore
│ ├── .gradle
│ │ ├── 6.5.1
│ │ │ ├── gc.properties
│ │ │ ├── fileChanges
│ │ │ │ └── last-build.bin
│ │ │ ├── fileHashes
│ │ │ │ └── fileHashes.lock
│ │ │ └── executionHistory
│ │ │ │ └── executionHistory.lock
│ │ ├── vcs-1
│ │ │ └── gc.properties
│ │ ├── buildOutputCleanup
│ │ │ ├── cache.properties
│ │ │ └── buildOutputCleanup.lock
│ │ └── checksums
│ │ │ └── checksums.lock
│ ├── settings.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── .gitattributes
│ ├── config
│ │ └── checkstyle
│ │ │ ├── suppressions.xml
│ │ │ └── checkstyle.xml
│ ├── docker
│ │ └── docker-entrypoint.sh
│ ├── Dockerfile
│ ├── build.gradle
│ ├── src
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── org
│ │ │ │ └── hyperledger
│ │ │ │ └── fabric
│ │ │ │ └── samples
│ │ │ │ └── assettransfer
│ │ │ │ ├── AssetTest.java
│ │ │ │ └── AssetTransferTest.java
│ │ └── main
│ │ │ └── java
│ │ │ └── org
│ │ │ └── hyperledger
│ │ │ └── fabric
│ │ │ └── samples
│ │ │ └── assettransfer
│ │ │ ├── Asset.java
│ │ │ └── AssetTransfer.java
│ ├── gradlew.bat
│ └── gradlew
└── ccaas_pkg.sh
├── _docs
├── CCAASDebug.png
├── vscode-001.png
├── vscode-002.png
├── vscode-debug.png
├── vscode-debug-step.png
└── Windows-WSL2-setup.md
├── .vscode
└── launch.json
├── infrastructure
└── dev-microfab
│ └── oneorg.sh
├── LICENSE
└── README.md
/contracts/asset-transfer-ts/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gradle/6.5.1/gc.properties:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gradle/vcs-1/gc.properties:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gradle/6.5.1/fileChanges/last-build.bin:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | _tmp
3 | lib
4 | coverage
5 | .nyc_output
6 |
--------------------------------------------------------------------------------
/_docs/CCAASDebug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/_docs/CCAASDebug.png
--------------------------------------------------------------------------------
/_docs/vscode-001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/_docs/vscode-001.png
--------------------------------------------------------------------------------
/_docs/vscode-002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/_docs/vscode-002.png
--------------------------------------------------------------------------------
/_docs/vscode-debug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/_docs/vscode-debug.png
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gradle/buildOutputCleanup/cache.properties:
--------------------------------------------------------------------------------
1 | #Wed Dec 15 13:53:26 GMT 2021
2 | gradle.version=6.5.1
3 |
--------------------------------------------------------------------------------
/_docs/vscode-debug-step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/_docs/vscode-debug-step.png
--------------------------------------------------------------------------------
/contracts/chaincode-java/settings.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | rootProject.name = 'basic'
6 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | export { AssetTransferContract } from './assetTransfer';
2 | export declare const contracts: any[];
3 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | trailingComma: 'all',
4 | singleQuote: true,
5 | printWidth: 120,
6 | tabWidth: 4,
7 | };
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gradle/checksums/checksums.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/contracts/chaincode-java/.gradle/checksums/checksums.lock
--------------------------------------------------------------------------------
/contracts/chaincode-java/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/contracts/chaincode-java/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gradle/6.5.1/fileHashes/fileHashes.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/contracts/chaincode-java/.gradle/6.5.1/fileHashes/fileHashes.lock
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gitattributes:
--------------------------------------------------------------------------------
1 | #
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | #
4 | # These are explicitly windows files and should use crlf
5 | *.bat text eol=crlf
6 |
7 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gradle/buildOutputCleanup/buildOutputCleanup.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/contracts/chaincode-java/.gradle/buildOutputCleanup/buildOutputCleanup.lock
--------------------------------------------------------------------------------
/contracts/chaincode-java/.gradle/6.5.1/executionHistory/executionHistory.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperledgendary/fabric-contract-debug-scenario/main/contracts/chaincode-java/.gradle/6.5.1/executionHistory/executionHistory.lock
--------------------------------------------------------------------------------
/_docs/Windows-WSL2-setup.md:
--------------------------------------------------------------------------------
1 | # Windows with WSL2
2 |
3 | Install WSl2 with Linux distro
4 | Install docker within wsl2
5 |
6 | VSCode installed in windows and use the remote development features to WSL2
7 |
8 | Install the extension within the WSL2 environment
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/.eslintignore:
--------------------------------------------------------------------------------
1 | # don't ever lint node_modules
2 | node_modules
3 |
4 | # don't lint build output (make sure it's set to your correct build folder name)
5 | dist
6 |
7 | # don't lint nyc coverage output
8 | coverage
9 |
10 | facade-14
--------------------------------------------------------------------------------
/contracts/chaincode-java/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/src/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | import { AssetTransferContract } from './assetTransfer';
6 |
7 | export { AssetTransferContract } from './assetTransfer';
8 |
9 | export const contracts: any[] = [AssetTransferContract];
10 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/target/npmlist.json:
--------------------------------------------------------------------------------
1 | {"version":"1.0.0","name":"asset-transfer-basic","dependencies":{"fabric-contract-api":{"version":"2.4.1"},"fabric-shim":{"version":"2.4.1"},"json-stringify-deterministic":{"version":"1.0.2"},"sort-keys-recursive":{"version":"2.1.2"},"source-map-support":{"version":"0.5.19"}}}
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/dist/asset.d.ts:
--------------------------------------------------------------------------------
1 | export declare class Asset {
2 | docType?: string;
3 | id: string;
4 | color: string;
5 | size: number;
6 | owner: string;
7 | appraisedValue: number;
8 | constructor(id: string, color: string, size: number, owner: string, appraisedValue: number);
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/.editorconfig:
--------------------------------------------------------------------------------
1 | #
2 | # SPDX-License-Identifier: Apache-2.0
3 | #
4 |
5 | root = true
6 |
7 | [*]
8 | indent_style = space
9 | indent_size = 4
10 | end_of_line = lf
11 | charset = utf-8
12 | trim_trailing_whitespace = true
13 | insert_final_newline = true
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/config/checkstyle/suppressions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/.npmignore:
--------------------------------------------------------------------------------
1 | #
2 | # SPDX-License-Identifier: Apache-2.0
3 | #
4 |
5 | # don't package the connection details
6 | local_fabric
7 |
8 | # don't package the tests
9 | test
10 | functionalTests
11 |
12 | # don't package config files
13 | .vscode
14 | .editorconfig
15 | .eslintignore
16 | .eslintrc.js
17 | .gitignore
18 | .npmignore
19 | .nyc_output
20 | coverage
21 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/docker/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # SPDX-License-Identifier: Apache-2.0
4 | #
5 | set -euo pipefail
6 | : ${CORE_PEER_TLS_ENABLED:="false"}
7 | : ${DEBUG:="false"}
8 |
9 | if [ "${DEBUG,,}" = "true" ]; then
10 | npm run start:server-debug
11 | elif [ "${CORE_PEER_TLS_ENABLED,,}" = "true" ]; then
12 | npm run start:server
13 | else
14 | npm run start:server-nontls
15 | fi
16 |
17 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/docker/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # SPDX-License-Identifier: Apache-2.0
4 | #
5 | set -euo pipefail
6 | : ${CORE_PEER_TLS_ENABLED:="false"}
7 | : ${DEBUG:="false"}
8 |
9 | if [ "${DEBUG,,}" = "true" ]; then
10 | java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8000 -jar /chaincode.jar
11 | elif [ "${CORE_PEER_TLS_ENABLED,,}" = "true" ]; then
12 | java -jar /chaincode.jar # todo
13 | else
14 | java -jar /chaincode.jar
15 | fi
16 |
17 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Attach by Process ID",
9 | "processId": "${command:PickProcess}",
10 | "request": "attach",
11 | "skipFiles": [
12 | "/**"
13 | ],
14 | "type": "pwa-node"
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Attach by Process ID",
9 | "processId": "${command:PickProcess}",
10 | "request": "attach",
11 | "skipFiles": [
12 | "/**"
13 | ],
14 | "type": "pwa-node"
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/infrastructure/dev-microfab/oneorg.sh:
--------------------------------------------------------------------------------
1 | export MICROFAB_CONFIG='{
2 | "endorsing_organizations":[
3 | {
4 | "name": "DigiBank"
5 | }
6 | ],
7 | "channels":[
8 | {
9 | "name": "assetnet",
10 | "endorsing_organizations":[
11 | "DigiBank"
12 | ]
13 | }
14 | ],
15 | "capability_level":"V2_0"
16 | }'
17 |
18 | LOGGING_SPEC=debug
19 | docker run --name microfab --rm -ti -p2005:2005 -p 8080:8080 -e MICROFAB_CONFIG="${MICROFAB_CONFIG}" -e FABRIC_LOGGING_SPEC=$LOGGING_SPEC ibmcom/ibp-microfab-rc
20 |
21 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/dist/assetTransfer.d.ts:
--------------------------------------------------------------------------------
1 | import { Context, Contract } from 'fabric-contract-api';
2 | export declare class AssetTransferContract extends Contract {
3 | InitLedger(ctx: Context): Promise;
4 | CreateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number): Promise;
5 | ReadAsset(ctx: Context, id: string): Promise;
6 | UpdateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number): Promise;
7 | DeleteAsset(ctx: Context, id: string): Promise;
8 | AssetExists(ctx: Context, id: string): Promise;
9 | TransferAsset(ctx: Context, id: string, newowner: string): Promise;
10 | GetAllAssets(ctx: Context): Promise;
11 | }
12 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser
3 | extends: [
4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
5 | 'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
6 | ],
7 | parserOptions: {
8 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
9 | sourceType: 'module', // Allows for the use of imports
10 | },
11 | rules: {
12 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
13 | // e.g. "@typescript-eslint/explicit-function-return-type": "off",
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/src/asset.ts:
--------------------------------------------------------------------------------
1 | /*
2 | SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | import { Object, Property } from 'fabric-contract-api';
6 |
7 | @Object()
8 | export class Asset {
9 | @Property()
10 | public docType?: string;
11 |
12 | @Property()
13 | public id: string;
14 |
15 | @Property()
16 | public color: string;
17 |
18 | @Property()
19 | public size: number;
20 |
21 | @Property()
22 | public owner: string;
23 |
24 | @Property()
25 | public appraisedValue: number;
26 |
27 | public constructor(id: string, color: string, size: number, owner: string, appraisedValue: number) {
28 | this.docType = 'Asset';
29 | this.id = id;
30 | this.color = color;
31 | this.size = size;
32 | this.owner = owner;
33 | this.appraisedValue = appraisedValue;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "module": "commonjs",
5 | "moduleResolution": "node",
6 | "lib": [
7 | "esnext",
8 | "es2017.object"
9 | ],
10 | "target": "es2015",
11 | "outDir": "./dist",
12 | "removeComments": true,
13 | "inlineSourceMap": true,
14 | "inlineSources": true,
15 | "preserveConstEnums": true,
16 | "experimentalDecorators": true,
17 | "emitDecoratorMetadata": true,
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noImplicitReturns": true,
22 | "noFallthroughCasesInSwitch": true,
23 | "esModuleInterop": true,
24 |
25 | },
26 | "include": [
27 | "src/**/*"
28 | ],
29 | "exclude": [
30 | "node_modules",
31 | "**/*-spec.ts"
32 | ]
33 | }
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # SPDX-License-Identifier: Apache-2.0
3 | #
4 | FROM node:16 AS builder
5 |
6 | WORKDIR /usr/src/app
7 |
8 | # Copy node.js source and build, changing owner as well
9 | COPY --chown=node:node . /usr/src/app
10 | RUN npm ci && npm run build && npm shrinkwrap
11 |
12 | FROM node:16 AS production
13 |
14 | # Setup tini to work better handle signals
15 | ENV TINI_VERSION v0.19.0
16 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
17 | RUN chmod +x /tini
18 |
19 |
20 | WORKDIR /usr/src/app
21 | COPY --chown=node:node --from=builder /usr/src/app/dist ./dist
22 | COPY --chown=node:node --from=builder /usr/src/app/package.json ./
23 | COPY --chown=node:node --from=builder /usr/src/app/npm-shrinkwrap.json ./
24 | COPY --chown=node:node docker/docker-entrypoint.sh /usr/src/app/docker-entrypoint.sh
25 | RUN npm ci --only=production
26 |
27 | ENV PORT 9999
28 | EXPOSE 9999
29 | ENV NODE_ENV=production
30 |
31 | USER node
32 | ENTRYPOINT [ "/tini", "--", "/usr/src/app/docker-entrypoint.sh" ]
33 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/dist/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | exports.contracts = exports.AssetTransferContract = void 0;
4 | const assetTransfer_1 = require("./assetTransfer");
5 | var assetTransfer_2 = require("./assetTransfer");
6 | Object.defineProperty(exports, "AssetTransferContract", { enumerable: true, get: function () { return assetTransfer_2.AssetTransferContract; } });
7 | exports.contracts = [assetTransfer_1.AssetTransferContract];
8 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBSUEsbURBQXdEO0FBRXhELGlEQUF3RDtBQUEvQyxzSEFBQSxxQkFBcUIsT0FBQTtBQUVqQixRQUFBLFNBQVMsR0FBVSxDQUFDLHFDQUFxQixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcbiAqL1xuXG5pbXBvcnQgeyBBc3NldFRyYW5zZmVyQ29udHJhY3QgfSBmcm9tICcuL2Fzc2V0VHJhbnNmZXInO1xuXG5leHBvcnQgeyBBc3NldFRyYW5zZmVyQ29udHJhY3QgfSBmcm9tICcuL2Fzc2V0VHJhbnNmZXInO1xuXG5leHBvcnQgY29uc3QgY29udHJhY3RzOiBhbnlbXSA9IFtBc3NldFRyYW5zZmVyQ29udHJhY3RdO1xuIl19
--------------------------------------------------------------------------------
/contracts/chaincode-java/Dockerfile:
--------------------------------------------------------------------------------
1 | # the first stage
2 | FROM gradle:jdk11 AS GRADLE_BUILD
3 |
4 | # copy the build.gradle and src code to the container
5 | COPY src/ src/
6 | COPY build.gradle ./
7 |
8 | # Build and package our code
9 | RUN gradle --no-daemon build shadowJar -x checkstyleMain -x checkstyleTest
10 |
11 |
12 | # the second stage of our build just needs the compiled files
13 | FROM openjdk:11-jre
14 | ARG CC_SERVER_PORT=9999
15 |
16 | # Setup tini to work better handle signals
17 | ENV TINI_VERSION v0.19.0
18 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
19 | RUN chmod +x /tini
20 |
21 | RUN addgroup --system javauser && useradd -g javauser javauser
22 |
23 | # copy only the artifacts we need from the first stage and discard the rest
24 | COPY --chown=javauser:javauser --from=GRADLE_BUILD /home/gradle/build/libs/chaincode.jar /chaincode.jar
25 | COPY --chown=javauser:javauser docker/docker-entrypoint.sh /docker-entrypoint.sh
26 |
27 | ENV PORT $CC_SERVER_PORT
28 | EXPOSE $CC_SERVER_PORT
29 |
30 | USER javauser
31 | ENTRYPOINT [ "/tini", "--", "/docker-entrypoint.sh" ]
32 |
--------------------------------------------------------------------------------
/contracts/ccaas_pkg.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # SPDX-License-Identifier: Apache-2.0
5 | #
6 |
7 | set -eo pipefail
8 |
9 | # Where am I?
10 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/." && pwd )"
11 |
12 | : ${CC_NAME:="assettransfer"}
13 | : ${CCAAS_SERVER_PORT:=9999}
14 | : ${CC_VERSION:=1}
15 |
16 |
17 | packageChaincode() {
18 |
19 | # address="{{.peername}}_${CC_NAME}_ccaas:${CCAAS_SERVER_PORT}"
20 | address="172.17.0.1:${CCAAS_SERVER_PORT}"
21 | prefix=$(basename "$0")
22 | tempdir=$(mktemp -d -t "$prefix.XXXXXXXX") || error_exit "Error creating temporary directory"
23 | label=${CC_NAME}_${CC_VERSION}
24 | mkdir -p "$tempdir/src"
25 |
26 | cat > "$tempdir/src/connection.json" < "$tempdir/pkg/metadata.json"
37 | {
38 | "type": "ccaas",
39 | "label": "$label"
40 | }
41 | METADATA-EOF
42 |
43 | tar -C "$tempdir/src" -czf "$tempdir/pkg/code.tar.gz" .
44 | tar -C "$tempdir/pkg" -czf "$CC_NAME.tar.gz" metadata.json code.tar.gz
45 | rm -Rf "$tempdir"
46 |
47 | ls -l "$CC_NAME.tar.gz"
48 | }
49 |
50 | getPackageId() {
51 | local cc_package="$CC_NAME.tar.gz"
52 | cc_sha256=$(shasum -a 256 ${cc_package} | tr -s ' ' | cut -d ' ' -f 1)
53 |
54 | label=${CC_NAME}_${CC_VERSION}
55 | CHAINCODE_ID=${label}:${cc_sha256}
56 | echo "CHAINCODE_ID=${CHAINCODE_ID}"
57 | }
58 |
59 |
60 | packageChaincode
61 | getPackageId
62 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | plugins {
6 | id 'com.github.johnrengelman.shadow' version '5.1.0'
7 | id 'application'
8 | id 'checkstyle'
9 | id 'jacoco'
10 | }
11 |
12 | group 'org.hyperledger.fabric.samples'
13 | version '1.0-SNAPSHOT'
14 |
15 | dependencies {
16 |
17 | implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.4.1'
18 | implementation 'org.json:json:+'
19 | implementation 'com.owlike:genson:1.5'
20 | testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.4.1'
21 | testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
22 | testImplementation 'org.assertj:assertj-core:3.11.1'
23 | testImplementation 'org.mockito:mockito-core:2.+'
24 | }
25 |
26 | repositories {
27 | mavenCentral()
28 | maven {
29 | url 'https://jitpack.io'
30 | }
31 | }
32 |
33 | application {
34 | mainClass = 'org.hyperledger.fabric.contract.ContractRouter'
35 | }
36 |
37 | checkstyle {
38 | toolVersion '8.21'
39 | configFile file("config/checkstyle/checkstyle.xml")
40 | }
41 |
42 | checkstyleMain {
43 | source ='src/main/java'
44 | }
45 |
46 | checkstyleTest {
47 | source ='src/test/java'
48 | }
49 |
50 | jacocoTestReport {
51 | dependsOn test
52 | }
53 |
54 | jacocoTestCoverageVerification {
55 | violationRules {
56 | rule {
57 | limit {
58 | minimum = 0.9
59 | }
60 | }
61 | }
62 |
63 | finalizedBy jacocoTestReport
64 | }
65 |
66 | test {
67 | useJUnitPlatform()
68 | testLogging {
69 | events "passed", "skipped", "failed"
70 | }
71 | }
72 |
73 | mainClassName = 'org.hyperledger.fabric.contract.ContractRouter'
74 |
75 | shadowJar {
76 | baseName = 'chaincode'
77 | version = null
78 | classifier = null
79 |
80 | manifest {
81 | attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter'
82 | }
83 | }
84 |
85 | check.dependsOn jacocoTestCoverageVerification
86 | installDist.dependsOn check
87 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | package org.hyperledger.fabric.samples.assettransfer;
6 |
7 | import static org.assertj.core.api.Assertions.assertThat;
8 |
9 | import org.junit.jupiter.api.Nested;
10 | import org.junit.jupiter.api.Test;
11 |
12 | public final class AssetTest {
13 |
14 | @Nested
15 | class Equality {
16 |
17 | @Test
18 | public void isReflexive() {
19 | Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100);
20 |
21 | assertThat(asset).isEqualTo(asset);
22 | }
23 |
24 | @Test
25 | public void isSymmetric() {
26 | Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100);
27 | Asset assetB = new Asset("asset1", "Blue", 20, "Guy", 100);
28 |
29 | assertThat(assetA).isEqualTo(assetB);
30 | assertThat(assetB).isEqualTo(assetA);
31 | }
32 |
33 | @Test
34 | public void isTransitive() {
35 | Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100);
36 | Asset assetB = new Asset("asset1", "Blue", 20, "Guy", 100);
37 | Asset assetC = new Asset("asset1", "Blue", 20, "Guy", 100);
38 |
39 | assertThat(assetA).isEqualTo(assetB);
40 | assertThat(assetB).isEqualTo(assetC);
41 | assertThat(assetA).isEqualTo(assetC);
42 | }
43 |
44 | @Test
45 | public void handlesInequality() {
46 | Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100);
47 | Asset assetB = new Asset("asset2", "Red", 40, "Lady", 200);
48 |
49 | assertThat(assetA).isNotEqualTo(assetB);
50 | }
51 |
52 | @Test
53 | public void handlesOtherObjects() {
54 | Asset assetA = new Asset("asset1", "Blue", 20, "Guy", 100);
55 | String assetB = "not a asset";
56 |
57 | assertThat(assetA).isNotEqualTo(assetB);
58 | }
59 |
60 | @Test
61 | public void handlesNull() {
62 | Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100);
63 |
64 | assertThat(asset).isNotEqualTo(null);
65 | }
66 | }
67 |
68 | @Test
69 | public void toStringIdentifiesAsset() {
70 | Asset asset = new Asset("asset1", "Blue", 20, "Guy", 100);
71 |
72 | assertThat(asset.toString()).isEqualTo("Asset@e04f6c53 [assetID=asset1, color=Blue, size=20, owner=Guy, appraisedValue=100]");
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/Asset.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | package org.hyperledger.fabric.samples.assettransfer;
6 |
7 | import java.util.Objects;
8 |
9 | import org.hyperledger.fabric.contract.annotation.DataType;
10 | import org.hyperledger.fabric.contract.annotation.Property;
11 |
12 | import com.owlike.genson.annotation.JsonProperty;
13 |
14 | @DataType()
15 | public final class Asset {
16 |
17 | @Property()
18 | private final String assetID;
19 |
20 | @Property()
21 | private final String color;
22 |
23 | @Property()
24 | private final int size;
25 |
26 | @Property()
27 | private final String owner;
28 |
29 | @Property()
30 | private final int appraisedValue;
31 |
32 | public String getAssetID() {
33 | return assetID;
34 | }
35 |
36 | public String getColor() {
37 | return color;
38 | }
39 |
40 | public int getSize() {
41 | return size;
42 | }
43 |
44 | public String getOwner() {
45 | return owner;
46 | }
47 |
48 | public int getAppraisedValue() {
49 | return appraisedValue;
50 | }
51 |
52 | public Asset(@JsonProperty("assetID") final String assetID, @JsonProperty("color") final String color,
53 | @JsonProperty("size") final int size, @JsonProperty("owner") final String owner,
54 | @JsonProperty("appraisedValue") final int appraisedValue) {
55 | this.assetID = assetID;
56 | this.color = color;
57 | this.size = size;
58 | this.owner = owner;
59 | this.appraisedValue = appraisedValue;
60 | }
61 |
62 | @Override
63 | public boolean equals(final Object obj) {
64 | if (this == obj) {
65 | return true;
66 | }
67 |
68 | if ((obj == null) || (getClass() != obj.getClass())) {
69 | return false;
70 | }
71 |
72 | Asset other = (Asset) obj;
73 |
74 | return Objects.deepEquals(
75 | new String[] {getAssetID(), getColor(), getOwner()},
76 | new String[] {other.getAssetID(), other.getColor(), other.getOwner()})
77 | &&
78 | Objects.deepEquals(
79 | new int[] {getSize(), getAppraisedValue()},
80 | new int[] {other.getSize(), other.getAppraisedValue()});
81 | }
82 |
83 | @Override
84 | public int hashCode() {
85 | return Objects.hash(getAssetID(), getColor(), getSize(), getOwner(), getAppraisedValue());
86 | }
87 |
88 | @Override
89 | public String toString() {
90 | return this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + " [assetID=" + assetID + ", color="
91 | + color + ", size=" + size + ", owner=" + owner + ", appraisedValue=" + appraisedValue + "]";
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "asset-transfer-basic",
3 | "version": "1.0.0",
4 | "description": "Asset Transfer Basic contract implemented in TypeScript",
5 | "main": "dist/index.js",
6 | "typings": "dist/index.d.ts",
7 | "files": [
8 | "lib/**/*"
9 | ],
10 | "engines": {
11 | "node": ">=12.15.0"
12 | },
13 | "fabric":{
14 | "files": [
15 | "lib/**/*"
16 | ],
17 | "label":"asset-transfer-basic"
18 | },
19 | "scripts": {
20 | "clean": "rimraf lib",
21 | "test": "nyc mocha -r ts-node/register src/**/*.spec.ts",
22 | "meta": "fabric-chaincode-node metadata generate",
23 | "format": "prettier --write \"{src,test}/**/*.ts\"",
24 | "lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix",
25 | "build": "npm run format && npm run clean && npm run lint && echo Using TypeScript && tsc --version && tsc --pretty",
26 | "package:cc": "../ccaas_pkg.sh",
27 | "package:image": "npm run build && npm shrinkwrap && docker build -f ./Dockerfile -t asset-transfer-basic .",
28 | "release": "standard-version",
29 | "start": "fabric-chaincode-node start",
30 | "start:server-nontls": "set -x && fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID",
31 | "start:server": "set -x && fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID --chaincode-tls-key-file=/hyperledger/privatekey.pem --chaincode-tls-client-cacert-file=/hyperledger/rootcert.pem --chaincode-tls-cert-file=/hyperledger/cert.pem",
32 | "start:server-debug": "set -x && NODE_OPTIONS='--inspect=0.0.0.0:9229' fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID"
33 | },
34 | "engineStrict": true,
35 | "author": "whitemat@uk.ibm.com",
36 | "license": "Apache-2.0",
37 | "dependencies": {
38 | "fabric-contract-api": "^2.4.0",
39 | "fabric-shim": "^2.4.0",
40 | "source-map-support": "^0.5.19",
41 | "json-stringify-deterministic": "^1.0.1",
42 | "sort-keys-recursive": "^2.1.2"
43 | },
44 | "devDependencies": {
45 | "@hyperledgendary/weftility": "^1.0.1-beta.0",
46 | "@types/mkdirp": "^1.0.1",
47 | "@types/node": "^14.0.11",
48 | "@types/rimraf": "^3.0.0",
49 | "@types/yargs": "^15.0.3",
50 | "@typescript-eslint/eslint-plugin": "^4.3.0",
51 | "@typescript-eslint/parser": "^4.3.0",
52 | "chai": "^4.2.0",
53 | "chai-as-promised": "^7.1.1",
54 | "eslint": "^7.2.0",
55 | "eslint-config-prettier": "^6.11.0",
56 | "eslint-plugin-prettier": "^3.1.3",
57 | "mocha": "^7.1.1",
58 | "nyc": "^15.0.0",
59 | "prettier": "^2.0.5",
60 | "sinon": "^9.0.1",
61 | "sinon-chai": "^3.5.0",
62 | "standard-version": "^9.0.0",
63 | "typescript": "^4.0.3"
64 | },
65 | "nyc": {
66 | "exclude": [
67 | ".eslintrc.js",
68 | "coverage/**",
69 | "test/**"
70 | ],
71 | "reporter": [
72 | "text-summary",
73 | "html"
74 | ],
75 | "all": true,
76 | "check-coverage": true,
77 | "statements": 100,
78 | "branches": 100,
79 | "functions": 100,
80 | "lines": 100
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/dist/asset.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6 | return c > 3 && r && Object.defineProperty(target, key, r), r;
7 | };
8 | var __metadata = (this && this.__metadata) || function (k, v) {
9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10 | };
11 | Object.defineProperty(exports, "__esModule", { value: true });
12 | exports.Asset = void 0;
13 | const fabric_contract_api_1 = require("fabric-contract-api");
14 | let Asset = class Asset {
15 | constructor(id, color, size, owner, appraisedValue) {
16 | this.docType = 'Asset';
17 | this.id = id;
18 | this.color = color;
19 | this.size = size;
20 | this.owner = owner;
21 | this.appraisedValue = appraisedValue;
22 | }
23 | };
24 | __decorate([
25 | fabric_contract_api_1.Property(),
26 | __metadata("design:type", String)
27 | ], Asset.prototype, "docType", void 0);
28 | __decorate([
29 | fabric_contract_api_1.Property(),
30 | __metadata("design:type", String)
31 | ], Asset.prototype, "id", void 0);
32 | __decorate([
33 | fabric_contract_api_1.Property(),
34 | __metadata("design:type", String)
35 | ], Asset.prototype, "color", void 0);
36 | __decorate([
37 | fabric_contract_api_1.Property(),
38 | __metadata("design:type", Number)
39 | ], Asset.prototype, "size", void 0);
40 | __decorate([
41 | fabric_contract_api_1.Property(),
42 | __metadata("design:type", String)
43 | ], Asset.prototype, "owner", void 0);
44 | __decorate([
45 | fabric_contract_api_1.Property(),
46 | __metadata("design:type", Number)
47 | ], Asset.prototype, "appraisedValue", void 0);
48 | Asset = __decorate([
49 | fabric_contract_api_1.Object(),
50 | __metadata("design:paramtypes", [String, String, Number, String, Number])
51 | ], Asset);
52 | exports.Asset = Asset;
53 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYXNzZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBSUEsNkRBQXVEO0FBR3ZELElBQWEsS0FBSyxHQUFsQixNQUFhLEtBQUs7SUFtQmQsWUFBbUIsRUFBVSxFQUFFLEtBQWEsRUFBRSxJQUFZLEVBQUUsS0FBYSxFQUFFLGNBQXNCO1FBQzdGLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDO1FBQ2IsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDbkIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDbkIsSUFBSSxDQUFDLGNBQWMsR0FBRyxjQUFjLENBQUM7SUFDekMsQ0FBQztDQUNKLENBQUE7QUF6Qkc7SUFEQyw4QkFBUSxFQUFFOztzQ0FDYTtBQUd4QjtJQURDLDhCQUFRLEVBQUU7O2lDQUNPO0FBR2xCO0lBREMsOEJBQVEsRUFBRTs7b0NBQ1U7QUFHckI7SUFEQyw4QkFBUSxFQUFFOzttQ0FDUztBQUdwQjtJQURDLDhCQUFRLEVBQUU7O29DQUNVO0FBR3JCO0lBREMsOEJBQVEsRUFBRTs7NkNBQ21CO0FBakJyQixLQUFLO0lBRGpCLDRCQUFNLEVBQUU7O0dBQ0ksS0FBSyxDQTJCakI7QUEzQlksc0JBQUsiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuKi9cblxuaW1wb3J0IHsgT2JqZWN0LCBQcm9wZXJ0eSB9IGZyb20gJ2ZhYnJpYy1jb250cmFjdC1hcGknO1xuXG5AT2JqZWN0KClcbmV4cG9ydCBjbGFzcyBBc3NldCB7XG4gICAgQFByb3BlcnR5KClcbiAgICBwdWJsaWMgZG9jVHlwZT86IHN0cmluZztcblxuICAgIEBQcm9wZXJ0eSgpXG4gICAgcHVibGljIGlkOiBzdHJpbmc7XG5cbiAgICBAUHJvcGVydHkoKVxuICAgIHB1YmxpYyBjb2xvcjogc3RyaW5nO1xuXG4gICAgQFByb3BlcnR5KClcbiAgICBwdWJsaWMgc2l6ZTogbnVtYmVyO1xuXG4gICAgQFByb3BlcnR5KClcbiAgICBwdWJsaWMgb3duZXI6IHN0cmluZztcblxuICAgIEBQcm9wZXJ0eSgpXG4gICAgcHVibGljIGFwcHJhaXNlZFZhbHVlOiBudW1iZXI7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IoaWQ6IHN0cmluZywgY29sb3I6IHN0cmluZywgc2l6ZTogbnVtYmVyLCBvd25lcjogc3RyaW5nLCBhcHByYWlzZWRWYWx1ZTogbnVtYmVyKSB7XG4gICAgICAgIHRoaXMuZG9jVHlwZSA9ICdBc3NldCc7XG4gICAgICAgIHRoaXMuaWQgPSBpZDtcbiAgICAgICAgdGhpcy5jb2xvciA9IGNvbG9yO1xuICAgICAgICB0aGlzLnNpemUgPSBzaXplO1xuICAgICAgICB0aGlzLm93bmVyID0gb3duZXI7XG4gICAgICAgIHRoaXMuYXBwcmFpc2VkVmFsdWUgPSBhcHByYWlzZWRWYWx1ZTtcbiAgICB9XG59XG4iXX0=
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/src/assetTransfer.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | import { Context, Contract, Info, Returns, Transaction } from 'fabric-contract-api';
6 | import { Asset } from './asset';
7 |
8 | @Info({ title: 'AssetTransfer', description: 'Smart contract for trading assets' })
9 | export class AssetTransferContract extends Contract {
10 | @Transaction()
11 | public async InitLedger(ctx: Context): Promise {
12 | const assets: Asset[] = [
13 | {
14 | id: 'asset1',
15 | color: 'blue',
16 | size: 5,
17 | owner: 'Tomoko',
18 | appraisedValue: 300,
19 | },
20 | {
21 | id: 'asset2',
22 | color: 'red',
23 | size: 5,
24 | owner: 'Brad',
25 | appraisedValue: 400,
26 | },
27 | {
28 | id: 'asset3',
29 | color: 'green',
30 | size: 10,
31 | owner: 'Jin Soo',
32 | appraisedValue: 500,
33 | },
34 | {
35 | id: 'asset4',
36 | color: 'yellow',
37 | size: 10,
38 | owner: 'Max',
39 | appraisedValue: 600,
40 | },
41 | {
42 | id: 'asset5',
43 | color: 'black',
44 | size: 15,
45 | owner: 'Adriana',
46 | appraisedValue: 700,
47 | },
48 | {
49 | id: 'asset6',
50 | color: 'white',
51 | size: 15,
52 | owner: 'Michel',
53 | appraisedValue: 800,
54 | },
55 | ];
56 |
57 | for (const asset of assets) {
58 | asset.docType = 'asset';
59 | await ctx.stub.putState(asset.id, Buffer.from(JSON.stringify(asset)));
60 | console.info(`Asset ${asset.id} initialized`);
61 | }
62 | }
63 |
64 | // CreateAsset issues a new asset to the world state with given details.
65 | @Transaction()
66 | public async CreateAsset(
67 | ctx: Context,
68 | id: string,
69 | color: string,
70 | size: number,
71 | owner: string,
72 | appraisedValue: number,
73 | ): Promise {
74 | const asset = {
75 | id: id,
76 | color: color,
77 | size: size,
78 | owner: owner,
79 | appraisedValue: appraisedValue,
80 | };
81 | await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
82 | }
83 |
84 | // ReadAsset returns the asset stored in the world state with given id.
85 | @Transaction(false)
86 | public async ReadAsset(ctx: Context, id: string): Promise {
87 | const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state
88 | if (!assetJSON || assetJSON.length === 0) {
89 | throw new Error(`The asset ${id} does not exist`);
90 | }
91 | return assetJSON.toString();
92 | }
93 |
94 | // UpdateAsset updates an existing asset in the world state with provided parameters.
95 | @Transaction()
96 | public async UpdateAsset(
97 | ctx: Context,
98 | id: string,
99 | color: string,
100 | size: number,
101 | owner: string,
102 | appraisedValue: number,
103 | ): Promise {
104 | const exists = await this.AssetExists(ctx, id);
105 | if (!exists) {
106 | throw new Error(`The asset ${id} does not exist`);
107 | }
108 |
109 | // overwriting original asset with new asset
110 | const updatedAsset = {
111 | id: id,
112 | color: color,
113 | size: size,
114 | owner: owner,
115 | appraisedValue: appraisedValue,
116 | };
117 | return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset)));
118 | }
119 |
120 | // DeleteAsset deletes an given asset from the world state.
121 | @Transaction()
122 | public async DeleteAsset(ctx: Context, id: string): Promise {
123 | const exists = await this.AssetExists(ctx, id);
124 | if (!exists) {
125 | throw new Error(`The asset ${id} does not exist`);
126 | }
127 | return ctx.stub.deleteState(id);
128 | }
129 |
130 | // AssetExists returns true when asset with given id exists in world state.
131 | @Transaction(false)
132 | @Returns('boolean')
133 | public async AssetExists(ctx: Context, id: string): Promise {
134 | const assetJSON = await ctx.stub.getState(id);
135 | return assetJSON && assetJSON.length > 0;
136 | }
137 |
138 | // TransferAsset updates the owner field of asset with given id in the world state.
139 | @Transaction()
140 | public async TransferAsset(ctx: Context, id: string, newowner: string): Promise {
141 | const assetString = await this.ReadAsset(ctx, id);
142 | const asset = JSON.parse(assetString);
143 | asset.owner = newowner;
144 | await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
145 | }
146 |
147 | // GetAllAssets returns all assets found in the world state.
148 | @Transaction(false)
149 | @Returns('string')
150 | public async GetAllAssets(ctx: Context): Promise {
151 | const allResults = [];
152 | // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace.
153 | const iterator = await ctx.stub.getStateByRange('', '');
154 | let result = await iterator.next();
155 | while (!result.done) {
156 | const strValue = Buffer.from(result.value.value.toString()).toString('utf8');
157 | let record;
158 | try {
159 | record = JSON.parse(strValue);
160 | } catch (err) {
161 | console.log(err);
162 | record = strValue;
163 | }
164 | allResults.push({ Key: result.value.key, Record: record });
165 | result = await iterator.next();
166 | }
167 | return JSON.stringify(allResults);
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/config/checkstyle/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
20 |
21 |
22 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | package org.hyperledger.fabric.samples.assettransfer;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 |
11 | import org.hyperledger.fabric.contract.Context;
12 | import org.hyperledger.fabric.contract.ContractInterface;
13 | import org.hyperledger.fabric.contract.annotation.Contact;
14 | import org.hyperledger.fabric.contract.annotation.Contract;
15 | import org.hyperledger.fabric.contract.annotation.Default;
16 | import org.hyperledger.fabric.contract.annotation.Info;
17 | import org.hyperledger.fabric.contract.annotation.License;
18 | import org.hyperledger.fabric.contract.annotation.Transaction;
19 | import org.hyperledger.fabric.shim.ChaincodeException;
20 | import org.hyperledger.fabric.shim.ChaincodeStub;
21 | import org.hyperledger.fabric.shim.ledger.KeyValue;
22 | import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
23 |
24 | import com.owlike.genson.Genson;
25 |
26 | @Contract(
27 | name = "basic",
28 | info = @Info(
29 | title = "Asset Transfer",
30 | description = "The hyperlegendary asset transfer",
31 | version = "0.0.1-SNAPSHOT",
32 | license = @License(
33 | name = "Apache 2.0 License",
34 | url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
35 | contact = @Contact(
36 | email = "a.transfer@example.com",
37 | name = "Adrian Transfer",
38 | url = "https://hyperledger.example.com")))
39 | @Default
40 | public final class AssetTransfer implements ContractInterface {
41 |
42 | private final Genson genson = new Genson();
43 |
44 | private enum AssetTransferErrors {
45 | ASSET_NOT_FOUND,
46 | ASSET_ALREADY_EXISTS
47 | }
48 |
49 | /**
50 | * Creates some initial assets on the ledger.
51 | *
52 | * @param ctx the transaction context
53 | */
54 | @Transaction(intent = Transaction.TYPE.SUBMIT)
55 | public void InitLedger(final Context ctx) {
56 | ChaincodeStub stub = ctx.getStub();
57 |
58 | CreateAsset(ctx, "asset1", "blue", 5, "Tomoko", 300);
59 | CreateAsset(ctx, "asset2", "red", 5, "Brad", 400);
60 | CreateAsset(ctx, "asset3", "green", 10, "Jin Soo", 500);
61 | CreateAsset(ctx, "asset4", "yellow", 10, "Max", 600);
62 | CreateAsset(ctx, "asset5", "black", 15, "Adrian", 700);
63 | CreateAsset(ctx, "asset6", "white", 15, "Michel", 700);
64 |
65 | }
66 |
67 | /**
68 | * Creates a new asset on the ledger.
69 | *
70 | * @param ctx the transaction context
71 | * @param assetID the ID of the new asset
72 | * @param color the color of the new asset
73 | * @param size the size for the new asset
74 | * @param owner the owner of the new asset
75 | * @param appraisedValue the appraisedValue of the new asset
76 | * @return the created asset
77 | */
78 | @Transaction(intent = Transaction.TYPE.SUBMIT)
79 | public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size,
80 | final String owner, final int appraisedValue) {
81 | ChaincodeStub stub = ctx.getStub();
82 |
83 | if (AssetExists(ctx, assetID)) {
84 | String errorMessage = String.format("Asset %s already exists", assetID);
85 | System.out.println(errorMessage);
86 | throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_ALREADY_EXISTS.toString());
87 | }
88 |
89 | Asset asset = new Asset(assetID, color, size, owner, appraisedValue);
90 | //Use Genson to convert the Asset into string, sort it alphabetically and serialize it into a json string
91 | String sortedJson = genson.serialize(asset);
92 | stub.putStringState(assetID, sortedJson);
93 |
94 | return asset;
95 | }
96 |
97 | /**
98 | * Retrieves an asset with the specified ID from the ledger.
99 | *
100 | * @param ctx the transaction context
101 | * @param assetID the ID of the asset
102 | * @return the asset found on the ledger if there was one
103 | */
104 | @Transaction(intent = Transaction.TYPE.EVALUATE)
105 | public Asset ReadAsset(final Context ctx, final String assetID) {
106 | ChaincodeStub stub = ctx.getStub();
107 | String assetJSON = stub.getStringState(assetID);
108 |
109 | if (assetJSON == null || assetJSON.isEmpty()) {
110 | String errorMessage = String.format("Asset %s does not exist", assetID);
111 | System.out.println(errorMessage);
112 | throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
113 | }
114 |
115 | Asset asset = genson.deserialize(assetJSON, Asset.class);
116 | return asset;
117 | }
118 |
119 | /**
120 | * Updates the properties of an asset on the ledger.
121 | *
122 | * @param ctx the transaction context
123 | * @param assetID the ID of the asset being updated
124 | * @param color the color of the asset being updated
125 | * @param size the size of the asset being updated
126 | * @param owner the owner of the asset being updated
127 | * @param appraisedValue the appraisedValue of the asset being updated
128 | * @return the transferred asset
129 | */
130 | @Transaction(intent = Transaction.TYPE.SUBMIT)
131 | public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size,
132 | final String owner, final int appraisedValue) {
133 | ChaincodeStub stub = ctx.getStub();
134 |
135 | if (!AssetExists(ctx, assetID)) {
136 | String errorMessage = String.format("Asset %s does not exist", assetID);
137 | System.out.println(errorMessage);
138 | throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
139 | }
140 |
141 | Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue);
142 | //Use Genson to convert the Asset into string, sort it alphabetically and serialize it into a json string
143 | String sortedJson = genson.serialize(newAsset);
144 | stub.putStringState(assetID, sortedJson);
145 | return newAsset;
146 | }
147 |
148 | /**
149 | * Deletes asset on the ledger.
150 | *
151 | * @param ctx the transaction context
152 | * @param assetID the ID of the asset being deleted
153 | */
154 | @Transaction(intent = Transaction.TYPE.SUBMIT)
155 | public void DeleteAsset(final Context ctx, final String assetID) {
156 | ChaincodeStub stub = ctx.getStub();
157 |
158 | if (!AssetExists(ctx, assetID)) {
159 | String errorMessage = String.format("Asset %s does not exist", assetID);
160 | System.out.println(errorMessage);
161 | throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
162 | }
163 |
164 | stub.delState(assetID);
165 | }
166 |
167 | /**
168 | * Checks the existence of the asset on the ledger
169 | *
170 | * @param ctx the transaction context
171 | * @param assetID the ID of the asset
172 | * @return boolean indicating the existence of the asset
173 | */
174 | @Transaction(intent = Transaction.TYPE.EVALUATE)
175 | public boolean AssetExists(final Context ctx, final String assetID) {
176 | ChaincodeStub stub = ctx.getStub();
177 | String assetJSON = stub.getStringState(assetID);
178 |
179 | return (assetJSON != null && !assetJSON.isEmpty());
180 | }
181 |
182 | /**
183 | * Changes the owner of a asset on the ledger.
184 | *
185 | * @param ctx the transaction context
186 | * @param assetID the ID of the asset being transferred
187 | * @param newOwner the new owner
188 | * @return the old owner
189 | */
190 | @Transaction(intent = Transaction.TYPE.SUBMIT)
191 | public String TransferAsset(final Context ctx, final String assetID, final String newOwner) {
192 | ChaincodeStub stub = ctx.getStub();
193 | String assetJSON = stub.getStringState(assetID);
194 |
195 | if (assetJSON == null || assetJSON.isEmpty()) {
196 | String errorMessage = String.format("Asset %s does not exist", assetID);
197 | System.out.println(errorMessage);
198 | throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
199 | }
200 |
201 | Asset asset = genson.deserialize(assetJSON, Asset.class);
202 |
203 | Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue());
204 | //Use a Genson to conver the Asset into string, sort it alphabetically and serialize it into a json string
205 | String sortedJson = genson.serialize(newAsset);
206 | stub.putStringState(assetID, sortedJson);
207 |
208 | return asset.getOwner();
209 | }
210 |
211 | /**
212 | * Retrieves all assets from the ledger.
213 | *
214 | * @param ctx the transaction context
215 | * @return array of assets found on the ledger
216 | */
217 | @Transaction(intent = Transaction.TYPE.EVALUATE)
218 | public String GetAllAssets(final Context ctx) {
219 | ChaincodeStub stub = ctx.getStub();
220 |
221 | List queryResults = new ArrayList();
222 |
223 | // To retrieve all assets from the ledger use getStateByRange with empty startKey & endKey.
224 | // Giving empty startKey & endKey is interpreted as all the keys from beginning to end.
225 | // As another example, if you use startKey = 'asset0', endKey = 'asset9' ,
226 | // then getStateByRange will retrieve asset with keys between asset0 (inclusive) and asset9 (exclusive) in lexical order.
227 | QueryResultsIterator results = stub.getStateByRange("", "");
228 |
229 | for (KeyValue result: results) {
230 | Asset asset = genson.deserialize(result.getStringValue(), Asset.class);
231 | System.out.println(asset);
232 | queryResults.add(asset);
233 | }
234 |
235 | final String response = genson.serialize(queryResults);
236 |
237 | return response;
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/contracts/chaincode-java/src/test/java/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: Apache-2.0
3 | */
4 |
5 | package org.hyperledger.fabric.samples.assettransfer;
6 |
7 | import static org.assertj.core.api.Assertions.assertThat;
8 | import static org.assertj.core.api.ThrowableAssert.catchThrowable;
9 | import static org.mockito.Mockito.inOrder;
10 | import static org.mockito.Mockito.mock;
11 | import static org.mockito.Mockito.verifyZeroInteractions;
12 | import static org.mockito.Mockito.when;
13 |
14 | import java.util.ArrayList;
15 | import java.util.Iterator;
16 | import java.util.List;
17 |
18 | import org.hyperledger.fabric.contract.Context;
19 | import org.hyperledger.fabric.shim.ChaincodeException;
20 | import org.hyperledger.fabric.shim.ChaincodeStub;
21 | import org.hyperledger.fabric.shim.ledger.KeyValue;
22 | import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
23 | import org.junit.jupiter.api.Nested;
24 | import org.junit.jupiter.api.Test;
25 | import org.mockito.InOrder;
26 |
27 | public final class AssetTransferTest {
28 |
29 | private final class MockKeyValue implements KeyValue {
30 |
31 | private final String key;
32 | private final String value;
33 |
34 | MockKeyValue(final String key, final String value) {
35 | super();
36 | this.key = key;
37 | this.value = value;
38 | }
39 |
40 | @Override
41 | public String getKey() {
42 | return this.key;
43 | }
44 |
45 | @Override
46 | public String getStringValue() {
47 | return this.value;
48 | }
49 |
50 | @Override
51 | public byte[] getValue() {
52 | return this.value.getBytes();
53 | }
54 |
55 | }
56 |
57 | private final class MockAssetResultsIterator implements QueryResultsIterator {
58 |
59 | private final List assetList;
60 |
61 | MockAssetResultsIterator() {
62 | super();
63 |
64 | assetList = new ArrayList();
65 |
66 | assetList.add(new MockKeyValue("asset1",
67 | "{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }"));
68 | assetList.add(new MockKeyValue("asset2",
69 | "{ \"assetID\": \"asset2\", \"color\": \"red\", \"size\": 5,\"owner\": \"Brad\", \"appraisedValue\": 400 }"));
70 | assetList.add(new MockKeyValue("asset3",
71 | "{ \"assetID\": \"asset3\", \"color\": \"green\", \"size\": 10,\"owner\": \"Jin Soo\", \"appraisedValue\": 500 }"));
72 | assetList.add(new MockKeyValue("asset4",
73 | "{ \"assetID\": \"asset4\", \"color\": \"yellow\", \"size\": 10,\"owner\": \"Max\", \"appraisedValue\": 600 }"));
74 | assetList.add(new MockKeyValue("asset5",
75 | "{ \"assetID\": \"asset5\", \"color\": \"black\", \"size\": 15,\"owner\": \"Adrian\", \"appraisedValue\": 700 }"));
76 | assetList.add(new MockKeyValue("asset6",
77 | "{ \"assetID\": \"asset6\", \"color\": \"white\", \"size\": 15,\"owner\": \"Michel\", \"appraisedValue\": 800 }"));
78 | }
79 |
80 | @Override
81 | public Iterator iterator() {
82 | return assetList.iterator();
83 | }
84 |
85 | @Override
86 | public void close() throws Exception {
87 | // do nothing
88 | }
89 |
90 | }
91 |
92 | @Test
93 | public void invokeUnknownTransaction() {
94 | AssetTransfer contract = new AssetTransfer();
95 | Context ctx = mock(Context.class);
96 |
97 | Throwable thrown = catchThrowable(() -> {
98 | contract.unknownTransaction(ctx);
99 | });
100 |
101 | assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
102 | .hasMessage("Undefined contract method called");
103 | assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo(null);
104 |
105 | verifyZeroInteractions(ctx);
106 | }
107 |
108 | @Nested
109 | class InvokeReadAssetTransaction {
110 |
111 | @Test
112 | public void whenAssetExists() {
113 | AssetTransfer contract = new AssetTransfer();
114 | Context ctx = mock(Context.class);
115 | ChaincodeStub stub = mock(ChaincodeStub.class);
116 | when(ctx.getStub()).thenReturn(stub);
117 | when(stub.getStringState("asset1"))
118 | .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }");
119 |
120 | Asset asset = contract.ReadAsset(ctx, "asset1");
121 |
122 | assertThat(asset).isEqualTo(new Asset("asset1", "blue", 5, "Tomoko", 300));
123 | }
124 |
125 | @Test
126 | public void whenAssetDoesNotExist() {
127 | AssetTransfer contract = new AssetTransfer();
128 | Context ctx = mock(Context.class);
129 | ChaincodeStub stub = mock(ChaincodeStub.class);
130 | when(ctx.getStub()).thenReturn(stub);
131 | when(stub.getStringState("asset1")).thenReturn("");
132 |
133 | Throwable thrown = catchThrowable(() -> {
134 | contract.ReadAsset(ctx, "asset1");
135 | });
136 |
137 | assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
138 | .hasMessage("Asset asset1 does not exist");
139 | assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes());
140 | }
141 | }
142 |
143 | @Test
144 | void invokeInitLedgerTransaction() {
145 | AssetTransfer contract = new AssetTransfer();
146 | Context ctx = mock(Context.class);
147 | ChaincodeStub stub = mock(ChaincodeStub.class);
148 | when(ctx.getStub()).thenReturn(stub);
149 |
150 | contract.InitLedger(ctx);
151 |
152 | InOrder inOrder = inOrder(stub);
153 | inOrder.verify(stub).putStringState("asset1", "{\"appraisedValue\":300,\"assetID\":\"asset1\",\"color\":\"blue\",\"owner\":\"Tomoko\",\"size\":5}");
154 | inOrder.verify(stub).putStringState("asset2", "{\"appraisedValue\":400,\"assetID\":\"asset2\",\"color\":\"red\",\"owner\":\"Brad\",\"size\":5}");
155 | inOrder.verify(stub).putStringState("asset3", "{\"appraisedValue\":500,\"assetID\":\"asset3\",\"color\":\"green\",\"owner\":\"Jin Soo\",\"size\":10}");
156 | inOrder.verify(stub).putStringState("asset4", "{\"appraisedValue\":600,\"assetID\":\"asset4\",\"color\":\"yellow\",\"owner\":\"Max\",\"size\":10}");
157 | inOrder.verify(stub).putStringState("asset5", "{\"appraisedValue\":700,\"assetID\":\"asset5\",\"color\":\"black\",\"owner\":\"Adrian\",\"size\":15}");
158 |
159 | }
160 |
161 | @Nested
162 | class InvokeCreateAssetTransaction {
163 |
164 | @Test
165 | public void whenAssetExists() {
166 | AssetTransfer contract = new AssetTransfer();
167 | Context ctx = mock(Context.class);
168 | ChaincodeStub stub = mock(ChaincodeStub.class);
169 | when(ctx.getStub()).thenReturn(stub);
170 | when(stub.getStringState("asset1"))
171 | .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }");
172 |
173 | Throwable thrown = catchThrowable(() -> {
174 | contract.CreateAsset(ctx, "asset1", "blue", 45, "Siobhán", 60);
175 | });
176 |
177 | assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
178 | .hasMessage("Asset asset1 already exists");
179 | assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_ALREADY_EXISTS".getBytes());
180 | }
181 |
182 | @Test
183 | public void whenAssetDoesNotExist() {
184 | AssetTransfer contract = new AssetTransfer();
185 | Context ctx = mock(Context.class);
186 | ChaincodeStub stub = mock(ChaincodeStub.class);
187 | when(ctx.getStub()).thenReturn(stub);
188 | when(stub.getStringState("asset1")).thenReturn("");
189 |
190 | Asset asset = contract.CreateAsset(ctx, "asset1", "blue", 45, "Siobhán", 60);
191 |
192 | assertThat(asset).isEqualTo(new Asset("asset1", "blue", 45, "Siobhán", 60));
193 | }
194 | }
195 |
196 | @Test
197 | void invokeGetAllAssetsTransaction() {
198 | AssetTransfer contract = new AssetTransfer();
199 | Context ctx = mock(Context.class);
200 | ChaincodeStub stub = mock(ChaincodeStub.class);
201 | when(ctx.getStub()).thenReturn(stub);
202 | when(stub.getStateByRange("", "")).thenReturn(new MockAssetResultsIterator());
203 |
204 | String assets = contract.GetAllAssets(ctx);
205 |
206 | assertThat(assets).isEqualTo("[{\"appraisedValue\":300,\"assetID\":\"asset1\",\"color\":\"blue\",\"owner\":\"Tomoko\",\"size\":5},"
207 | + "{\"appraisedValue\":400,\"assetID\":\"asset2\",\"color\":\"red\",\"owner\":\"Brad\",\"size\":5},"
208 | + "{\"appraisedValue\":500,\"assetID\":\"asset3\",\"color\":\"green\",\"owner\":\"Jin Soo\",\"size\":10},"
209 | + "{\"appraisedValue\":600,\"assetID\":\"asset4\",\"color\":\"yellow\",\"owner\":\"Max\",\"size\":10},"
210 | + "{\"appraisedValue\":700,\"assetID\":\"asset5\",\"color\":\"black\",\"owner\":\"Adrian\",\"size\":15},"
211 | + "{\"appraisedValue\":800,\"assetID\":\"asset6\",\"color\":\"white\",\"owner\":\"Michel\",\"size\":15}]");
212 |
213 | }
214 |
215 | @Nested
216 | class TransferAssetTransaction {
217 |
218 | @Test
219 | public void whenAssetExists() {
220 | AssetTransfer contract = new AssetTransfer();
221 | Context ctx = mock(Context.class);
222 | ChaincodeStub stub = mock(ChaincodeStub.class);
223 | when(ctx.getStub()).thenReturn(stub);
224 | when(stub.getStringState("asset1"))
225 | .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }");
226 |
227 | String oldOwner = contract.TransferAsset(ctx, "asset1", "Dr Evil");
228 |
229 | assertThat(oldOwner).isEqualTo("Tomoko");
230 | }
231 |
232 | @Test
233 | public void whenAssetDoesNotExist() {
234 | AssetTransfer contract = new AssetTransfer();
235 | Context ctx = mock(Context.class);
236 | ChaincodeStub stub = mock(ChaincodeStub.class);
237 | when(ctx.getStub()).thenReturn(stub);
238 | when(stub.getStringState("asset1")).thenReturn("");
239 |
240 | Throwable thrown = catchThrowable(() -> {
241 | contract.TransferAsset(ctx, "asset1", "Dr Evil");
242 | });
243 |
244 | assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
245 | .hasMessage("Asset asset1 does not exist");
246 | assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes());
247 | }
248 | }
249 |
250 | @Nested
251 | class UpdateAssetTransaction {
252 |
253 | @Test
254 | public void whenAssetExists() {
255 | AssetTransfer contract = new AssetTransfer();
256 | Context ctx = mock(Context.class);
257 | ChaincodeStub stub = mock(ChaincodeStub.class);
258 | when(ctx.getStub()).thenReturn(stub);
259 | when(stub.getStringState("asset1"))
260 | .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 45, \"owner\": \"Arturo\", \"appraisedValue\": 60 }");
261 |
262 | Asset asset = contract.UpdateAsset(ctx, "asset1", "pink", 45, "Arturo", 600);
263 |
264 | assertThat(asset).isEqualTo(new Asset("asset1", "pink", 45, "Arturo", 600));
265 | }
266 |
267 | @Test
268 | public void whenAssetDoesNotExist() {
269 | AssetTransfer contract = new AssetTransfer();
270 | Context ctx = mock(Context.class);
271 | ChaincodeStub stub = mock(ChaincodeStub.class);
272 | when(ctx.getStub()).thenReturn(stub);
273 | when(stub.getStringState("asset1")).thenReturn("");
274 |
275 | Throwable thrown = catchThrowable(() -> {
276 | contract.TransferAsset(ctx, "asset1", "Alex");
277 | });
278 |
279 | assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
280 | .hasMessage("Asset asset1 does not exist");
281 | assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes());
282 | }
283 | }
284 |
285 | @Nested
286 | class DeleteAssetTransaction {
287 |
288 | @Test
289 | public void whenAssetDoesNotExist() {
290 | AssetTransfer contract = new AssetTransfer();
291 | Context ctx = mock(Context.class);
292 | ChaincodeStub stub = mock(ChaincodeStub.class);
293 | when(ctx.getStub()).thenReturn(stub);
294 | when(stub.getStringState("asset1")).thenReturn("");
295 |
296 | Throwable thrown = catchThrowable(() -> {
297 | contract.DeleteAsset(ctx, "asset1");
298 | });
299 |
300 | assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
301 | .hasMessage("Asset asset1 does not exist");
302 | assertThat(((ChaincodeException) thrown).getPayload()).isEqualTo("ASSET_NOT_FOUND".getBytes());
303 | }
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/contracts/asset-transfer-ts/dist/assetTransfer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6 | return c > 3 && r && Object.defineProperty(target, key, r), r;
7 | };
8 | var __metadata = (this && this.__metadata) || function (k, v) {
9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10 | };
11 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13 | return new (P || (P = Promise))(function (resolve, reject) {
14 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17 | step((generator = generator.apply(thisArg, _arguments || [])).next());
18 | });
19 | };
20 | Object.defineProperty(exports, "__esModule", { value: true });
21 | exports.AssetTransferContract = void 0;
22 | const fabric_contract_api_1 = require("fabric-contract-api");
23 | let AssetTransferContract = class AssetTransferContract extends fabric_contract_api_1.Contract {
24 | InitLedger(ctx) {
25 | return __awaiter(this, void 0, void 0, function* () {
26 | const assets = [
27 | {
28 | id: 'asset1',
29 | color: 'blue',
30 | size: 5,
31 | owner: 'Tomoko',
32 | appraisedValue: 300,
33 | },
34 | {
35 | id: 'asset2',
36 | color: 'red',
37 | size: 5,
38 | owner: 'Brad',
39 | appraisedValue: 400,
40 | },
41 | {
42 | id: 'asset3',
43 | color: 'green',
44 | size: 10,
45 | owner: 'Jin Soo',
46 | appraisedValue: 500,
47 | },
48 | {
49 | id: 'asset4',
50 | color: 'yellow',
51 | size: 10,
52 | owner: 'Max',
53 | appraisedValue: 600,
54 | },
55 | {
56 | id: 'asset5',
57 | color: 'black',
58 | size: 15,
59 | owner: 'Adriana',
60 | appraisedValue: 700,
61 | },
62 | {
63 | id: 'asset6',
64 | color: 'white',
65 | size: 15,
66 | owner: 'Michel',
67 | appraisedValue: 800,
68 | },
69 | ];
70 | for (const asset of assets) {
71 | asset.docType = 'asset';
72 | yield ctx.stub.putState(asset.id, Buffer.from(JSON.stringify(asset)));
73 | console.info(`Asset ${asset.id} initialized`);
74 | }
75 | });
76 | }
77 | CreateAsset(ctx, id, color, size, owner, appraisedValue) {
78 | return __awaiter(this, void 0, void 0, function* () {
79 | const asset = {
80 | id: id,
81 | color: color,
82 | size: size,
83 | owner: owner,
84 | appraisedValue: appraisedValue,
85 | };
86 | yield ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
87 | });
88 | }
89 | ReadAsset(ctx, id) {
90 | return __awaiter(this, void 0, void 0, function* () {
91 | const assetJSON = yield ctx.stub.getState(id);
92 | if (!assetJSON || assetJSON.length === 0) {
93 | throw new Error(`The asset ${id} does not exist`);
94 | }
95 | return assetJSON.toString();
96 | });
97 | }
98 | UpdateAsset(ctx, id, color, size, owner, appraisedValue) {
99 | return __awaiter(this, void 0, void 0, function* () {
100 | const exists = yield this.AssetExists(ctx, id);
101 | if (!exists) {
102 | throw new Error(`The asset ${id} does not exist`);
103 | }
104 | const updatedAsset = {
105 | id: id,
106 | color: color,
107 | size: size,
108 | owner: owner,
109 | appraisedValue: appraisedValue,
110 | };
111 | return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset)));
112 | });
113 | }
114 | DeleteAsset(ctx, id) {
115 | return __awaiter(this, void 0, void 0, function* () {
116 | const exists = yield this.AssetExists(ctx, id);
117 | if (!exists) {
118 | throw new Error(`The asset ${id} does not exist`);
119 | }
120 | return ctx.stub.deleteState(id);
121 | });
122 | }
123 | AssetExists(ctx, id) {
124 | return __awaiter(this, void 0, void 0, function* () {
125 | const assetJSON = yield ctx.stub.getState(id);
126 | return assetJSON && assetJSON.length > 0;
127 | });
128 | }
129 | TransferAsset(ctx, id, newowner) {
130 | return __awaiter(this, void 0, void 0, function* () {
131 | const assetString = yield this.ReadAsset(ctx, id);
132 | const asset = JSON.parse(assetString);
133 | asset.owner = newowner;
134 | yield ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
135 | });
136 | }
137 | GetAllAssets(ctx) {
138 | return __awaiter(this, void 0, void 0, function* () {
139 | const allResults = [];
140 | const iterator = yield ctx.stub.getStateByRange('', '');
141 | let result = yield iterator.next();
142 | while (!result.done) {
143 | const strValue = Buffer.from(result.value.value.toString()).toString('utf8');
144 | let record;
145 | try {
146 | record = JSON.parse(strValue);
147 | }
148 | catch (err) {
149 | console.log(err);
150 | record = strValue;
151 | }
152 | allResults.push({ Key: result.value.key, Record: record });
153 | result = yield iterator.next();
154 | }
155 | return JSON.stringify(allResults);
156 | });
157 | }
158 | };
159 | __decorate([
160 | fabric_contract_api_1.Transaction(),
161 | __metadata("design:type", Function),
162 | __metadata("design:paramtypes", [fabric_contract_api_1.Context]),
163 | __metadata("design:returntype", Promise)
164 | ], AssetTransferContract.prototype, "InitLedger", null);
165 | __decorate([
166 | fabric_contract_api_1.Transaction(),
167 | __metadata("design:type", Function),
168 | __metadata("design:paramtypes", [fabric_contract_api_1.Context, String, String, Number, String, Number]),
169 | __metadata("design:returntype", Promise)
170 | ], AssetTransferContract.prototype, "CreateAsset", null);
171 | __decorate([
172 | fabric_contract_api_1.Transaction(false),
173 | __metadata("design:type", Function),
174 | __metadata("design:paramtypes", [fabric_contract_api_1.Context, String]),
175 | __metadata("design:returntype", Promise)
176 | ], AssetTransferContract.prototype, "ReadAsset", null);
177 | __decorate([
178 | fabric_contract_api_1.Transaction(),
179 | __metadata("design:type", Function),
180 | __metadata("design:paramtypes", [fabric_contract_api_1.Context, String, String, Number, String, Number]),
181 | __metadata("design:returntype", Promise)
182 | ], AssetTransferContract.prototype, "UpdateAsset", null);
183 | __decorate([
184 | fabric_contract_api_1.Transaction(),
185 | __metadata("design:type", Function),
186 | __metadata("design:paramtypes", [fabric_contract_api_1.Context, String]),
187 | __metadata("design:returntype", Promise)
188 | ], AssetTransferContract.prototype, "DeleteAsset", null);
189 | __decorate([
190 | fabric_contract_api_1.Transaction(false),
191 | fabric_contract_api_1.Returns('boolean'),
192 | __metadata("design:type", Function),
193 | __metadata("design:paramtypes", [fabric_contract_api_1.Context, String]),
194 | __metadata("design:returntype", Promise)
195 | ], AssetTransferContract.prototype, "AssetExists", null);
196 | __decorate([
197 | fabric_contract_api_1.Transaction(),
198 | __metadata("design:type", Function),
199 | __metadata("design:paramtypes", [fabric_contract_api_1.Context, String, String]),
200 | __metadata("design:returntype", Promise)
201 | ], AssetTransferContract.prototype, "TransferAsset", null);
202 | __decorate([
203 | fabric_contract_api_1.Transaction(false),
204 | fabric_contract_api_1.Returns('string'),
205 | __metadata("design:type", Function),
206 | __metadata("design:paramtypes", [fabric_contract_api_1.Context]),
207 | __metadata("design:returntype", Promise)
208 | ], AssetTransferContract.prototype, "GetAllAssets", null);
209 | AssetTransferContract = __decorate([
210 | fabric_contract_api_1.Info({ title: 'AssetTransfer', description: 'Smart contract for trading assets' })
211 | ], AssetTransferContract);
212 | exports.AssetTransferContract = AssetTransferContract;
213 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXRUcmFuc2Zlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9hc3NldFRyYW5zZmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUlBLDZEQUFvRjtBQUlwRixJQUFhLHFCQUFxQixHQUFsQyxNQUFhLHFCQUFzQixTQUFRLDhCQUFRO0lBRWxDLFVBQVUsQ0FBQyxHQUFZOztZQUNoQyxNQUFNLE1BQU0sR0FBWTtnQkFDcEI7b0JBQ0ksRUFBRSxFQUFFLFFBQVE7b0JBQ1osS0FBSyxFQUFFLE1BQU07b0JBQ2IsSUFBSSxFQUFFLENBQUM7b0JBQ1AsS0FBSyxFQUFFLFFBQVE7b0JBQ2YsY0FBYyxFQUFFLEdBQUc7aUJBQ3RCO2dCQUNEO29CQUNJLEVBQUUsRUFBRSxRQUFRO29CQUNaLEtBQUssRUFBRSxLQUFLO29CQUNaLElBQUksRUFBRSxDQUFDO29CQUNQLEtBQUssRUFBRSxNQUFNO29CQUNiLGNBQWMsRUFBRSxHQUFHO2lCQUN0QjtnQkFDRDtvQkFDSSxFQUFFLEVBQUUsUUFBUTtvQkFDWixLQUFLLEVBQUUsT0FBTztvQkFDZCxJQUFJLEVBQUUsRUFBRTtvQkFDUixLQUFLLEVBQUUsU0FBUztvQkFDaEIsY0FBYyxFQUFFLEdBQUc7aUJBQ3RCO2dCQUNEO29CQUNJLEVBQUUsRUFBRSxRQUFRO29CQUNaLEtBQUssRUFBRSxRQUFRO29CQUNmLElBQUksRUFBRSxFQUFFO29CQUNSLEtBQUssRUFBRSxLQUFLO29CQUNaLGNBQWMsRUFBRSxHQUFHO2lCQUN0QjtnQkFDRDtvQkFDSSxFQUFFLEVBQUUsUUFBUTtvQkFDWixLQUFLLEVBQUUsT0FBTztvQkFDZCxJQUFJLEVBQUUsRUFBRTtvQkFDUixLQUFLLEVBQUUsU0FBUztvQkFDaEIsY0FBYyxFQUFFLEdBQUc7aUJBQ3RCO2dCQUNEO29CQUNJLEVBQUUsRUFBRSxRQUFRO29CQUNaLEtBQUssRUFBRSxPQUFPO29CQUNkLElBQUksRUFBRSxFQUFFO29CQUNSLEtBQUssRUFBRSxRQUFRO29CQUNmLGNBQWMsRUFBRSxHQUFHO2lCQUN0QjthQUNKLENBQUM7WUFFRixLQUFLLE1BQU0sS0FBSyxJQUFJLE1BQU0sRUFBRTtnQkFDeEIsS0FBSyxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7Z0JBQ3hCLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN0RSxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsS0FBSyxDQUFDLEVBQUUsY0FBYyxDQUFDLENBQUM7YUFDakQ7UUFDTCxDQUFDO0tBQUE7SUFJWSxXQUFXLENBQ3BCLEdBQVksRUFDWixFQUFVLEVBQ1YsS0FBYSxFQUNiLElBQVksRUFDWixLQUFhLEVBQ2IsY0FBc0I7O1lBRXRCLE1BQU0sS0FBSyxHQUFHO2dCQUNWLEVBQUUsRUFBRSxFQUFFO2dCQUNOLEtBQUssRUFBRSxLQUFLO2dCQUNaLElBQUksRUFBRSxJQUFJO2dCQUNWLEtBQUssRUFBRSxLQUFLO2dCQUNaLGNBQWMsRUFBRSxjQUFjO2FBQ2pDLENBQUM7WUFDRixNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3BFLENBQUM7S0FBQTtJQUlZLFNBQVMsQ0FBQyxHQUFZLEVBQUUsRUFBVTs7WUFDM0MsTUFBTSxTQUFTLEdBQUcsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUM5QyxJQUFJLENBQUMsU0FBUyxJQUFJLFNBQVMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO2dCQUN0QyxNQUFNLElBQUksS0FBSyxDQUFDLGFBQWEsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO2FBQ3JEO1lBQ0QsT0FBTyxTQUFTLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDaEMsQ0FBQztLQUFBO0lBSVksV0FBVyxDQUNwQixHQUFZLEVBQ1osRUFBVSxFQUNWLEtBQWEsRUFDYixJQUFZLEVBQ1osS0FBYSxFQUNiLGNBQXNCOztZQUV0QixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLElBQUksQ0FBQyxNQUFNLEVBQUU7Z0JBQ1QsTUFBTSxJQUFJLEtBQUssQ0FBQyxhQUFhLEVBQUUsaUJBQWlCLENBQUMsQ0FBQzthQUNyRDtZQUdELE1BQU0sWUFBWSxHQUFHO2dCQUNqQixFQUFFLEVBQUUsRUFBRTtnQkFDTixLQUFLLEVBQUUsS0FBSztnQkFDWixJQUFJLEVBQUUsSUFBSTtnQkFDVixLQUFLLEVBQUUsS0FBSztnQkFDWixjQUFjLEVBQUUsY0FBYzthQUNqQyxDQUFDO1lBQ0YsT0FBTyxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM1RSxDQUFDO0tBQUE7SUFJWSxXQUFXLENBQUMsR0FBWSxFQUFFLEVBQVU7O1lBQzdDLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDL0MsSUFBSSxDQUFDLE1BQU0sRUFBRTtnQkFDVCxNQUFNLElBQUksS0FBSyxDQUFDLGFBQWEsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO2FBQ3JEO1lBQ0QsT0FBTyxHQUFHLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNwQyxDQUFDO0tBQUE7SUFLWSxXQUFXLENBQUMsR0FBWSxFQUFFLEVBQVU7O1lBQzdDLE1BQU0sU0FBUyxHQUFHLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDOUMsT0FBTyxTQUFTLElBQUksU0FBUyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFDN0MsQ0FBQztLQUFBO0lBSVksYUFBYSxDQUFDLEdBQVksRUFBRSxFQUFVLEVBQUUsUUFBZ0I7O1lBQ2pFLE1BQU0sV0FBVyxHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN0QyxLQUFLLENBQUMsS0FBSyxHQUFHLFFBQVEsQ0FBQztZQUN2QixNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3BFLENBQUM7S0FBQTtJQUtZLFlBQVksQ0FBQyxHQUFZOztZQUNsQyxNQUFNLFVBQVUsR0FBRyxFQUFFLENBQUM7WUFFdEIsTUFBTSxRQUFRLEdBQUcsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDeEQsSUFBSSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDbkMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUU7Z0JBQ2pCLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQzdFLElBQUksTUFBTSxDQUFDO2dCQUNYLElBQUk7b0JBQ0EsTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUM7aUJBQ2pDO2dCQUFDLE9BQU8sR0FBRyxFQUFFO29CQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7b0JBQ2pCLE1BQU0sR0FBRyxRQUFRLENBQUM7aUJBQ3JCO2dCQUNELFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzNELE1BQU0sR0FBRyxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQzthQUNsQztZQUNELE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN0QyxDQUFDO0tBQUE7Q0FDSixDQUFBO0FBOUpHO0lBREMsaUNBQVcsRUFBRTs7cUNBQ2UsNkJBQU87O3VEQW1EbkM7QUFJRDtJQURDLGlDQUFXLEVBQUU7O3FDQUVMLDZCQUFPOzt3REFlZjtBQUlEO0lBREMsaUNBQVcsQ0FBQyxLQUFLLENBQUM7O3FDQUNTLDZCQUFPOztzREFNbEM7QUFJRDtJQURDLGlDQUFXLEVBQUU7O3FDQUVMLDZCQUFPOzt3REFxQmY7QUFJRDtJQURDLGlDQUFXLEVBQUU7O3FDQUNnQiw2QkFBTzs7d0RBTXBDO0FBS0Q7SUFGQyxpQ0FBVyxDQUFDLEtBQUssQ0FBQztJQUNsQiw2QkFBTyxDQUFDLFNBQVMsQ0FBQzs7cUNBQ1csNkJBQU87O3dEQUdwQztBQUlEO0lBREMsaUNBQVcsRUFBRTs7cUNBQ2tCLDZCQUFPOzswREFLdEM7QUFLRDtJQUZDLGlDQUFXLENBQUMsS0FBSyxDQUFDO0lBQ2xCLDZCQUFPLENBQUMsUUFBUSxDQUFDOztxQ0FDYSw2QkFBTzs7eURBa0JyQztBQS9KUSxxQkFBcUI7SUFEakMsMEJBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUUsV0FBVyxFQUFFLG1DQUFtQyxFQUFFLENBQUM7R0FDdEUscUJBQXFCLENBZ0tqQztBQWhLWSxzREFBcUIiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcbiAqL1xuXG5pbXBvcnQgeyBDb250ZXh0LCBDb250cmFjdCwgSW5mbywgUmV0dXJucywgVHJhbnNhY3Rpb24gfSBmcm9tICdmYWJyaWMtY29udHJhY3QtYXBpJztcbmltcG9ydCB7IEFzc2V0IH0gZnJvbSAnLi9hc3NldCc7XG5cbkBJbmZvKHsgdGl0bGU6ICdBc3NldFRyYW5zZmVyJywgZGVzY3JpcHRpb246ICdTbWFydCBjb250cmFjdCBmb3IgdHJhZGluZyBhc3NldHMnIH0pXG5leHBvcnQgY2xhc3MgQXNzZXRUcmFuc2ZlckNvbnRyYWN0IGV4dGVuZHMgQ29udHJhY3Qge1xuICAgIEBUcmFuc2FjdGlvbigpXG4gICAgcHVibGljIGFzeW5jIEluaXRMZWRnZXIoY3R4OiBDb250ZXh0KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IGFzc2V0czogQXNzZXRbXSA9IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBpZDogJ2Fzc2V0MScsXG4gICAgICAgICAgICAgICAgY29sb3I6ICdibHVlJyxcbiAgICAgICAgICAgICAgICBzaXplOiA1LFxuICAgICAgICAgICAgICAgIG93bmVyOiAnVG9tb2tvJyxcbiAgICAgICAgICAgICAgICBhcHByYWlzZWRWYWx1ZTogMzAwLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBpZDogJ2Fzc2V0MicsXG4gICAgICAgICAgICAgICAgY29sb3I6ICdyZWQnLFxuICAgICAgICAgICAgICAgIHNpemU6IDUsXG4gICAgICAgICAgICAgICAgb3duZXI6ICdCcmFkJyxcbiAgICAgICAgICAgICAgICBhcHByYWlzZWRWYWx1ZTogNDAwLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBpZDogJ2Fzc2V0MycsXG4gICAgICAgICAgICAgICAgY29sb3I6ICdncmVlbicsXG4gICAgICAgICAgICAgICAgc2l6ZTogMTAsXG4gICAgICAgICAgICAgICAgb3duZXI6ICdKaW4gU29vJyxcbiAgICAgICAgICAgICAgICBhcHByYWlzZWRWYWx1ZTogNTAwLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBpZDogJ2Fzc2V0NCcsXG4gICAgICAgICAgICAgICAgY29sb3I6ICd5ZWxsb3cnLFxuICAgICAgICAgICAgICAgIHNpemU6IDEwLFxuICAgICAgICAgICAgICAgIG93bmVyOiAnTWF4JyxcbiAgICAgICAgICAgICAgICBhcHByYWlzZWRWYWx1ZTogNjAwLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBpZDogJ2Fzc2V0NScsXG4gICAgICAgICAgICAgICAgY29sb3I6ICdibGFjaycsXG4gICAgICAgICAgICAgICAgc2l6ZTogMTUsXG4gICAgICAgICAgICAgICAgb3duZXI6ICdBZHJpYW5hJyxcbiAgICAgICAgICAgICAgICBhcHByYWlzZWRWYWx1ZTogNzAwLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBpZDogJ2Fzc2V0NicsXG4gICAgICAgICAgICAgICAgY29sb3I6ICd3aGl0ZScsXG4gICAgICAgICAgICAgICAgc2l6ZTogMTUsXG4gICAgICAgICAgICAgICAgb3duZXI6ICdNaWNoZWwnLFxuICAgICAgICAgICAgICAgIGFwcHJhaXNlZFZhbHVlOiA4MDAsXG4gICAgICAgICAgICB9LFxuICAgICAgICBdO1xuXG4gICAgICAgIGZvciAoY29uc3QgYXNzZXQgb2YgYXNzZXRzKSB7XG4gICAgICAgICAgICBhc3NldC5kb2NUeXBlID0gJ2Fzc2V0JztcbiAgICAgICAgICAgIGF3YWl0IGN0eC5zdHViLnB1dFN0YXRlKGFzc2V0LmlkLCBCdWZmZXIuZnJvbShKU09OLnN0cmluZ2lmeShhc3NldCkpKTtcbiAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhgQXNzZXQgJHthc3NldC5pZH0gaW5pdGlhbGl6ZWRgKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIENyZWF0ZUFzc2V0IGlzc3VlcyBhIG5ldyBhc3NldCB0byB0aGUgd29ybGQgc3RhdGUgd2l0aCBnaXZlbiBkZXRhaWxzLlxuICAgIEBUcmFuc2FjdGlvbigpXG4gICAgcHVibGljIGFzeW5jIENyZWF0ZUFzc2V0KFxuICAgICAgICBjdHg6IENvbnRleHQsXG4gICAgICAgIGlkOiBzdHJpbmcsXG4gICAgICAgIGNvbG9yOiBzdHJpbmcsXG4gICAgICAgIHNpemU6IG51bWJlcixcbiAgICAgICAgb3duZXI6IHN0cmluZyxcbiAgICAgICAgYXBwcmFpc2VkVmFsdWU6IG51bWJlcixcbiAgICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgYXNzZXQgPSB7XG4gICAgICAgICAgICBpZDogaWQsXG4gICAgICAgICAgICBjb2xvcjogY29sb3IsXG4gICAgICAgICAgICBzaXplOiBzaXplLFxuICAgICAgICAgICAgb3duZXI6IG93bmVyLFxuICAgICAgICAgICAgYXBwcmFpc2VkVmFsdWU6IGFwcHJhaXNlZFZhbHVlLFxuICAgICAgICB9O1xuICAgICAgICBhd2FpdCBjdHguc3R1Yi5wdXRTdGF0ZShpZCwgQnVmZmVyLmZyb20oSlNPTi5zdHJpbmdpZnkoYXNzZXQpKSk7XG4gICAgfVxuXG4gICAgLy8gUmVhZEFzc2V0IHJldHVybnMgdGhlIGFzc2V0IHN0b3JlZCBpbiB0aGUgd29ybGQgc3RhdGUgd2l0aCBnaXZlbiBpZC5cbiAgICBAVHJhbnNhY3Rpb24oZmFsc2UpXG4gICAgcHVibGljIGFzeW5jIFJlYWRBc3NldChjdHg6IENvbnRleHQsIGlkOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgICAgICBjb25zdCBhc3NldEpTT04gPSBhd2FpdCBjdHguc3R1Yi5nZXRTdGF0ZShpZCk7IC8vIGdldCB0aGUgYXNzZXQgZnJvbSBjaGFpbmNvZGUgc3RhdGVcbiAgICAgICAgaWYgKCFhc3NldEpTT04gfHwgYXNzZXRKU09OLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBUaGUgYXNzZXQgJHtpZH0gZG9lcyBub3QgZXhpc3RgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXNzZXRKU09OLnRvU3RyaW5nKCk7XG4gICAgfVxuXG4gICAgLy8gVXBkYXRlQXNzZXQgdXBkYXRlcyBhbiBleGlzdGluZyBhc3NldCBpbiB0aGUgd29ybGQgc3RhdGUgd2l0aCBwcm92aWRlZCBwYXJhbWV0ZXJzLlxuICAgIEBUcmFuc2FjdGlvbigpXG4gICAgcHVibGljIGFzeW5jIFVwZGF0ZUFzc2V0KFxuICAgICAgICBjdHg6IENvbnRleHQsXG4gICAgICAgIGlkOiBzdHJpbmcsXG4gICAgICAgIGNvbG9yOiBzdHJpbmcsXG4gICAgICAgIHNpemU6IG51bWJlcixcbiAgICAgICAgb3duZXI6IHN0cmluZyxcbiAgICAgICAgYXBwcmFpc2VkVmFsdWU6IG51bWJlcixcbiAgICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgZXhpc3RzID0gYXdhaXQgdGhpcy5Bc3NldEV4aXN0cyhjdHgsIGlkKTtcbiAgICAgICAgaWYgKCFleGlzdHMpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVGhlIGFzc2V0ICR7aWR9IGRvZXMgbm90IGV4aXN0YCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBvdmVyd3JpdGluZyBvcmlnaW5hbCBhc3NldCB3aXRoIG5ldyBhc3NldFxuICAgICAgICBjb25zdCB1cGRhdGVkQXNzZXQgPSB7XG4gICAgICAgICAgICBpZDogaWQsXG4gICAgICAgICAgICBjb2xvcjogY29sb3IsXG4gICAgICAgICAgICBzaXplOiBzaXplLFxuICAgICAgICAgICAgb3duZXI6IG93bmVyLFxuICAgICAgICAgICAgYXBwcmFpc2VkVmFsdWU6IGFwcHJhaXNlZFZhbHVlLFxuICAgICAgICB9O1xuICAgICAgICByZXR1cm4gY3R4LnN0dWIucHV0U3RhdGUoaWQsIEJ1ZmZlci5mcm9tKEpTT04uc3RyaW5naWZ5KHVwZGF0ZWRBc3NldCkpKTtcbiAgICB9XG5cbiAgICAvLyBEZWxldGVBc3NldCBkZWxldGVzIGFuIGdpdmVuIGFzc2V0IGZyb20gdGhlIHdvcmxkIHN0YXRlLlxuICAgIEBUcmFuc2FjdGlvbigpXG4gICAgcHVibGljIGFzeW5jIERlbGV0ZUFzc2V0KGN0eDogQ29udGV4dCwgaWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBleGlzdHMgPSBhd2FpdCB0aGlzLkFzc2V0RXhpc3RzKGN0eCwgaWQpO1xuICAgICAgICBpZiAoIWV4aXN0cykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBUaGUgYXNzZXQgJHtpZH0gZG9lcyBub3QgZXhpc3RgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY3R4LnN0dWIuZGVsZXRlU3RhdGUoaWQpO1xuICAgIH1cblxuICAgIC8vIEFzc2V0RXhpc3RzIHJldHVybnMgdHJ1ZSB3aGVuIGFzc2V0IHdpdGggZ2l2ZW4gaWQgZXhpc3RzIGluIHdvcmxkIHN0YXRlLlxuICAgIEBUcmFuc2FjdGlvbihmYWxzZSlcbiAgICBAUmV0dXJucygnYm9vbGVhbicpXG4gICAgcHVibGljIGFzeW5jIEFzc2V0RXhpc3RzKGN0eDogQ29udGV4dCwgaWQ6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgICAgICBjb25zdCBhc3NldEpTT04gPSBhd2FpdCBjdHguc3R1Yi5nZXRTdGF0ZShpZCk7XG4gICAgICAgIHJldHVybiBhc3NldEpTT04gJiYgYXNzZXRKU09OLmxlbmd0aCA+IDA7XG4gICAgfVxuXG4gICAgLy8gVHJhbnNmZXJBc3NldCB1cGRhdGVzIHRoZSBvd25lciBmaWVsZCBvZiBhc3NldCB3aXRoIGdpdmVuIGlkIGluIHRoZSB3b3JsZCBzdGF0ZS5cbiAgICBAVHJhbnNhY3Rpb24oKVxuICAgIHB1YmxpYyBhc3luYyBUcmFuc2ZlckFzc2V0KGN0eDogQ29udGV4dCwgaWQ6IHN0cmluZywgbmV3b3duZXI6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBhc3NldFN0cmluZyA9IGF3YWl0IHRoaXMuUmVhZEFzc2V0KGN0eCwgaWQpO1xuICAgICAgICBjb25zdCBhc3NldCA9IEpTT04ucGFyc2UoYXNzZXRTdHJpbmcpO1xuICAgICAgICBhc3NldC5vd25lciA9IG5ld293bmVyO1xuICAgICAgICBhd2FpdCBjdHguc3R1Yi5wdXRTdGF0ZShpZCwgQnVmZmVyLmZyb20oSlNPTi5zdHJpbmdpZnkoYXNzZXQpKSk7XG4gICAgfVxuXG4gICAgLy8gR2V0QWxsQXNzZXRzIHJldHVybnMgYWxsIGFzc2V0cyBmb3VuZCBpbiB0aGUgd29ybGQgc3RhdGUuXG4gICAgQFRyYW5zYWN0aW9uKGZhbHNlKVxuICAgIEBSZXR1cm5zKCdzdHJpbmcnKVxuICAgIHB1YmxpYyBhc3luYyBHZXRBbGxBc3NldHMoY3R4OiBDb250ZXh0KTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgY29uc3QgYWxsUmVzdWx0cyA9IFtdO1xuICAgICAgICAvLyByYW5nZSBxdWVyeSB3aXRoIGVtcHR5IHN0cmluZyBmb3Igc3RhcnRLZXkgYW5kIGVuZEtleSBkb2VzIGFuIG9wZW4tZW5kZWQgcXVlcnkgb2YgYWxsIGFzc2V0cyBpbiB0aGUgY2hhaW5jb2RlIG5hbWVzcGFjZS5cbiAgICAgICAgY29uc3QgaXRlcmF0b3IgPSBhd2FpdCBjdHguc3R1Yi5nZXRTdGF0ZUJ5UmFuZ2UoJycsICcnKTtcbiAgICAgICAgbGV0IHJlc3VsdCA9IGF3YWl0IGl0ZXJhdG9yLm5leHQoKTtcbiAgICAgICAgd2hpbGUgKCFyZXN1bHQuZG9uZSkge1xuICAgICAgICAgICAgY29uc3Qgc3RyVmFsdWUgPSBCdWZmZXIuZnJvbShyZXN1bHQudmFsdWUudmFsdWUudG9TdHJpbmcoKSkudG9TdHJpbmcoJ3V0ZjgnKTtcbiAgICAgICAgICAgIGxldCByZWNvcmQ7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHJlY29yZCA9IEpTT04ucGFyc2Uoc3RyVmFsdWUpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coZXJyKTtcbiAgICAgICAgICAgICAgICByZWNvcmQgPSBzdHJWYWx1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGFsbFJlc3VsdHMucHVzaCh7IEtleTogcmVzdWx0LnZhbHVlLmtleSwgUmVjb3JkOiByZWNvcmQgfSk7XG4gICAgICAgICAgICByZXN1bHQgPSBhd2FpdCBpdGVyYXRvci5uZXh0KCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGFsbFJlc3VsdHMpO1xuICAgIH1cbn1cbiJdfQ==
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Develop, Debug and Deploy Smart Contracts - DRAFT
2 |
3 | **Aim:** Stand up a Hyperledger Fabric Smart Contract so it can easily debugged
4 | **Objectives:**
5 |
6 | - Introduce what Chaincode-as-a-Service is, and how it helps
7 | - Show how to build & configure a Chaincode to run like this
8 | - How to deploy these a running Hyperledger Fabric network
9 | - How then to debug this running Chaincode.
10 |
11 | ## Overview
12 |
13 | It helps to think of three 'parts'
14 |
15 | - The Fabric network, consisting of the peers, orderers, certificate authorities etc. Along with configured channels and identities. For our purposes here, this can be considered as a 'black box'. The 'black box' can be configured a number of different ways, but typically will be one or docker containers.
16 | - The Chaincode - this will be running in it's own docker container.
17 | - The editor - VSCode is covered here, but the approach should hold with other debuggers and editors.
18 |
19 | 
20 |
21 | The _high level process_ is
22 |
23 | 0. Stand up Fabric
24 | 1. Develop the Smart Contract
25 | 3. Create a chaincode package but using the chaincode-as-a-service approach
26 | Install/Approve/Commit this package
27 | 4. Stand up the Chaincode using the chaincode-as-a-service approach
28 | Attach your debugger to the running chaincode
29 | 5. Invoke a transaction, this will then halt in the debugger to let you step over the code
30 | 5. Find the bugs and repeat **from step 4** - note step 4... you don't need to go back to a new package or approval cycle.
31 |
32 | ### What do you need?
33 |
34 | You'll need to have docker available to you, along with VSCode and the IBM Blockchain Platofrm Extension installed. Also, install the VSCode extensions you prefer for debugging your preferred language.
35 |
36 | - For TypeScript and JavaScript VSCode has built-in support.
37 | - For Java the [JavaExtension pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) is suggested
38 | - For go [_to be added_]
39 |
40 | Finally, clone this repo to a directory of your own choice if you want to use the example contract code here.
41 | ### What is Chaincode as Service?
42 |
43 | The chaincode-as-a-service feature is a very useful and practical way to run 'Smart Contracts'. Traditionally the Fabric Peer has taken on the role of orchestrating the complete lifecycle of the chaincode. It required access to the Docker Daemon to create images, and start containers. Java, Node.js and Go chaincode frameworks were explicitly known to the peer including how they should be built and started.
44 |
45 | As a result this makes it very hard to deploy into Kubernetes (K8S) style environments, or to run in any form of debug mode. Additionally, the code is being rebuilt by the peer therefore there is some degree of uncertainty about what dependencies have been pulled in.
46 |
47 | Chaincode-as-service requires you to orchestrate the build and deployment phase yourself. Whilst this is an additional step, it gives control back. The Peer still requires a 'chaincode package' to be installed. In this case this doesn't contain code, but the information about where the chaincode is hosted. (Hostname,Port,TLS config etc)
48 |
49 | #### Fabric v2.4.1 Improvements
50 |
51 | We need to use the latest 2.4.1 release as this contains some improvements to make this process easier. The core functionality is available in earlier releases but requires more configuration.
52 |
53 | - The docker image for the peer contains a builder for chaincode-as-a-service preconfigured. This is named 'ccaasbuilder'. This removes the need to build your own external builder and repackage and configure the peer
54 | - The `ccaasbuilder` applications are included in the binary tgz archive download for use in other circumstances. The `sampleconfig/core.yaml` is updated as well to refer to 'ccaasbuilder'
55 | - The 2.4.1 Java Chaincode release has been updated to remove the need to write a custom bootstrap main class, similar to the Node.js Chaincode. It is intended that this will be added to the go chaincode as well.
56 |
57 | ## Which Fabric deployment to use?
58 |
59 | The core Peer, Orderer and Certificate Authority binaries can be deployed in many different configurations, for example
60 |
61 | - VSCode Extension, using it's built in templates for Microfab. All the Fabric binaries within 1 docker container
62 | - Running Microfab standalone from the extensions
63 | - Fabric's test network found within the fabric-samples repo
64 | - [_test-network-k8s?_]
65 | - [_ibp?_]
66 | - [_minifab?_]
67 |
68 | ## Creating the Smart Contracts
69 |
70 | An important point is that the code written for the Smart Contract is exactly the same, whether it's managed by the peer or Chaincode-as-a-Service. What is different is how that is started and packaged. This repo contains a chaincode for each language, these are copied from the Fabric-Samples repo. Note that in all cases, the Java/Typescript/Go code is the same, the difference is in the packaging.
71 |
72 | ### TypeScript/JavaScript
73 |
74 | Using the Typescript contract as an example, the difference is easier to see. The package.json contains 4 'start' commands
75 |
76 | ```
77 | "start": "fabric-chaincode-node start",
78 | "start:server-nontls": "fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID",
79 | "start:server": "fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID --chaincode-tls-key-file=/hyperledger/privatekey.pem --chaincode-tls-client-cacert-file=/hyperledger/rootcert.pem --chaincode-tls-cert-file=/hyperledger/cert.pem",
80 | "start:server-debug": "set -x && NODE_OPTIONS='--inspect=0.0.0.0:9229' fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID"
81 | ```
82 |
83 | The first, is used when the peer is completely controlling the chaincode. The second `start:server-nontls` starts in the Chaincode-as-a-service mode (without using TLS). The command
84 | is very similar `fabric-chainmcode-node server` rather than `fabric-chaincode-node start`. Two options are provided here, these are the network address the chaincode will listen on and it's id. (aside when the Peer runs the chaincode, it does pass extra options, but they aren't seen in the package.json)
85 |
86 | The third `start:server` adds the required TLS configuration, but is otherwise the same.
87 | The forth `start:server-debug` is the same as the non-TLS case, but includes the environment variable required to get Node.js to open a port to allow a debugger to connect remotly.
88 |
89 | ### Java
90 |
91 | The changes for the Java chaincode are logically the same. The build.gradle (or use Maven if you wish) is exactly the same (like there were no changes to the TypeScript compilation). With the v2.4.1 Java Chaincode libraries, there are no code changes to make or build changes. The '-as-a-service' mode will be used if the environment variable `CHAINCODE_SERVER_ADDRESS` is set.
92 |
93 | For the non-TLS case the Java chaincode is started with `java -jar /chaincode.jar` - and will use the Chaincode-as-a-service mode _if_ the environment variable `CHAINCODE_SERVER_ADDRESS` is set.
94 |
95 | For the TLS case .[_to be added_]
96 |
97 | For debug, the JVM needs to put into debug mode `java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8000 -jar /chaincode.jar`
98 |
99 | ### go
100 |
101 | [_go to follow_]
102 |
103 |
104 | ## How is the chaincode package different?
105 |
106 | A key difference is that the chaincode package, does not contain code. It is used as a holder of data that indicates to the peer where teh chaincode is. What host/port and what TLS configuration is needed. Chaincode packages already can hold data about the couchdn indicies to use or the private data collections.
107 |
108 | Within the package the `connection.json` is a key file. At it's simplest it would be
109 |
110 | ```json
111 | {
112 | "address": "assettransfer_ccaas:9999",
113 | "dial_timeout": "10s",
114 | "tls_required": false
115 | }
116 | ```
117 |
118 | This is telling the peer the chaincode is on host `assettransfer_ccaas` port 9999. 10s timeout on connecting, and tls is not needed.
119 |
120 | The packager can be constructed by hand, it's a set of json files, collected together with `tgz`. To help there is [bash script](./contracts/ccaas_pkg.sh) to create the package in this repo.
121 |
122 | ### Important networking warning
123 |
124 | The chaincode package that is installed critically contains the hostname and port that the peer is expecting the chaincode to listening on. If nothing answers the peer, it obviously will fail the transaction.
125 |
126 | Note that it is ok not to have the chaincode running at all times, the peer won't complain until it is asked to actually connect to the chaincode. This is an important ability as it let's debug, and restart the container.
127 |
128 | The hostname that is supplied must be something that the peer, from it's perspective can resolve. Typically the peer will be inside a docker container, therefore suppling `localhost` or `127.0.0.1` will resolve to the same container the the peer is running in.
129 |
130 | Assuming that the peer is running in a docker container, the chaincode could either be run in it's down docker container, on the same docker network as the peers container, or it could be run directly on the host system.
131 |
132 | Depending your host OS, the 'specialhostname' that is used from within the docker container to access the host varies. For example see this [stackoverflow post](https://stackoverflow.com/questions/24319662/from-inside-of-a-docker-container-how-do-i-connect-to-the-localhost-of-the-mach#:~:text=To%20access%20host%20machine%20from,using%20it%20to%20anything%20else.&text=Then%20make%20sure%20that%20you,0.0%20.)
133 |
134 | The advantage of this is the chaincode can run locally on your hostmachine and is simple to conenct to from a debugger.
135 |
136 | Alternatively, you can package the chaincode into it's own docker container, and run that. You can still debug into this, but need to ensure that the ports of the container are exposed correctly for your language runtime.
137 |
138 | The first practical will show the approach of using the 'specialhostname', the other examples show how to use a docker container. Neither Fabric, or the Chaincode or VSCode really care which approach is used, so long as the network connections can resolve themselves.
139 |
140 | ## The Practical - with VSCode's built-in Microfab
141 |
142 | Firstly using VSCode and MicroFab; the VSCode extension can create a simple Fabric network in Microfab for you.
143 |
144 | > Please do read this section as the later stages are very much the same for all the ways of running fabric.
145 |
146 | ### Use the latest Microfab version
147 |
148 | You need to use the latest microfab version; here I'm using a copy locally rebuilt and tagged with `ibmcom/ibp-microfab-rc`.
149 |
150 | In a new VSCode window, open the settings and find the Blockchain Configuration. Enter the name of the "Custom Local Environment Start Image", and select the Enable Custom Local Environment Start Image"
151 |
152 | 
153 |
154 | ### Running and deploying a contract
155 |
156 | Add the repo to the workspace, and in a terminal window (either inside or outside VSCode to your preference) change to contract directory, and compile the code.
157 |
158 | First we need to `npm install` and `npm build` the chaincode
159 |
160 | ```bash
161 | cd contracts/asset-transfer-ts
162 | npm install
163 | npm run build
164 | ```
165 |
166 | This has built the typescript contract. Debug settings have already been included in the typescript configuration. If you get issues with later debugging, check the typescript configuration in `tsconfig.json`
167 |
168 | ### Create the Chaincode Package
169 |
170 | In common with the traditional ways of deploying chaincodes (containing SmartContracts) we need to have a chaincode package. In this case however this package primarily contains the host:port of where the chaincode is running. Currently the peer command can't properly create these, so a batch file has been included that does it.
171 |
172 | To create a package
173 |
174 | ```bash
175 | npm run package:cc
176 |
177 | > asset-transfer-basic@1.0.0 package:cc
178 | > ../ccaas_pkg.sh
179 |
180 | -rw-r--r-- 1 matthew matthew 461 Jan 27 15:25 asset-transfer.tar.gz
181 | CHAINCODE_ID=asset-transfer_1:5206e43f17e0532a67480d7a384dd402da15f23a713e75db5b5ef898627315a9
182 | ```
183 |
184 | ### Start the local Fabric Network
185 |
186 | In the 'Fabric Environments' panel, select the `Add local or remote environment` and choose `Create new from template` and select the first opion - the 1 Org template. Enter a name of your choice, here `nx01`, and you must select the `v2_0` capability.
187 |
188 | ### Install the chaincode package
189 |
190 | In VSCode, click on the Blockchain Icon to get into the Blockchain View. From the Smart Contracts panel, import the `asset-transfer-tar.gz`. In this example this has already been imported
191 |
192 | 
193 |
194 | Then from the FabricEnvironment panel, click on the just created environment, and expand the 'channel, so see the `+ Deploy smart contract` option. Select the packaged smart contract, and accept all the default options.
195 |
196 | WHen this has deployed, you'll be able to connect to the Channel in the `Fabric Gateways` and expand the channel and see the contract. However if click on the contract, nothing will happen - and in fact in the log there will be an error. The chaincode isn't actually running at this point.
197 |
198 |
199 | ### Running the chaincode
200 |
201 | You can run the chaincode directly from a terminal, you'll need the `CHAINCODE_ID` from earlier.
202 |
203 | ```
204 | export CHAINCODE_SERVER_ADDRESS=0.0.0.0:9999
205 | export CHAINCODE_ID=assettransfer_1:b4626214ee5d9a1425f938938905cd91e5b37a6b835553630ace71f0397a1025
206 | npm run start:server-debug
207 | ```
208 |
209 | This will start in Chaincode-as-a-Service mode, with the node.js debug options. You can now go back to the `Fabric Gateways` view, and refresh. You'll be able to see all the transaction functions listed now. To get these the VSCode Extension sent a transaction to the chaincode, and it responded with information.
210 |
211 | ### Debugging the chaincode
212 |
213 | At this point we can issue transactions via the extension to test how things are working. As the chaincode is running separate from the peer's control it's a *lot* easier to attach say a debugger to it.
214 |
215 | In the right window, is the `assetTransfer.ts` file, with a breakpoint added in `InitLedger` (click in the margin to add the breakpoint). By clicking on the InitLedger in the edxpanded Fabric Gateway view, brings up the 'transaction exerciser'.
216 |
217 | 
218 |
219 | To connect the debugger, click on the VSCode debugging icon in the toolbar. You'll need to have a configuration for attaching to a remote node process.
220 | ```
221 | {
222 | "name": "Attach by Process ID",
223 | "processId": "${command:PickProcess}",
224 | "request": "attach",
225 | "skipFiles": [
226 | "/**"
227 | ],
228 | "type": "pwa-node"
229 | }
230 | ```
231 |
232 | Check the VSCode documentation on debugging for more info on how to connect - the chaincode here is, as far as VSCode is concerned, no different from any other node.js application.
233 |
234 | Once connected, in the transaction explorer view, `InitLedger` is already selected, and it doesn't need any arguments. Click on `SubmitTransaction`, and you'll find the debugger will stop at the set break point and you can step over the code.
235 |
236 | It's advisable not to take too long, as there is a transaction timeout!
237 |
238 | When you've identified any problems, and made code changes the approach is to rebuild the code, kill the running node.js process and re run it.
239 |
240 | > You don't need to install, approve or commit again. Just stop, rebuild and restart the chaincode.
241 |
242 | ## Practical - with Fabric's Test Network
243 | The `test-network` and some of the chaincodes have been updated to support running chaincode-as-a-service. The commands below assume that you've got the latest fabric-samples cloned, along with the latest Fabric docker images.
244 |
245 | It's useful to have two terminal windows open, one for starting the Fabric Network, and a second for monitoring all the docker containers.
246 |
247 | In your 'monitoring' window, run this to watch all activity from the all the docker containers on the `fabric_test` network; this will monitor all the docker containers that are added to the `fabric-test` network. The network is usually created by the `./network.sh up` command, so remember to delay running this until at least the network is created. It is possible to precreate the network with `docker network create fabric-test` if you wish.
248 |
249 | ```bash
250 | # from the fabric-samples repo
251 | ./test-network/monitordocker.sh
252 | ```
253 |
254 | In the 'Fabric Network' window, start the test network
255 |
256 | ```bash
257 | cd test-network
258 | ./network.sh up createChannel
259 | ```
260 |
261 | You can run other variants of this command, eg to use CouchDB or CAs, without affecting the '-as-a-service' feature. The three keys steps are:
262 |
263 | - Build a docker image of the contract. Both `/asset-transfer-basic/chaincode-typescript` and `/asset-transfer-basic/chaincode-java` have been updated with Dockerfiles
264 | - Install, Approve and Commit a chaincode definition. This is unchanged, but the chaincode package contains connection information (hostname,port,tls certificates etc.), not code
265 | - Start the docker container(s) containing the contract
266 |
267 | Note that the order listed isn't mandatory. The key thing is that the containers are running before the first transaction is set by the peer. Remember that this could be on the `commit` if the `initRequired` flag is set.
268 |
269 | This sequence can be run as follows
270 |
271 | ```bash
272 | ./network.sh deployCCAAS -ccn basicts -ccp ../asset-transfer-basic/chaincode-typescript
273 | ```
274 |
275 | This is very similar to the `deployCC` command, it needs the name, and path. But also needs to have the port the chaincode container is going use. As each container is on the `fabric-test` network, you might wish to alter this so there are no collisions with other chaincode containers.
276 |
277 | You should be able to see the contract starting in the monitoring window. There will be two containers running, one for org1 and one for org2. The container names contain the organization/peer and the name of the chaincode.
278 |
279 | To test things are working you can invoke the 'Contract Metadata' function. For information on how to work as different organizations see [Interacting with the network](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html#interacting-with-the-network)
280 |
281 | ```bash
282 | # Environment variables for Org1
283 |
284 | export CORE_PEER_TLS_ENABLED=true
285 | export CORE_PEER_LOCALMSPID="Org1MSP"
286 | export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
287 | export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
288 | export CORE_PEER_ADDRESS=localhost:7051
289 | export PATH=${PWD}/../bin:$PATH
290 | export FABRIC_CFG_PATH=${PWD}/../config
291 |
292 | # invoke the function
293 | peer chaincode query -C mychannel -n basicts -c '{"Args":["org.hyperledger.fabric:GetMetadata"]}' | jq
294 | ```
295 |
296 | If you don't have `jq` installed omit `| jq`. The metadata shows the details of the deployed contract and is JSON, so jq makes it easier to read. You can repeat the above commands for org2 to confirm that is working.
297 |
298 | To run the Java example, change the `deployCCAAS` command as follows, This will create two new containers.
299 |
300 | ```bash
301 | ./network.sh deployCCAAS -ccn basicj -ccp ../asset-transfer-basic/chaincode-java
302 | ```
303 |
304 | ### Troubleshooting
305 |
306 | If the JSON structure passed in is badly formatted JSON this error will be in the peer log:
307 |
308 | ```
309 | ::Error: Failed to unmarshal json: cannot unmarshal string into Go value of type map[string]interface {} command=build
310 | ```
311 |
312 | ## How to configure each language
313 |
314 | Each language can work in the '-as-a-service' mode. Note that the approaches here are based on the very latest libraries.
315 | When starting the image you can also specify any of the TLS options or additional logging options for the respective chaincode libraries.
316 |
317 | ### Java
318 |
319 | With the v2.4.1 Java Chaincode libraries, there are no code changes to make or build changes. The '-as-a-service' mode will be used if the environment variable `CHAINCODE_SERVER_ADDRESS` is set.
320 |
321 | A sample docker run command could be as follows. The two key variables that are needed are the `CHAINCODE_SERVER_ADDRESS` and `CORE_CHAICODE_ID_NAME`
322 |
323 | ```bash
324 | docker run --rm -d --name peer0org1_assettx_ccaas \
325 | --network fabric_test \
326 | -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:9999 \
327 | -e CORE_CHAINCODE_ID_NAME=