mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-26 11:35:10 +00:00
modified asset-transef-basic chaincode files to achieve json determinism plus added new java contract that runs on vscode aswell
Signed-off-by: fraVlaca <ocsenarf@outlook.com>
This commit is contained in:
parent
1aba810ebf
commit
235df3271d
39 changed files with 592 additions and 47 deletions
18
asset-transfer-basic/application-java/.classpath
Normal file
18
asset-transfer-basic/application-java/.classpath
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="bin/main" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/main" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
|
|
@ -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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
19
asset-transfer-basic/chaincode-java/.classpath
Normal file
19
asset-transfer-basic/chaincode-java/.classpath
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="bin/main" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="test"/>
|
||||
<attribute name="gradle_used_by_scope" value="test"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
|
|
@ -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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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.+'
|
||||
|
|
|
|||
|
|
@ -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<String, Object> map = om.readValue(json, HashMap.class);
|
||||
String sortedJson = om.writeValueAsString(map);
|
||||
stub.putStringState(assetID, sortedJson);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
/*String json = gson.toJson(asset);
|
||||
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
|
||||
String sortedJson = gson.toJson(map);
|
||||
stub.putStringState(assetID, sortedJson);*/
|
||||
|
||||
//could use JSON Object approach instead
|
||||
/*Map<String, String> 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<String, String> 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<String, Object> 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<String, Object> map = gson.fromJson(json, TreeMap.class);
|
||||
String sortedJson = gson.toJson(map);
|
||||
stub.putStringState(assetID, sortedJson);*/
|
||||
|
||||
//could use JSON Object approach instead
|
||||
/*Map<String, String> 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<String, String> 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<String, Object> map = om.readValue(json, HashMap.class);
|
||||
String sortedJson = om.writeValueAsString(map);
|
||||
stub.putStringState(assetID, sortedJson);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
/*String json = gson.toJson(asset);
|
||||
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
|
||||
String sortedJson = gson.toJson(map);
|
||||
stub.putStringState(assetID, sortedJson);*/
|
||||
|
||||
//could use JSON Object approach instead
|
||||
/*Map<String, String> 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<String, String> 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<KeyValue> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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);
|
||||
|
|
|
|||
11
asset-transfer-basic/java/.fabricignore
Normal file
11
asset-transfer-basic/java/.fabricignore
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
/.classpath
|
||||
/.git/
|
||||
/.gradle/
|
||||
/.project
|
||||
/.settings/
|
||||
/bin/
|
||||
/build/
|
||||
10
asset-transfer-basic/java/.gitignore
vendored
Normal file
10
asset-transfer-basic/java/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
/.classpath
|
||||
/.gradle/
|
||||
/.project
|
||||
/.settings/
|
||||
/bin/
|
||||
/build/
|
||||
52
asset-transfer-basic/java/build.gradle
Normal file
52
asset-transfer-basic/java/build.gradle
Normal file
|
|
@ -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"
|
||||
}
|
||||
BIN
asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
asset-transfer-basic/java/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -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
|
||||
172
asset-transfer-basic/java/gradlew
vendored
Executable file
172
asset-transfer-basic/java/gradlew
vendored
Executable file
|
|
@ -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" "$@"
|
||||
84
asset-transfer-basic/java/gradlew.bat
vendored
Normal file
84
asset-transfer-basic/java/gradlew.bat
vendored
Normal file
|
|
@ -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
|
||||
5
asset-transfer-basic/java/settings.gradle
Normal file
5
asset-transfer-basic/java/settings.gradle
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
rootProject.name = 'java'
|
||||
|
||||
|
|
@ -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": {}
|
||||
}
|
||||
]
|
||||
Loading…
Reference in a new issue