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 <ocsenarf@outlook.com>

* final fixes for chaincode-java of asset-tranfer-basic

Signed-off-by: fraVlaca <ocsenarf@outlook.com>

* removed extra unused excheptions

Signed-off-by: fraVlaca <ocsenarf@outlook.com>

* corrected indentation of contract in the chancode-javasript of asset-trnsfer-basic

Signed-off-by: fraVlaca <ocsenarf@outlook.com>

* last fixes for chaincode-javascript of asset-transfer-basic

Signed-off-by: fraVlaca <ocsenarf@outlook.com>

* last last fixes for chaincode-javascript of asset-transfer-basic

Signed-off-by: fraVlaca <ocsenarf@outlook.com>
This commit is contained in:
fraVlaca 2021-09-06 13:47:25 +01:00 committed by GitHub
parent bd559c81c8
commit 53f0ef0d92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 26 deletions

View file

@ -13,12 +13,14 @@ type SmartContract struct {
} }
// Asset describes basic details of what makes up a simple asset // 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 { type Asset struct {
AppraisedValue int `json:"AppraisedValue"`
Color string `json:"Color"`
ID string `json:"ID"` ID string `json:"ID"`
Color string `json:"color"` Owner string `json:"Owner"`
Size int `json:"size"` Size int `json:"Size"`
Owner string `json:"owner"`
AppraisedValue int `json:"appraisedValue"`
} }
// InitLedger adds a base set of assets to the ledger // InitLedger adds a base set of assets to the ledger

View file

@ -7,6 +7,7 @@ package org.hyperledger.fabric.samples.assettransfer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.Contact; 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); Asset asset = new Asset(assetID, color, size, owner, appraisedValue);
String assetJSON = genson.serialize(asset); //Use Genson to convert the Asset into string, sort it alphabetically and serialize it into a json string
stub.putStringState(assetID, assetJSON); String sortedJson = genson.serialize(asset);
stub.putStringState(assetID, sortedJson);
return asset; return asset;
} }
@ -137,9 +139,9 @@ public final class AssetTransfer implements ContractInterface {
} }
Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue); Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue);
String newAssetJSON = genson.serialize(newAsset); //Use Genson to convert the Asset into string, sort it alphabetically and serialize it into a json string
stub.putStringState(assetID, newAssetJSON); String sortedJson = genson.serialize(newAsset);
stub.putStringState(assetID, sortedJson);
return newAsset; return newAsset;
} }
@ -199,8 +201,9 @@ public final class AssetTransfer implements ContractInterface {
Asset asset = genson.deserialize(assetJSON, Asset.class); Asset asset = genson.deserialize(assetJSON, Asset.class);
Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue()); Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue());
String newAssetJSON = genson.serialize(newAsset); //Use a Genson to conver the Asset into string, sort it alphabetically and serialize it into a json string
stub.putStringState(assetID, newAssetJSON); String sortedJson = genson.serialize(newAsset);
stub.putStringState(assetID, sortedJson);
return newAsset; return newAsset;
} }
@ -225,8 +228,8 @@ public final class AssetTransfer implements ContractInterface {
for (KeyValue result: results) { for (KeyValue result: results) {
Asset asset = genson.deserialize(result.getStringValue(), Asset.class); Asset asset = genson.deserialize(result.getStringValue(), Asset.class);
System.out.println(asset);
queryResults.add(asset); queryResults.add(asset);
System.out.println(asset.toString());
} }
final String response = genson.serialize(queryResults); final String response = genson.serialize(queryResults);

View file

@ -6,6 +6,9 @@
'use strict'; 'use strict';
// Deterministic JSON.stringify()
const stringify = require('json-stringify-deterministic');
const sortKeysRecursive = require('sort-keys-recursive');
const { Contract } = require('fabric-contract-api'); const { Contract } = require('fabric-contract-api');
class AssetTransfer extends Contract { class AssetTransfer extends Contract {
@ -58,8 +61,11 @@ class AssetTransfer extends Contract {
for (const asset of assets) { for (const asset of assets) {
asset.docType = 'asset'; asset.docType = 'asset';
await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset))); // example of how to write to world state deterministically
console.info(`Asset ${asset.ID} initialized`); // 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, Owner: owner,
AppraisedValue: appraisedValue, 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); return JSON.stringify(asset);
} }
@ -105,7 +112,8 @@ class AssetTransfer extends Contract {
Owner: owner, Owner: owner,
AppraisedValue: appraisedValue, 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. // 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 assetString = await this.ReadAsset(ctx, id);
const asset = JSON.parse(assetString); const asset = JSON.parse(assetString);
asset.Owner = newOwner; 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. // GetAllAssets returns all assets found in the world state.
@ -146,7 +155,7 @@ class AssetTransfer extends Contract {
console.log(err); console.log(err);
record = strValue; record = strValue;
} }
allResults.push({ Key: result.value.key, Record: record }); allResults.push(record);
result = await iterator.next(); result = await iterator.next();
} }
return JSON.stringify(allResults); return JSON.stringify(allResults);

View file

@ -18,7 +18,9 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"fabric-contract-api": "^2.0.0", "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": { "devDependencies": {
"chai": "^4.1.2", "chai": "^4.1.2",

View file

@ -22,7 +22,9 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"fabric-contract-api": "^2.0.0", "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": { "devDependencies": {
"@types/chai": "^4.1.7", "@types/chai": "^4.1.7",

View file

@ -1,7 +1,9 @@
/* /*
* SPDX-License-Identifier: Apache-2.0 * 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 {Context, Contract, Info, Returns, Transaction} from 'fabric-contract-api';
import {Asset} from './asset'; import {Asset} from './asset';
@ -57,7 +59,11 @@ export class AssetTransferContract extends Contract {
for (const asset of assets) { for (const asset of assets) {
asset.docType = 'asset'; 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`); console.info(`Asset ${asset.ID} initialized`);
} }
} }
@ -77,7 +83,8 @@ export class AssetTransferContract extends Contract {
Owner: owner, Owner: owner,
AppraisedValue: appraisedValue, 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. // ReadAsset returns the asset stored in the world state with given id.
@ -106,7 +113,8 @@ export class AssetTransferContract extends Contract {
Owner: owner, Owner: owner,
AppraisedValue: appraisedValue, 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. // 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 assetString = await this.ReadAsset(ctx, id);
const asset = JSON.parse(assetString); const asset = JSON.parse(assetString);
asset.Owner = newOwner; 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. // GetAllAssets returns all assets found in the world state.
@ -153,7 +162,7 @@ export class AssetTransferContract extends Contract {
console.log(err); console.log(err);
record = strValue; record = strValue;
} }
allResults.push({Key: result.value.key, Record: record}); allResults.push(record);
result = await iterator.next(); result = await iterator.next();
} }
return JSON.stringify(allResults); return JSON.stringify(allResults);