diff --git a/asset-transfer-basic/application-java/.classpath b/asset-transfer-basic/application-java/.classpath new file mode 100644 index 00000000..6d924d76 --- /dev/null +++ b/asset-transfer-basic/application-java/.classpath @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/asset-transfer-basic/application-java/.settings/org.eclipse.buildship.core.prefs b/asset-transfer-basic/application-java/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..98515123 --- /dev/null +++ b/asset-transfer-basic/application-java/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/asset-transfer-basic/application-java/bin/main/application/java/App.class b/asset-transfer-basic/application-java/bin/main/application/java/App.class new file mode 100644 index 00000000..4df6f455 Binary files /dev/null and b/asset-transfer-basic/application-java/bin/main/application/java/App.class differ diff --git a/asset-transfer-basic/application-java/bin/main/application/java/EnrollAdmin.class b/asset-transfer-basic/application-java/bin/main/application/java/EnrollAdmin.class new file mode 100644 index 00000000..adc60369 Binary files /dev/null and b/asset-transfer-basic/application-java/bin/main/application/java/EnrollAdmin.class differ diff --git a/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser$1$1.class b/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser$1$1.class new file mode 100644 index 00000000..435c271a Binary files /dev/null and b/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser$1$1.class differ diff --git a/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser$1.class b/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser$1.class new file mode 100644 index 00000000..a5ca91c0 Binary files /dev/null and b/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser$1.class differ diff --git a/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser.class b/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser.class new file mode 100644 index 00000000..ff58f1a2 Binary files /dev/null and b/asset-transfer-basic/application-java/bin/main/application/java/RegisterUser.class differ diff --git a/asset-transfer-basic/application-java/bin/main/log4j.properties b/asset-transfer-basic/application-java/bin/main/log4j.properties new file mode 100644 index 00000000..f1f841fe --- /dev/null +++ b/asset-transfer-basic/application-java/bin/main/log4j.properties @@ -0,0 +1,19 @@ +# initialize root logger with level ERROR for stdout and fout +log4j.rootLogger=ERROR,stdout,fout +# set the log level for these components +log4j.logger.com.endeca=INFO +log4j.logger.com.endeca.itl.web.metrics=INFO + +# add a ConsoleAppender to the logger stdout to write to the console +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +# use a simple message format +log4j.appender.stdout.layout.ConversionPattern=%m%n + +# add a FileAppender to the logger fout +log4j.appender.fout=org.apache.log4j.FileAppender +# create a log file +log4j.appender.fout.File=crawl.log +log4j.appender.fout.layout=org.apache.log4j.PatternLayout +# use a more detailed message pattern +log4j.appender.fout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n diff --git a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go index 71e8dd84..ced42c4c 100644 --- a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go +++ b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go @@ -13,12 +13,15 @@ type SmartContract struct { } // Asset describes basic details of what makes up a simple asset + +// Insert struct field in alphabetic order => to achieve determinism accross languages +// golang keeps the order when marshal to json but doesn't order automatically type Asset struct { + AppraisedValue int `json:"AppraisedValue"` + Color string `json:"Color"` ID string `json:"ID"` - Color string `json:"color"` - Size int `json:"size"` - Owner string `json:"owner"` - AppraisedValue int `json:"appraisedValue"` + Owner string `json:"Owner"` + Size int `json:"Size"` } // InitLedger adds a base set of assets to the ledger @@ -56,7 +59,7 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, if exists { return fmt.Errorf("the asset %s already exists", id) } - + asset := Asset{ ID: id, Color: color, @@ -182,4 +185,4 @@ func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface } return assets, nil -} +} \ No newline at end of file diff --git a/asset-transfer-basic/chaincode-java/.classpath b/asset-transfer-basic/chaincode-java/.classpath new file mode 100644 index 00000000..9f9cc9f1 --- /dev/null +++ b/asset-transfer-basic/chaincode-java/.classpath @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/asset-transfer-basic/chaincode-java/.settings/org.eclipse.buildship.core.prefs b/asset-transfer-basic/chaincode-java/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..98515123 --- /dev/null +++ b/asset-transfer-basic/chaincode-java/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/Asset.class b/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/Asset.class new file mode 100644 index 00000000..064237ea Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/Asset.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/AssetTransfer$AssetTransferErrors.class b/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/AssetTransfer$AssetTransferErrors.class new file mode 100644 index 00000000..f1150e03 Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/AssetTransfer$AssetTransferErrors.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.class b/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.class new file mode 100644 index 00000000..58279bfc Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/main/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTest$Equality.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTest$Equality.class new file mode 100644 index 00000000..535cf70e Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTest$Equality.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTest.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTest.class new file mode 100644 index 00000000..8560ba59 Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTest.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$DeleteAssetTransaction.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$DeleteAssetTransaction.class new file mode 100644 index 00000000..5a59d8b2 Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$DeleteAssetTransaction.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$InvokeCreateAssetTransaction.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$InvokeCreateAssetTransaction.class new file mode 100644 index 00000000..22805dcc Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$InvokeCreateAssetTransaction.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$InvokeReadAssetTransaction.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$InvokeReadAssetTransaction.class new file mode 100644 index 00000000..9e2f610b Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$InvokeReadAssetTransaction.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$MockAssetResultsIterator.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$MockAssetResultsIterator.class new file mode 100644 index 00000000..0e684b6c Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$MockAssetResultsIterator.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$MockKeyValue.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$MockKeyValue.class new file mode 100644 index 00000000..7d27f042 Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$MockKeyValue.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$TransferAssetTransaction.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$TransferAssetTransaction.class new file mode 100644 index 00000000..17f80556 Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$TransferAssetTransaction.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$UpdateAssetTransaction.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$UpdateAssetTransaction.class new file mode 100644 index 00000000..0acaf834 Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest$UpdateAssetTransaction.class differ diff --git a/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.class b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.class new file mode 100644 index 00000000..c879ac7d Binary files /dev/null and b/asset-transfer-basic/chaincode-java/bin/test/org/hyperledger/fabric/samples/assettransfer/AssetTransferTest.class differ diff --git a/asset-transfer-basic/chaincode-java/build.gradle b/asset-transfer-basic/chaincode-java/build.gradle index 1b5a2070..73c92a5c 100644 --- a/asset-transfer-basic/chaincode-java/build.gradle +++ b/asset-transfer-basic/chaincode-java/build.gradle @@ -13,10 +13,11 @@ group 'org.hyperledger.fabric.samples' version '1.0-SNAPSHOT' dependencies { - - compileOnly 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+' - implementation 'com.owlike:genson:1.5' - testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+' + implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.2.0' + implementation 'com.owlike:genson:1.6' + implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.8.9' + implementation 'io.vertx:vertx-core:3.5.3' testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2' testImplementation 'org.assertj:assertj-core:3.11.1' testImplementation 'org.mockito:mockito-core:2.+' diff --git a/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java index 465d4694..e9e321f2 100644 --- a/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java +++ b/asset-transfer-basic/chaincode-java/src/main/java/org/hyperledger/fabric/samples/assettransfer/AssetTransfer.java @@ -6,6 +6,10 @@ package org.hyperledger.fabric.samples.assettransfer; import java.util.ArrayList; import java.util.List; +import java.util.TreeMap; +import java.util.Map; +import java.util.HashMap; + import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; @@ -20,7 +24,9 @@ import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; -import com.owlike.genson.Genson; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; @Contract( name = "basic", @@ -38,7 +44,8 @@ import com.owlike.genson.Genson; @Default public final class AssetTransfer implements ContractInterface { - private final Genson genson = new Genson(); + private Gson gson = new Gson(); + private ObjectMapper om = new ObjectMapper(); private enum AssetTransferErrors { ASSET_NOT_FOUND, @@ -86,8 +93,28 @@ public final class AssetTransfer implements ContractInterface { } Asset asset = new Asset(assetID, color, size, owner, appraisedValue); - String assetJSON = genson.serialize(asset); - stub.putStringState(assetID, assetJSON); + //deserializing your json into a sorted map, and then serialize the map to get the sorted-by-key json string. + //TreeMap will enforce a sorting order for you, independent of what order they're supplied in + String json = gson.toJson(asset); + try { + om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); + Map map = om.readValue(json, HashMap.class); + String sortedJson = om.writeValueAsString(map); + stub.putStringState(assetID, sortedJson); + } catch (Exception e) { + } + /*String json = gson.toJson(asset); + TreeMap map = gson.fromJson(json, TreeMap.class); + String sortedJson = gson.toJson(map); + stub.putStringState(assetID, sortedJson);*/ + + //could use JSON Object approach instead + /*Map sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically + JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result + for (Map.Entry entry : sortedMap.entrySet()) { + sortedJSON.put(entry.getKey(), entry.getValue()); + } + stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */ return asset; } @@ -110,7 +137,7 @@ public final class AssetTransfer implements ContractInterface { throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); } - Asset asset = genson.deserialize(assetJSON, Asset.class); + Asset asset = gson.fromJson(assetJSON, Asset.class); return asset; } @@ -137,8 +164,26 @@ public final class AssetTransfer implements ContractInterface { } Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue); - String newAssetJSON = genson.serialize(newAsset); - stub.putStringState(assetID, newAssetJSON); + String json = gson.toJson(newAsset); + try { + om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); + Map map = om.readValue(json, HashMap.class); + String sortedJson = om.writeValueAsString(map);//could also use gson instead + stub.putStringState(assetID, sortedJson); + } catch (Exception e) { + } + /*String json = gson.toJson(asset); + TreeMap map = gson.fromJson(json, TreeMap.class); + String sortedJson = gson.toJson(map); + stub.putStringState(assetID, sortedJson);*/ + + //could use JSON Object approach instead + /*Map sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically + JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result + for (Map.Entry entry : sortedMap.entrySet()) { + sortedJSON.put(entry.getKey(), entry.getValue()); + } + stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */ return newAsset; } @@ -196,11 +241,29 @@ public final class AssetTransfer implements ContractInterface { throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); } - Asset asset = genson.deserialize(assetJSON, Asset.class); + Asset asset = gson.fromJson(assetJSON, Asset.class); Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue()); - String newAssetJSON = genson.serialize(newAsset); - stub.putStringState(assetID, newAssetJSON); + String json = gson.toJson(newAsset); + try { + om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); + Map map = om.readValue(json, HashMap.class); + String sortedJson = om.writeValueAsString(map); + stub.putStringState(assetID, sortedJson); + } catch (Exception e) { + } + /*String json = gson.toJson(asset); + TreeMap map = gson.fromJson(json, TreeMap.class); + String sortedJson = gson.toJson(map); + stub.putStringState(assetID, sortedJson);*/ + + //could use JSON Object approach instead + /*Map sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically + JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result + for (Map.Entry entry : sortedMap.entrySet()) { + sortedJSON.put(entry.getKey(), entry.getValue()); + } + stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */ return newAsset; } @@ -224,13 +287,13 @@ public final class AssetTransfer implements ContractInterface { QueryResultsIterator results = stub.getStateByRange("", ""); for (KeyValue result: results) { - Asset asset = genson.deserialize(result.getStringValue(), Asset.class); + Asset asset = gson.fromJson(result.getStringValue(), Asset.class); queryResults.add(asset); System.out.println(asset.toString()); } - final String response = genson.serialize(queryResults); + final String response = gson.toJson(queryResults); return response; } -} +} \ No newline at end of file diff --git a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js index a1a27aa4..9732eb7a 100644 --- a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js +++ b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js @@ -6,6 +6,10 @@ 'use strict'; + +//Deterministic JSON.stringify() +const stringify = require('json-stringify-deterministic') +const sortKeysRecursive = require('sort-keys-recursive') const { Contract } = require('fabric-contract-api'); class AssetTransfer extends Contract { @@ -57,19 +61,17 @@ class AssetTransfer extends Contract { ]; for (const asset of assets) { - asset.docType = 'asset'; - await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset))); + //example of how to write to world state deterministically + //use convetion of alphabetic order + //we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' + //when retrieving data, in any lang, the order of data will be the same and consequently also the corresonding hash + await ctx.stub.putState(asset.ID, Buffer.from(stringify(sortKeysRecursive(asset)))); console.info(`Asset ${asset.ID} initialized`); } } // CreateAsset issues a new asset to the world state with given details. async CreateAsset(ctx, id, color, size, owner, appraisedValue) { - const exists = await this.AssetExists(ctx, id); - if (exists) { - throw new Error(`The asset ${id} already exists`); - } - const asset = { ID: id, Color: color, @@ -77,7 +79,8 @@ class AssetTransfer extends Contract { Owner: owner, AppraisedValue: appraisedValue, }; - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + //we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' + await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset)))); return JSON.stringify(asset); } @@ -105,7 +108,8 @@ class AssetTransfer extends Contract { Owner: owner, AppraisedValue: appraisedValue, }; - return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); + //we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' + return ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(updatedAsset)))); } // DeleteAsset deletes an given asset from the world state. @@ -128,7 +132,8 @@ class AssetTransfer extends Contract { const assetString = await this.ReadAsset(ctx, id); const asset = JSON.parse(assetString); asset.Owner = newOwner; - return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + //we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' + return ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset)))); } // GetAllAssets returns all assets found in the world state. @@ -146,7 +151,7 @@ class AssetTransfer extends Contract { console.log(err); record = strValue; } - allResults.push({ Key: result.value.key, Record: record }); + allResults.push(record); result = await iterator.next(); } return JSON.stringify(allResults); diff --git a/asset-transfer-basic/chaincode-javascript/package.json b/asset-transfer-basic/chaincode-javascript/package.json index 9cc22242..b79ae948 100644 --- a/asset-transfer-basic/chaincode-javascript/package.json +++ b/asset-transfer-basic/chaincode-javascript/package.json @@ -18,7 +18,9 @@ "license": "Apache-2.0", "dependencies": { "fabric-contract-api": "^2.0.0", - "fabric-shim": "^2.0.0" + "fabric-shim": "^2.0.0", + "json-stringify-deterministic":"^1.0.0", + "sort-keys-recursive":"^2.0.0" }, "devDependencies": { "chai": "^4.1.2", diff --git a/asset-transfer-basic/chaincode-typescript/package.json b/asset-transfer-basic/chaincode-typescript/package.json index 2b681fc1..befe4a5f 100644 --- a/asset-transfer-basic/chaincode-typescript/package.json +++ b/asset-transfer-basic/chaincode-typescript/package.json @@ -22,7 +22,9 @@ "license": "Apache-2.0", "dependencies": { "fabric-contract-api": "^2.0.0", - "fabric-shim": "^2.0.0" + "fabric-shim": "^2.0.0", + "json-stringify-deterministic":"^1.0.0", + "sort-keys-recursive":"^2.0.0" }, "devDependencies": { "@types/chai": "^4.1.7", diff --git a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts index 55b19b77..3b8148cf 100644 --- a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts +++ b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts @@ -2,6 +2,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +//Deterministic JSON.stringify() +const stringify = require('json-stringify-deterministic') +const sortKeysRecursive = require('sort-keys-recursive') import {Context, Contract, Info, Returns, Transaction} from 'fabric-contract-api'; import {Asset} from './asset'; @@ -56,8 +59,11 @@ export class AssetTransferContract extends Contract { ]; for (const asset of assets) { - asset.docType = 'asset'; - await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset))); + //example of how to write to world state deterministically + //use convetion of alphabetic order + //we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' + //when retrieving data, in any lang, the order of data will be the same and consequently also the corresonding hash + await ctx.stub.putState(asset.ID, Buffer.from(stringify(sortKeysRecursive(asset)))); console.info(`Asset ${asset.ID} initialized`); } } @@ -65,11 +71,6 @@ export class AssetTransferContract extends Contract { // CreateAsset issues a new asset to the world state with given details. @Transaction() public async CreateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number): Promise { - const exists = await this.AssetExists(ctx, id); - if (exists) { - throw new Error(`The asset ${id} already exists`); - } - const asset = { ID: id, Color: color, @@ -77,7 +78,8 @@ export class AssetTransferContract extends Contract { Owner: owner, AppraisedValue: appraisedValue, }; - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + //we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' + await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset)))); } // ReadAsset returns the asset stored in the world state with given id. @@ -106,7 +108,8 @@ export class AssetTransferContract extends Contract { Owner: owner, AppraisedValue: appraisedValue, }; - return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); + //we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' + return ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(updatedAsset)))); } // DeleteAsset deletes an given asset from the world state. @@ -133,7 +136,8 @@ export class AssetTransferContract extends Contract { const assetString = await this.ReadAsset(ctx, id); const asset = JSON.parse(assetString); asset.Owner = newOwner; - await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + //we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' + await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset)))); } // GetAllAssets returns all assets found in the world state. @@ -153,7 +157,7 @@ export class AssetTransferContract extends Contract { console.log(err); record = strValue; } - allResults.push({Key: result.value.key, Record: record}); + allResults.push(record); result = await iterator.next(); } return JSON.stringify(allResults); diff --git a/asset-transfer-basic/java/.fabricignore b/asset-transfer-basic/java/.fabricignore new file mode 100644 index 00000000..4cc531ee --- /dev/null +++ b/asset-transfer-basic/java/.fabricignore @@ -0,0 +1,11 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +/.classpath +/.git/ +/.gradle/ +/.project +/.settings/ +/bin/ +/build/ diff --git a/asset-transfer-basic/java/.gitignore b/asset-transfer-basic/java/.gitignore new file mode 100644 index 00000000..ae1478ca --- /dev/null +++ b/asset-transfer-basic/java/.gitignore @@ -0,0 +1,10 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +/.classpath +/.gradle/ +/.project +/.settings/ +/bin/ +/build/ diff --git a/asset-transfer-basic/java/build.gradle b/asset-transfer-basic/java/build.gradle new file mode 100644 index 00000000..a02eb79a --- /dev/null +++ b/asset-transfer-basic/java/build.gradle @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ +plugins { + id 'com.github.johnrengelman.shadow' version '5.2.0' + id 'java' +} + +version '0.0.1' + +sourceCompatibility = 1.8 + +repositories { + mavenLocal() + mavenCentral() + maven { + url 'https://jitpack.io' + } +} + +dependencies { + implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.2.0' + implementation 'com.owlike:genson:1.6' + implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.8.9' + implementation 'io.vertx:vertx-core:3.5.3' + testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2' + testImplementation 'org.assertj:assertj-core:3.11.1' + testImplementation 'org.mockito:mockito-core:2.+' +} + +shadowJar { + baseName = 'chaincode' + version = null + classifier = null + + manifest { + attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter' + } +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} + + +tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" +} diff --git a/asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.jar b/asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f6b961fd Binary files /dev/null and b/asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.jar differ diff --git a/asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.properties b/asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..7c4388a9 --- /dev/null +++ b/asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/asset-transfer-basic/java/gradlew b/asset-transfer-basic/java/gradlew new file mode 100755 index 00000000..cccdd3d5 --- /dev/null +++ b/asset-transfer-basic/java/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/asset-transfer-basic/java/gradlew.bat b/asset-transfer-basic/java/gradlew.bat new file mode 100644 index 00000000..e95643d6 --- /dev/null +++ b/asset-transfer-basic/java/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/asset-transfer-basic/java/settings.gradle b/asset-transfer-basic/java/settings.gradle new file mode 100644 index 00000000..2182e899 --- /dev/null +++ b/asset-transfer-basic/java/settings.gradle @@ -0,0 +1,5 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ +rootProject.name = 'java' + diff --git a/asset-transfer-basic/java/transaction_data/my-asset-transactions.txdata b/asset-transfer-basic/java/transaction_data/my-asset-transactions.txdata new file mode 100644 index 00000000..06dd2c81 --- /dev/null +++ b/asset-transfer-basic/java/transaction_data/my-asset-transactions.txdata @@ -0,0 +1,44 @@ +[ + { + "transactionName": "myAssetExists", + "transactionLabel": "A test myAssetExists transaction", + "arguments": [ + "001" + ], + "transientData": {} + }, + { + "transactionName": "createMyAsset", + "transactionLabel": "A test createMyAsset transaction", + "arguments": [ + "001", + "some value" + ], + "transientData": {} + }, + { + "transactionName": "readMyAsset", + "transactionLabel": "A test readMyAsset transaction", + "arguments": [ + "001" + ], + "transientData": {} + }, + { + "transactionName": "updateMyAsset", + "transactionLabel": "A test updateMyAsset transaction", + "arguments": [ + "001", + "some other value" + ], + "transientData": {} + }, + { + "transactionName": "deleteMyAsset", + "transactionLabel": "A test deleteMyAsset transaction", + "arguments": [ + "001" + ], + "transientData": {} + } +]