From 53f0ef0d9239dc0277374d8880e766fe97e59222 Mon Sep 17 00:00:00 2001 From: fraVlaca <86831094+fraVlaca@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:47:25 +0100 Subject: [PATCH] updated chaincodes for asset-transfer-basic in order to show good example on how achieving determinism over json (#486) * updated chaincodes for asset-trnsfer-basic in order to show good example on how achieving determinism in json Signed-off-by: fraVlaca * final fixes for chaincode-java of asset-tranfer-basic Signed-off-by: fraVlaca * removed extra unused excheptions Signed-off-by: fraVlaca * corrected indentation of contract in the chancode-javasript of asset-trnsfer-basic Signed-off-by: fraVlaca * last fixes for chaincode-javascript of asset-transfer-basic Signed-off-by: fraVlaca * last last fixes for chaincode-javascript of asset-transfer-basic Signed-off-by: fraVlaca --- .../chaincode-go/chaincode/smartcontract.go | 10 +++++---- .../samples/assettransfer/AssetTransfer.java | 19 ++++++++++------- .../chaincode-javascript/lib/assetTransfer.js | 21 +++++++++++++------ .../chaincode-javascript/package.json | 4 +++- .../chaincode-typescript/package.json | 4 +++- .../chaincode-typescript/src/assetTransfer.ts | 21 +++++++++++++------ 6 files changed, 53 insertions(+), 26 deletions(-) diff --git a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go index 71e8dd84..7373ad42 100644 --- a/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go +++ b/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go @@ -13,12 +13,14 @@ 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 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..fa7aba67 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 @@ -7,6 +7,7 @@ package org.hyperledger.fabric.samples.assettransfer; import java.util.ArrayList; import java.util.List; + import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contact; @@ -86,8 +87,9 @@ public final class AssetTransfer implements ContractInterface { } Asset asset = new Asset(assetID, color, size, owner, appraisedValue); - String assetJSON = genson.serialize(asset); - stub.putStringState(assetID, assetJSON); + //Use Genson to convert the Asset into string, sort it alphabetically and serialize it into a json string + String sortedJson = genson.serialize(asset); + stub.putStringState(assetID, sortedJson); return asset; } @@ -137,9 +139,9 @@ public final class AssetTransfer implements ContractInterface { } Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue); - String newAssetJSON = genson.serialize(newAsset); - stub.putStringState(assetID, newAssetJSON); - + //Use Genson to convert the Asset into string, sort it alphabetically and serialize it into a json string + String sortedJson = genson.serialize(newAsset); + stub.putStringState(assetID, sortedJson); return newAsset; } @@ -199,8 +201,9 @@ public final class AssetTransfer implements ContractInterface { Asset asset = genson.deserialize(assetJSON, Asset.class); Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue()); - String newAssetJSON = genson.serialize(newAsset); - stub.putStringState(assetID, newAssetJSON); + //Use a Genson to conver the Asset into string, sort it alphabetically and serialize it into a json string + String sortedJson = genson.serialize(newAsset); + stub.putStringState(assetID, sortedJson); return newAsset; } @@ -225,8 +228,8 @@ public final class AssetTransfer implements ContractInterface { for (KeyValue result: results) { Asset asset = genson.deserialize(result.getStringValue(), Asset.class); + System.out.println(asset); queryResults.add(asset); - System.out.println(asset.toString()); } final String response = genson.serialize(queryResults); diff --git a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js index a1a27aa4..22d109c8 100644 --- a/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js +++ b/asset-transfer-basic/chaincode-javascript/lib/assetTransfer.js @@ -6,6 +6,9 @@ '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 { @@ -58,8 +61,11 @@ class AssetTransfer extends Contract { for (const asset of assets) { asset.docType = 'asset'; - await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset))); - console.info(`Asset ${asset.ID} initialized`); + // 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)))); } } @@ -77,7 +83,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 +112,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 +136,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 +155,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..430f9f61 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.1", + "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..db2f9a9e 100644 --- a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts +++ b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts @@ -1,7 +1,9 @@ /* * SPDX-License-Identifier: Apache-2.0 */ - +// Deterministic JSON.stringify() +import * as stringify from 'json-stringify-deterministic'; +import * as sortKeysRecursive from 'sort-keys-recursive'; import {Context, Contract, Info, Returns, Transaction} from 'fabric-contract-api'; import {Asset} from './asset'; @@ -57,7 +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`); } } @@ -77,7 +83,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 +113,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 +141,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 +162,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);