mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-26 03:25:09 +00:00
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>
This commit is contained in:
parent
bd559c81c8
commit
b933b35a95
7 changed files with 71 additions and 39 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ package org.hyperledger.fabric.samples.assettransfer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.hyperledger.fabric.contract.Context;
|
||||
import org.hyperledger.fabric.contract.ContractInterface;
|
||||
import org.hyperledger.fabric.contract.annotation.Contact;
|
||||
|
|
@ -51,7 +54,7 @@ public final class AssetTransfer implements ContractInterface {
|
|||
* @param ctx the transaction context
|
||||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public void InitLedger(final Context ctx) {
|
||||
public void InitLedger(final Context ctx) throws JsonProcessingException {
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
|
||||
CreateAsset(ctx, "asset1", "blue", 5, "Tomoko", 300);
|
||||
|
|
@ -76,7 +79,7 @@ public final class AssetTransfer implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size,
|
||||
final String owner, final int appraisedValue) {
|
||||
final String owner, final int appraisedValue) throws JsonProcessingException {
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
|
||||
if (AssetExists(ctx, assetID)) {
|
||||
|
|
@ -86,8 +89,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;
|
||||
}
|
||||
|
|
@ -100,7 +104,7 @@ public final class AssetTransfer implements ContractInterface {
|
|||
* @return the asset found on the ledger if there was one
|
||||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public Asset ReadAsset(final Context ctx, final String assetID) {
|
||||
public Asset ReadAsset(final Context ctx, final String assetID) throws JsonProcessingException, IOException {
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
String assetJSON = stub.getStringState(assetID);
|
||||
|
||||
|
|
@ -127,7 +131,7 @@ public final class AssetTransfer implements ContractInterface {
|
|||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size,
|
||||
final String owner, final int appraisedValue) {
|
||||
final String owner, final int appraisedValue) throws JsonProcessingException {
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
|
||||
if (!AssetExists(ctx, assetID)) {
|
||||
|
|
@ -137,9 +141,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;
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +190,7 @@ public final class AssetTransfer implements ContractInterface {
|
|||
* @return the updated asset
|
||||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) {
|
||||
public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) throws JsonProcessingException {
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
String assetJSON = stub.getStringState(assetID);
|
||||
|
||||
|
|
@ -199,8 +203,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;
|
||||
}
|
||||
|
|
@ -212,7 +217,7 @@ public final class AssetTransfer implements ContractInterface {
|
|||
* @return array of assets found on the ledger
|
||||
*/
|
||||
@Transaction(intent = Transaction.TYPE.EVALUATE)
|
||||
public String GetAllAssets(final Context ctx) {
|
||||
public String GetAllAssets(final Context ctx) throws JsonProcessingException, IOException {
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
|
||||
List<Asset> queryResults = new ArrayList<Asset>();
|
||||
|
|
@ -225,8 +230,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);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ import org.junit.jupiter.api.Nested;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.io.IOException;
|
||||
|
||||
public final class AssetTransferTest {
|
||||
|
||||
private final class MockKeyValue implements KeyValue {
|
||||
|
|
@ -109,7 +112,7 @@ public final class AssetTransferTest {
|
|||
class InvokeReadAssetTransaction {
|
||||
|
||||
@Test
|
||||
public void whenAssetExists() {
|
||||
public void whenAssetExists() throws JsonProcessingException, IOException {
|
||||
AssetTransfer contract = new AssetTransfer();
|
||||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
|
|
@ -141,7 +144,7 @@ public final class AssetTransferTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void invokeInitLedgerTransaction() {
|
||||
void invokeInitLedgerTransaction() throws JsonProcessingException {
|
||||
AssetTransfer contract = new AssetTransfer();
|
||||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
|
|
@ -180,7 +183,7 @@ public final class AssetTransferTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void whenAssetDoesNotExist() {
|
||||
public void whenAssetDoesNotExist() throws JsonProcessingException {
|
||||
AssetTransfer contract = new AssetTransfer();
|
||||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
|
|
@ -194,7 +197,7 @@ public final class AssetTransferTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void invokeGetAllAssetsTransaction() {
|
||||
void invokeGetAllAssetsTransaction() throws JsonProcessingException, IOException {
|
||||
AssetTransfer contract = new AssetTransfer();
|
||||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
|
|
@ -216,7 +219,7 @@ public final class AssetTransferTest {
|
|||
class TransferAssetTransaction {
|
||||
|
||||
@Test
|
||||
public void whenAssetExists() {
|
||||
public void whenAssetExists() throws JsonProcessingException {
|
||||
AssetTransfer contract = new AssetTransfer();
|
||||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
|
|
@ -251,7 +254,7 @@ public final class AssetTransferTest {
|
|||
class UpdateAssetTransaction {
|
||||
|
||||
@Test
|
||||
public void whenAssetExists() {
|
||||
public void whenAssetExists() throws JsonProcessingException {
|
||||
AssetTransfer contract = new AssetTransfer();
|
||||
Context ctx = mock(Context.class);
|
||||
ChaincodeStub stub = mock(ChaincodeStub.class);
|
||||
|
|
|
|||
|
|
@ -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,9 +61,12 @@ 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))));
|
||||
}
|
||||
}
|
||||
|
||||
// CreateAsset issues a new asset to the world state with given details.
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,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';
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue