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": {}
+ }
+]