mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-26 11:35:10 +00:00
Merge branch 'main' into feature/kind-test-network
This commit is contained in:
commit
866a1ab2dd
9 changed files with 131 additions and 27 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,22 @@
|
||||||
You can use the `./network.sh` script to stand up a simple Fabric test network. The test network has two peer organizations with one peer each and a single node raft ordering service. You can also use the `./network.sh` script to create channels and deploy chaincode. For more information, see [Using the Fabric test network](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html). The test network is being introduced in Fabric v2.0 as the long term replacement for the `first-network` sample.
|
You can use the `./network.sh` script to stand up a simple Fabric test network. The test network has two peer organizations with one peer each and a single node raft ordering service. You can also use the `./network.sh` script to create channels and deploy chaincode. For more information, see [Using the Fabric test network](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html). The test network is being introduced in Fabric v2.0 as the long term replacement for the `first-network` sample.
|
||||||
|
|
||||||
Before you can deploy the test network, you need to follow the instructions to [Install the Samples, Binaries and Docker Images](https://hyperledger-fabric.readthedocs.io/en/latest/install.html) in the Hyperledger Fabric documentation.
|
Before you can deploy the test network, you need to follow the instructions to [Install the Samples, Binaries and Docker Images](https://hyperledger-fabric.readthedocs.io/en/latest/install.html) in the Hyperledger Fabric documentation.
|
||||||
|
|
||||||
|
## Using the Peer commands
|
||||||
|
The `setOrgEnv.sh` script can be used to setup the environment variables for the ogrganziations, this will will help to be able to use the `peer` commands directly.
|
||||||
|
|
||||||
|
First, ensure that the peer binaries are on your path, and the Fabric Config path is set Assuming that you're in the `test-network` directory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PATH=$PATH:$(realpath ../bin)
|
||||||
|
export FABRIC_CFG_PATH=$(realpath ../config)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then set up the environment variables for each organization. The `./setOrgEnv.sh` command is designed to be run as follows.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export $(./setOrgEnv.sh Org2 | xargs)
|
||||||
|
```
|
||||||
|
|
||||||
|
You will now be able to run the `peer` commands in the context of Org2. If a different command prompt you can run the same command with Org1 instead.
|
||||||
|
The `setOrgEnv` script outputs a series of `<name>=<value>` strings. These can then be fed into the export command for your current shell
|
||||||
|
|
@ -151,7 +151,6 @@ services:
|
||||||
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
|
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
|
||||||
command: /bin/bash
|
command: /bin/bash
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/:/host/var/run/
|
|
||||||
- ../organizations:/opt/gopath/src/github.com/hyperledger/fabric/peer/organizations
|
- ../organizations:/opt/gopath/src/github.com/hyperledger/fabric/peer/organizations
|
||||||
- ../scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
|
- ../scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
|
||||||
59
test-network/setOrgEnv.sh
Executable file
59
test-network/setOrgEnv.sh
Executable file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# default to using Org1
|
||||||
|
ORG=${1:-Org1}
|
||||||
|
|
||||||
|
# Exit on first error, print all commands.
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# Where am I?
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"
|
||||||
|
|
||||||
|
ORDERER_CA=${DIR}/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
|
||||||
|
PEER0_ORG1_CA=${DIR}/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
|
||||||
|
PEER0_ORG2_CA=${DIR}/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
|
||||||
|
PEER0_ORG3_CA=${DIR}/test-network/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
|
||||||
|
|
||||||
|
|
||||||
|
if [[ ${ORG,,} == "org1" || ${ORG,,} == "digibank" ]]; then
|
||||||
|
|
||||||
|
CORE_PEER_LOCALMSPID=Org1MSP
|
||||||
|
CORE_PEER_MSPCONFIGPATH=${DIR}/test-network/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
|
||||||
|
CORE_PEER_ADDRESS=localhost:7051
|
||||||
|
CORE_PEER_TLS_ROOTCERT_FILE=${DIR}/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
|
||||||
|
|
||||||
|
elif [[ ${ORG,,} == "org2" || ${ORG,,} == "magnetocorp" ]]; then
|
||||||
|
|
||||||
|
CORE_PEER_LOCALMSPID=Org2MSP
|
||||||
|
CORE_PEER_MSPCONFIGPATH=${DIR}/test-network/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
|
||||||
|
CORE_PEER_ADDRESS=localhost:9051
|
||||||
|
CORE_PEER_TLS_ROOTCERT_FILE=${DIR}/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Unknown \"$ORG\", please choose Org1/Digibank or Org2/Magnetocorp"
|
||||||
|
echo "For example to get the environment variables to set upa Org2 shell environment run: ./setOrgEnv.sh Org2"
|
||||||
|
echo
|
||||||
|
echo "This can be automated to set them as well with:"
|
||||||
|
echo
|
||||||
|
echo 'export $(./setOrgEnv.sh Org2 | xargs)'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# output the variables that need to be set
|
||||||
|
echo "CORE_PEER_TLS_ENABLED=true"
|
||||||
|
echo "ORDERER_CA=${ORDERER_CA}"
|
||||||
|
echo "PEER0_ORG1_CA=${PEER0_ORG1_CA}"
|
||||||
|
echo "PEER0_ORG2_CA=${PEER0_ORG2_CA}"
|
||||||
|
echo "PEER0_ORG3_CA=${PEER0_ORG3_CA}"
|
||||||
|
|
||||||
|
echo "CORE_PEER_MSPCONFIGPATH=${CORE_PEER_MSPCONFIGPATH}"
|
||||||
|
echo "CORE_PEER_ADDRESS=${CORE_PEER_ADDRESS}"
|
||||||
|
echo "CORE_PEER_TLS_ROOTCERT_FILE=${CORE_PEER_TLS_ROOTCERT_FILE}"
|
||||||
|
|
||||||
|
echo "CORE_PEER_LOCALMSPID=${CORE_PEER_LOCALMSPID}"
|
||||||
Loading…
Reference in a new issue