mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-21 09:05:10 +00:00
Move off chain data samples to basic chaincode
Signed-off-by: NIKHIL E GUPTA <ngupta@symbridge.com>
This commit is contained in:
parent
327e3a78aa
commit
6d196bd9df
8 changed files with 189 additions and 382 deletions
|
|
@ -10,9 +10,9 @@ CouchDB.
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
This sample uses Node Fabric SDK application code similar to the `fabcar` sample
|
This sample uses Node Fabric SDK application code to connect to a running instance
|
||||||
to connect to a running instance of the Fabric test network. Make sure that you
|
of the Fabric test network. Make sure that you are running the following
|
||||||
are running the following commands from the `off_chain_data` directory.
|
commands from the `off_chain_data` directory.
|
||||||
|
|
||||||
### Starting the Network
|
### Starting the Network
|
||||||
|
|
||||||
|
|
@ -25,7 +25,8 @@ Use the following command to start the sample network:
|
||||||
This command will deploy an instance of the Fabric test network. The network
|
This command will deploy an instance of the Fabric test network. The network
|
||||||
consists of an ordering service, two peer organizations with one peers each, and
|
consists of an ordering service, two peer organizations with one peers each, and
|
||||||
a CA for each org. The command also creates a channel named `mychannel`. The
|
a CA for each org. The command also creates a channel named `mychannel`. The
|
||||||
marbles chaincode will be installed on both peers and deployed to the channel.
|
`asset-transfer-basic` chaincode will be installed on both peers and deployed to
|
||||||
|
the channel.
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
|
|
@ -55,10 +56,11 @@ If you set the "use_couchdb" option to true in `config.json`, you can run the
|
||||||
following command start a local instance of CouchDB using docker:
|
following command start a local instance of CouchDB using docker:
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run --publish 5990:5984 --detach --name offchaindb couchdb
|
docker run --publish 5990:5984 --detach --name offchaindb couchdb:2.3.1
|
||||||
docker start offchaindb
|
docker start offchaindb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Install dependencies
|
### Install dependencies
|
||||||
|
|
||||||
You need to install Node.js version 8.9.x to use the sample application code.
|
You need to install Node.js version 8.9.x to use the sample application code.
|
||||||
|
|
@ -92,53 +94,7 @@ node blockEventListener.js
|
||||||
|
|
||||||
If the command is successful, you should see the output of the listener reading
|
If the command is successful, you should see the output of the listener reading
|
||||||
the configuration blocks of `mychannel` in addition to the blocks that recorded
|
the configuration blocks of `mychannel` in addition to the blocks that recorded
|
||||||
the approval and commitment of the marbles chaincode definition.
|
the approval and commitment of the assets chaincode definition.
|
||||||
|
|
||||||
```
|
|
||||||
Listening for block events, nextblock: 0
|
|
||||||
Added block 0 to ProcessingMap
|
|
||||||
Added block 1 to ProcessingMap
|
|
||||||
Added block 2 to ProcessingMap
|
|
||||||
Added block 3 to ProcessingMap
|
|
||||||
Added block 4 to ProcessingMap
|
|
||||||
Added block 5 to ProcessingMap
|
|
||||||
Added block 6 to ProcessingMap
|
|
||||||
------------------------------------------------
|
|
||||||
Block Number: 0
|
|
||||||
------------------------------------------------
|
|
||||||
Block Number: 1
|
|
||||||
------------------------------------------------
|
|
||||||
Block Number: 2
|
|
||||||
------------------------------------------------
|
|
||||||
Block Number: 3
|
|
||||||
Block Timestamp: 2019-08-08T19:47:56.148Z
|
|
||||||
ChaincodeID: _lifecycle
|
|
||||||
[]
|
|
||||||
------------------------------------------------
|
|
||||||
Block Number: 4
|
|
||||||
Block Timestamp: 2019-08-08T19:48:00.234Z
|
|
||||||
ChaincodeID: _lifecycle
|
|
||||||
[]
|
|
||||||
------------------------------------------------
|
|
||||||
Block Number: 5
|
|
||||||
Block Timestamp: 2019-08-08T19:48:14.092Z
|
|
||||||
ChaincodeID: _lifecycle
|
|
||||||
[ { key: 'namespaces/fields/marbles/Collections',
|
|
||||||
is_delete: false,
|
|
||||||
value: '\u0012\u0000' },
|
|
||||||
{ key: 'namespaces/fields/marbles/EndorsementInfo',
|
|
||||||
is_delete: false,
|
|
||||||
value: '\u0012\r\n\u00031.0\u0010\u0001\u001a\u0004escc' },
|
|
||||||
{ key: 'namespaces/fields/marbles/Sequence',
|
|
||||||
is_delete: false,
|
|
||||||
value: '\b\u0001' },
|
|
||||||
{ key: 'namespaces/fields/marbles/ValidationInfo',
|
|
||||||
is_delete: false,
|
|
||||||
value: '\u00122\n\u0004vscc\u0012*\n(\u0012\f\u0012\n\b\u0002\u0012\u0002\b\u0000\u0012\u0002\b\u0001\u001a\u000b\u0012\t\n\u0007Org1MSP\u001a\u000b\u0012\t\n\u0007Org2MSP' },
|
|
||||||
{ key: 'namespaces/metadata/marbles',
|
|
||||||
is_delete: false,
|
|
||||||
value: '\n\u0013ChaincodeDefinition\u0012\bSequence\u0012\u000fEndorsementInfo\u0012\u000eValidationInfo\u0012\u000bCollections' } ]
|
|
||||||
```
|
|
||||||
|
|
||||||
`blockEventListener.js` creates a listener named "offchain-listener" on the
|
`blockEventListener.js` creates a listener named "offchain-listener" on the
|
||||||
channel `mychannel`. The listener writes each block added to the channel to a
|
channel `mychannel`. The listener writes each block added to the channel to a
|
||||||
|
|
@ -157,7 +113,7 @@ read-write set.
|
||||||
|
|
||||||
The channel event listener also writes metadata from each block to a log file
|
The channel event listener also writes metadata from each block to a log file
|
||||||
defined as channelid_chaincodeid.log. In this example, events will be written to
|
defined as channelid_chaincodeid.log. In this example, events will be written to
|
||||||
a file named `mychannel_marbles.log`. This allows you to record a history of
|
a file named `mychannel_basic.log`. This allows you to record a history of
|
||||||
changes made by each block for each key in addition to storing the latest value
|
changes made by each block for each key in addition to storing the latest value
|
||||||
of the world state.
|
of the world state.
|
||||||
|
|
||||||
|
|
@ -166,42 +122,42 @@ new window to execute the next parts of the demo.
|
||||||
|
|
||||||
### Generate data on the blockchain
|
### Generate data on the blockchain
|
||||||
|
|
||||||
Now that our listener is setup, we can generate data using the marbles chaincode
|
Now that our listener is setup, we can generate data using the assets chaincode
|
||||||
and use our application to replicate the data to our database. Open a new
|
and use our application to replicate the data to our database. Open a new
|
||||||
terminal and navigate to the `fabric-samples/off_chain_data` directory.
|
terminal and navigate to the `fabric-samples/off_chain_data` directory.
|
||||||
|
|
||||||
You can use the `addMarbles.js` file to add random sample data to blockchain.
|
You can use the `addAssets.js` file to add random sample data to blockchain.
|
||||||
The file uses the configuration information stored in `addMarbles.json` to
|
The file uses the configuration information stored in `addAssets.json` to
|
||||||
create a series of marbles. This file will be created during the first execution
|
create a series of assets. This file will be created during the first execution
|
||||||
of `addMarbles.js` if it does not exist. This program can be run multiple times
|
of `addAssets.js` if it does not exist. This program can be run multiple times
|
||||||
without changing the properties. The `nextMarbleNumber` will be incremented and
|
without changing the properties. The `nextAssetNumber` will be incremented and
|
||||||
stored in the `addMarbles.json` file.
|
stored in the `addAssets.json` file.
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"nextMarbleNumber": 100,
|
"nextAssetNumber": 100,
|
||||||
"numberMarblesToAdd": 20
|
"numberAssetsToAdd": 20
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Open a new window and run the following command to add 20 marbles to the
|
Open a new window and run the following command to add 20 assets to the
|
||||||
blockchain:
|
blockchain:
|
||||||
|
|
||||||
```
|
```
|
||||||
node addMarbles.js
|
node addAssets.js
|
||||||
```
|
```
|
||||||
|
|
||||||
After the marbles have been added to the ledger, use the following command to
|
After the assets have been added to the ledger, use the following command to
|
||||||
transfer one of the marbles to a new owner:
|
transfer one of the assets to a new owner:
|
||||||
|
|
||||||
```
|
```
|
||||||
node transferMarble.js marble110 james
|
node transferAsset.js asset110 james
|
||||||
```
|
```
|
||||||
|
|
||||||
Now run the following command to delete the marble that was transferred:
|
Now run the following command to delete the asset that was transferred:
|
||||||
|
|
||||||
```
|
```
|
||||||
node deleteMarble.js marble110
|
node deleteAsset.js asset110
|
||||||
```
|
```
|
||||||
|
|
||||||
## Offchain CouchDB storage:
|
## Offchain CouchDB storage:
|
||||||
|
|
@ -215,17 +171,17 @@ The first table is an offline representation of the current world state of the
|
||||||
blockchain ledger. This table was created using the read-write set data from
|
blockchain ledger. This table was created using the read-write set data from
|
||||||
the blocks. If the listener is running, this table should be the same as the
|
the blocks. If the listener is running, this table should be the same as the
|
||||||
latest values in the state database running on your peer. The table is named
|
latest values in the state database running on your peer. The table is named
|
||||||
after the channelid and chaincodeid, and is named mychannel_marbles in this
|
after the channelid and chaincodeid, and is named mychannel_basic in this
|
||||||
example. You can navigate to this table using your browser:
|
example. You can navigate to this table using your browser:
|
||||||
http://127.0.0.1:5990/mychannel_marbles/_all_docs
|
http://127.0.0.1:5990/mychannel_basic/_all_docs
|
||||||
|
|
||||||
A second table records each block as a historical record entry, and was created
|
A second table records each block as a historical record entry, and was created
|
||||||
using the block data that was recorded in the log file. The table name appends
|
using the block data that was recorded in the log file. The table name appends
|
||||||
history to the name of the first table, and is named mychannel_marbles_history
|
history to the name of the first table, and is named mychannel_basic_history
|
||||||
in this example. You can also navigate to this table using your browser:
|
in this example. You can also navigate to this table using your browser:
|
||||||
http://127.0.0.1:5990/mychannel_marbles_history/_all_docs
|
http://127.0.0.1:5990/mychannel_basic_history/_all_docs
|
||||||
|
|
||||||
### Configure a map/reduce view for summarizing counts of marbles by color:
|
### Configure a map/reduce view for summarizing counts of assets by color:
|
||||||
|
|
||||||
Now that we have state and history data replicated to tables in CouchDB, we
|
Now that we have state and history data replicated to tables in CouchDB, we
|
||||||
can use the following commands query our off-chain data. We will also add an
|
can use the following commands query our off-chain data. We will also add an
|
||||||
|
|
@ -236,16 +192,16 @@ created when events are received.
|
||||||
Open a new terminal window and execute the following:
|
Open a new terminal window and execute the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -X PUT http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign -d '{"views":{"colorview":{"map":"function (doc) { emit(doc.color, 1);}","reduce":"function ( keys , values , combine ) {return sum( values )}"}}}' -H 'Content-Type:application/json'
|
curl -X PUT http://127.0.0.1:5990/mychannel_basic/_design/colorviewdesign -d '{"views":{"colorview":{"map":"function (doc) { emit(doc.color, 1);}","reduce":"function ( keys , values , combine ) {return sum( values )}"}}}' -H 'Content-Type:application/json'
|
||||||
```
|
```
|
||||||
|
|
||||||
Execute a query to retrieve the total number of marbles (reduce function):
|
Execute a query to retrieve the total number of assets (reduce function):
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -X GET http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign/_view/colorview?reduce=true
|
curl -X GET http://127.0.0.1:5990/mychannel_basic/_design/colorviewdesign/_view/colorview?reduce=true
|
||||||
```
|
```
|
||||||
|
|
||||||
If successful, this command will return the number of marbles in the blockchain
|
If successful, this command will return the number of assets in the blockchain
|
||||||
world state, without having to query the blockchain ledger:
|
world state, without having to query the blockchain ledger:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -254,13 +210,13 @@ world state, without having to query the blockchain ledger:
|
||||||
]}
|
]}
|
||||||
```
|
```
|
||||||
|
|
||||||
Execute a new query to retrieve the number of marbles by color (map function):
|
Execute a new query to retrieve the number of assets by color (map function):
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -X GET http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign/_view/colorview?group=true
|
curl -X GET http://127.0.0.1:5990/mychannel_basic/_design/colorviewdesign/_view/colorview?group=true
|
||||||
```
|
```
|
||||||
|
|
||||||
The command will return a list of marbles by color from the CouchDB database.
|
The command will return a list of assets by color from the CouchDB database.
|
||||||
|
|
||||||
```
|
```
|
||||||
{"rows":[
|
{"rows":[
|
||||||
|
|
@ -275,27 +231,27 @@ The command will return a list of marbles by color from the CouchDB database.
|
||||||
|
|
||||||
To run a more complex command that reads through the block history database, we
|
To run a more complex command that reads through the block history database, we
|
||||||
will create an index of the blocknumber, sequence, and key fields. This index
|
will create an index of the blocknumber, sequence, and key fields. This index
|
||||||
will support a query that traces the history of each marble. Execute the
|
will support a query that traces the history of each asset. Execute the
|
||||||
following command to create the index:
|
following command to create the index:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -X POST http://127.0.0.1:5990/mychannel_marbles_history/_index -d '{"index":{"fields":["blocknumber", "sequence", "key"]},"name":"marble_history"}' -H 'Content-Type:application/json'
|
curl -X POST http://127.0.0.1:5990/mychannel_basic_history/_index -d '{"index":{"fields":["blocknumber", "sequence", "key"]},"name":"asset_history"}' -H 'Content-Type:application/json'
|
||||||
```
|
```
|
||||||
|
|
||||||
Now execute a query to retrieve the history for the marble we transferred and
|
Now execute a query to retrieve the history for the asset we transferred and
|
||||||
then deleted:
|
then deleted:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -X POST http://127.0.0.1:5990/mychannel_marbles_history/_find -d '{"selector":{"key":{"$eq":"marble110"}}, "fields":["blocknumber","is_delete","value"],"sort":[{"blocknumber":"asc"}, {"sequence":"asc"}]}' -H 'Content-Type:application/json'
|
curl -X POST http://127.0.0.1:5990/mychannel_basic_history/_find -d '{"selector":{"key":{"$eq":"asset110"}}, "fields":["blocknumber","is_delete","value"],"sort":[{"blocknumber":"asc"}, {"sequence":"asc"}]}' -H 'Content-Type:application/json'
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see the transaction history of the marble that was created,
|
You should see the transaction history of the asset that was created,
|
||||||
transferred, and then removed from the ledger.
|
transferred, and then removed from the ledger.
|
||||||
|
|
||||||
```
|
```
|
||||||
{"docs":[
|
{"docs":[
|
||||||
{"blocknumber":12,"is_delete":false,"value":"{\"docType\":\"marble\",\"name\":\"marble110\",\"color\":\"blue\",\"size\":60,\"owner\":\"debra\"}"},
|
{"blocknumber":12,"is_delete":false,"value":"{\"docType\":\"asset\",\"name\":\"asset110\",\"color\":\"blue\",\"size\":60,\"owner\":\"debra\"}"},
|
||||||
{"blocknumber":22,"is_delete":false,"value":"{\"docType\":\"marble\",\"name\":\"marble110\",\"color\":\"blue\",\"size\":60,\"owner\":\"james\"}"},
|
{"blocknumber":22,"is_delete":false,"value":"{\"docType\":\"asset\",\"name\":\"asset110\",\"color\":\"blue\",\"size\":60,\"owner\":\"james\"}"},
|
||||||
{"blocknumber":23,"is_delete":true,"value":""}
|
{"blocknumber":23,"is_delete":true,"value":""}
|
||||||
]}
|
]}
|
||||||
```
|
```
|
||||||
|
|
@ -309,19 +265,19 @@ have missed.
|
||||||
|
|
||||||
If you ran through the example steps above, navigate back to the terminal window
|
If you ran through the example steps above, navigate back to the terminal window
|
||||||
where `blockEventListener.js` is running and close it. Once the listener is no
|
where `blockEventListener.js` is running and close it. Once the listener is no
|
||||||
longer running, use the following command to add 20 more marbles to the
|
longer running, use the following command to add 20 more assets to the
|
||||||
ledger:
|
ledger:
|
||||||
|
|
||||||
```
|
```
|
||||||
node addMarbles.js
|
node addAssets.js
|
||||||
```
|
```
|
||||||
|
|
||||||
The listener will not be able to add the new marbles to your CouchDB database.
|
The listener will not be able to add the new assets to your CouchDB database.
|
||||||
If you check the current state table using the reduce command, you will only
|
If you check the current state table using the reduce command, you will only
|
||||||
be able to see the original marbles in your database.
|
be able to see the original assets in your database.
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -X GET http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign/_view/colorview?reduce=true
|
curl -X GET http://127.0.0.1:5990/mychannel_basic/_design/colorviewdesign/_view/colorview?reduce=true
|
||||||
```
|
```
|
||||||
|
|
||||||
To add the new data to your off-chain database, remove the `nextblock.txt`
|
To add the new data to your off-chain database, remove the `nextblock.txt`
|
||||||
|
|
@ -337,15 +293,15 @@ You can new re-run the channel listener to read every block from the channel:
|
||||||
node blockEventListener.js
|
node blockEventListener.js
|
||||||
```
|
```
|
||||||
|
|
||||||
This will rebuild the CouchDB tables and include the 20 marbles that have been
|
This will rebuild the CouchDB tables and include the 20 assets that have been
|
||||||
added to the ledger. If you run the reduce command against your database one
|
added to the ledger. If you run the reduce command against your database one
|
||||||
more time,
|
more time,
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -X GET http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign/_view/colorview?reduce=true
|
curl -X GET http://127.0.0.1:5990/mychannel_basic/_design/colorviewdesign/_view/colorview?reduce=true
|
||||||
```
|
```
|
||||||
|
|
||||||
you will be able to see that all of the marbles have been added to your
|
you will be able to see that all of the assets have been added to your
|
||||||
database:
|
database:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -369,4 +325,4 @@ Running the script will complete the following actions:
|
||||||
* Remove the certificates you generated by deleting the `wallet` folder.
|
* Remove the certificates you generated by deleting the `wallet` folder.
|
||||||
* Delete `nextblock.txt` so you can start with the first block next time you
|
* Delete `nextblock.txt` so you can start with the first block next time you
|
||||||
operate the listener.
|
operate the listener.
|
||||||
* Removes `addMarbles.json`.
|
* Removes `addAssets.json`.
|
||||||
|
|
|
||||||
118
off_chain_data/addAssets.js
Normal file
118
off_chain_data/addAssets.js
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* addAssets.js will add random sample data to blockchain.
|
||||||
|
*
|
||||||
|
* $ node addAssets.js
|
||||||
|
*
|
||||||
|
* addAssets will add 10 Assets by default with a starting Asset name of "Asset100".
|
||||||
|
* Additional Assets will be added by incrementing the number at the end of the Asset name.
|
||||||
|
*
|
||||||
|
* The properties for adding Assets are stored in addAssets.json. This file will be created
|
||||||
|
* during the first execution of the utility if it does not exist. The utility can be run
|
||||||
|
* multiple times without changing the properties. The nextAssetNumber will be incremented and
|
||||||
|
* stored in the JSON file.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "nextAssetNumber": 100,
|
||||||
|
* "numberAssetsToAdd": 10
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Wallets, Gateway } = require('fabric-network');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const addAssetsConfigFile = path.resolve(__dirname, 'addAssets.json');
|
||||||
|
|
||||||
|
const colors=[ 'blue', 'red', 'yellow', 'green', 'white', 'purple' ];
|
||||||
|
const owners=[ 'tom', 'fred', 'julie', 'james', 'janet', 'henry', 'alice', 'marie', 'sam', 'debra', 'nancy'];
|
||||||
|
const sizes=[ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ];
|
||||||
|
const appraisedValues=[ 300, 310, 320, 330, 340, 350, 360, 370, 380, 390 ];
|
||||||
|
const docType='asset'
|
||||||
|
|
||||||
|
const config = require('./config.json');
|
||||||
|
const channelid = config.channelid;
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
let nextAssetNumber;
|
||||||
|
let numberAssetsToAdd;
|
||||||
|
let addAssetsConfig;
|
||||||
|
|
||||||
|
// check to see if there is a config json defined
|
||||||
|
if (fs.existsSync(addAssetsConfigFile)) {
|
||||||
|
// read file the next asset and number of assets to create
|
||||||
|
let addAssetsConfigJSON = fs.readFileSync(addAssetsConfigFile, 'utf8');
|
||||||
|
addAssetsConfig = JSON.parse(addAssetsConfigJSON);
|
||||||
|
nextAssetNumber = addAssetsConfig.nextAssetNumber;
|
||||||
|
numberAssetsToAdd = addAssetsConfig.numberAssetsToAdd;
|
||||||
|
} else {
|
||||||
|
nextAssetNumber = 100;
|
||||||
|
numberAssetsToAdd = 20;
|
||||||
|
// create a default config and save
|
||||||
|
addAssetsConfig = new Object;
|
||||||
|
addAssetsConfig.nextAssetNumber = nextAssetNumber;
|
||||||
|
addAssetsConfig.numberAssetsToAdd = numberAssetsToAdd;
|
||||||
|
fs.writeFileSync(addAssetsConfigFile, JSON.stringify(addAssetsConfig, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the connection profile.
|
||||||
|
const ccpPath = path.resolve(__dirname, '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
|
||||||
|
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
|
||||||
|
// Configure a wallet. This wallet must already be primed with an identity that
|
||||||
|
// the application can use to interact with the peer node.
|
||||||
|
const walletPath = path.resolve(__dirname, 'wallet');
|
||||||
|
const wallet = await Wallets.newFileSystemWallet(walletPath);
|
||||||
|
|
||||||
|
// Create a new gateway, and connect to the gateway peer node(s). The identity
|
||||||
|
// specified must already exist in the specified wallet.
|
||||||
|
const gateway = new Gateway();
|
||||||
|
await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
// Get the network channel that the smart contract is deployed to.
|
||||||
|
const network = await gateway.getNetwork(channelid);
|
||||||
|
|
||||||
|
// Get the smart contract from the network channel.
|
||||||
|
const contract = network.getContract('basic');
|
||||||
|
|
||||||
|
for (var counter = nextAssetNumber; counter < nextAssetNumber + numberAssetsToAdd; counter++) {
|
||||||
|
|
||||||
|
var randomColor = Math.floor(Math.random() * (6));
|
||||||
|
var randomOwner = Math.floor(Math.random() * (11));
|
||||||
|
var randomSize = Math.floor(Math.random() * (10));
|
||||||
|
var randomValue = Math.floor(Math.random() * (9));
|
||||||
|
|
||||||
|
// Submit the 'CreateAsset' transaction to the smart contract, and wait for it
|
||||||
|
// to be committed to the ledger.
|
||||||
|
await contract.submitTransaction('CreateAsset', docType+counter, colors[randomColor], ''+sizes[randomSize], owners[randomOwner],appraisedValues[randomValue]);
|
||||||
|
console.log("Adding asset: " + docType + counter + " owner:" + owners[randomOwner] + " color:" + colors[randomColor] + " size:" + '' + sizes[randomSize] + " appraised value:" + '' + appraisedValues[randomValue] );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await gateway.disconnect();
|
||||||
|
|
||||||
|
addAssetsConfig.nextAssetNumber = nextAssetNumber + numberAssetsToAdd;
|
||||||
|
|
||||||
|
fs.writeFileSync(addAssetsConfigFile, JSON.stringify(addAssetsConfig, null, 2));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to submit transaction: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright IBM Corp. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* addMarbles.js will add random sample data to blockchain.
|
|
||||||
*
|
|
||||||
* $ node addMarbles.js
|
|
||||||
*
|
|
||||||
* addMarbles will add 10 marbles by default with a starting marble name of "marble100".
|
|
||||||
* Additional marbles will be added by incrementing the number at the end of the marble name.
|
|
||||||
*
|
|
||||||
* The properties for adding marbles are stored in addMarbles.json. This file will be created
|
|
||||||
* during the first execution of the utility if it does not exist. The utility can be run
|
|
||||||
* multiple times without changing the properties. The nextMarbleNumber will be incremented and
|
|
||||||
* stored in the JSON file.
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* "nextMarbleNumber": 100,
|
|
||||||
* "numberMarblesToAdd": 10
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const { Wallets, Gateway } = require('fabric-network');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const addMarblesConfigFile = path.resolve(__dirname, 'addMarbles.json');
|
|
||||||
|
|
||||||
const colors=[ 'blue', 'red', 'yellow', 'green', 'white', 'purple' ];
|
|
||||||
const owners=[ 'tom', 'fred', 'julie', 'james', 'janet', 'henry', 'alice', 'marie', 'sam', 'debra', 'nancy'];
|
|
||||||
const sizes=[ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ];
|
|
||||||
const docType='marble'
|
|
||||||
|
|
||||||
const config = require('./config.json');
|
|
||||||
const channelid = config.channelid;
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
let nextMarbleNumber;
|
|
||||||
let numberMarblesToAdd;
|
|
||||||
let addMarblesConfig;
|
|
||||||
|
|
||||||
// check to see if there is a config json defined
|
|
||||||
if (fs.existsSync(addMarblesConfigFile)) {
|
|
||||||
// read file the next marble and number of marbles to create
|
|
||||||
let addMarblesConfigJSON = fs.readFileSync(addMarblesConfigFile, 'utf8');
|
|
||||||
addMarblesConfig = JSON.parse(addMarblesConfigJSON);
|
|
||||||
nextMarbleNumber = addMarblesConfig.nextMarbleNumber;
|
|
||||||
numberMarblesToAdd = addMarblesConfig.numberMarblesToAdd;
|
|
||||||
} else {
|
|
||||||
nextMarbleNumber = 100;
|
|
||||||
numberMarblesToAdd = 20;
|
|
||||||
// create a default config and save
|
|
||||||
addMarblesConfig = new Object;
|
|
||||||
addMarblesConfig.nextMarbleNumber = nextMarbleNumber;
|
|
||||||
addMarblesConfig.numberMarblesToAdd = numberMarblesToAdd;
|
|
||||||
fs.writeFileSync(addMarblesConfigFile, JSON.stringify(addMarblesConfig, null, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the connection profile. This would be the path to the file downloaded
|
|
||||||
// from the IBM Blockchain Platform operational console.
|
|
||||||
const ccpPath = path.resolve(__dirname, '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
|
|
||||||
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
|
||||||
|
|
||||||
// Configure a wallet. This wallet must already be primed with an identity that
|
|
||||||
// the application can use to interact with the peer node.
|
|
||||||
const walletPath = path.resolve(__dirname, 'wallet');
|
|
||||||
const wallet = await Wallets.newFileSystemWallet(walletPath);
|
|
||||||
|
|
||||||
// Create a new gateway, and connect to the gateway peer node(s). The identity
|
|
||||||
// specified must already exist in the specified wallet.
|
|
||||||
const gateway = new Gateway();
|
|
||||||
await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } });
|
|
||||||
|
|
||||||
// Get the network channel that the smart contract is deployed to.
|
|
||||||
const network = await gateway.getNetwork(channelid);
|
|
||||||
|
|
||||||
// Get the smart contract from the network channel.
|
|
||||||
const contract = network.getContract('marbles');
|
|
||||||
|
|
||||||
for (var counter = nextMarbleNumber; counter < nextMarbleNumber + numberMarblesToAdd; counter++) {
|
|
||||||
|
|
||||||
var randomColor = Math.floor(Math.random() * (6));
|
|
||||||
var randomOwner = Math.floor(Math.random() * (11));
|
|
||||||
var randomSize = Math.floor(Math.random() * (10));
|
|
||||||
|
|
||||||
// Submit the 'initMarble' transaction to the smart contract, and wait for it
|
|
||||||
// to be committed to the ledger.
|
|
||||||
await contract.submitTransaction('initMarble', docType+counter, colors[randomColor], ''+sizes[randomSize], owners[randomOwner]);
|
|
||||||
console.log("Adding marble: " + docType + counter + " owner:" + owners[randomOwner] + " color:" + colors[randomColor] + " size:" + '' + sizes[randomSize] );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
await gateway.disconnect();
|
|
||||||
|
|
||||||
addMarblesConfig.nextMarbleNumber = nextMarbleNumber + numberMarblesToAdd;
|
|
||||||
|
|
||||||
fs.writeFileSync(addMarblesConfigFile, JSON.stringify(addMarblesConfig, null, 2));
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to submit transaction: ${error}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* deleteMarble.js will delete a specified marble. Example:
|
* deleteAsset.js will delete a specified asset. Example:
|
||||||
*
|
*
|
||||||
* $ node deleteMarble.js marble100
|
* $ node deleteAsset.js asset100
|
||||||
*
|
*
|
||||||
* The utility is meant to demonstrate delete block events.
|
* The utility is meant to demonstrate delete block events.
|
||||||
*/
|
*/
|
||||||
|
|
@ -26,7 +26,7 @@ const channelid = config.channelid;
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|
||||||
if (process.argv[2] == undefined) {
|
if (process.argv[2] == undefined) {
|
||||||
console.log("Usage: node deleteMarble marbleId");
|
console.log("Usage: node deleteAsset AssetId");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,8 +34,7 @@ async function main() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Parse the connection profile. This would be the path to the file downloaded
|
// Parse the connection profile.
|
||||||
// from the IBM Blockchain Platform operational console.
|
|
||||||
const ccpPath = path.resolve(__dirname, '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
|
const ccpPath = path.resolve(__dirname, '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
|
||||||
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
|
||||||
|
|
@ -53,10 +52,10 @@ async function main() {
|
||||||
const network = await gateway.getNetwork(channelid);
|
const network = await gateway.getNetwork(channelid);
|
||||||
|
|
||||||
// Get the smart contract from the network channel.
|
// Get the smart contract from the network channel.
|
||||||
const contract = network.getContract('marbles');
|
const contract = network.getContract('basic');
|
||||||
|
|
||||||
await contract.submitTransaction('delete', deletekey);
|
await contract.submitTransaction('DeleteAsset', deletekey);
|
||||||
console.log("Deleted marble: " + deletekey);
|
console.log("Deleted asset: " + deletekey);
|
||||||
|
|
||||||
await gateway.disconnect();
|
await gateway.disconnect();
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ popd
|
||||||
|
|
||||||
# clean out any old identites in the wallets
|
# clean out any old identites in the wallets
|
||||||
rm -rf wallet
|
rm -rf wallet
|
||||||
rm -rf addMarbles.json mychannel_marbles.log mychannel__lifecycle.log nextblock.txt
|
rm -rf addAssets.json mychannel_basic.log mychannel__lifecycle.log nextblock.txt
|
||||||
|
|
||||||
docker stop offchaindb
|
docker stop offchaindb
|
||||||
docker rm offchaindb
|
docker rm offchaindb
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
"author": "Hyperledger",
|
"author": "Hyperledger",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fabric-ca-client": "^2.1.0",
|
"fabric-ca-client": "^2.2.0",
|
||||||
"fabric-network": "^2.1.0"
|
"fabric-network": "^2.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
|
|
|
||||||
|
|
@ -7,161 +7,13 @@
|
||||||
# Exit on first error
|
# Exit on first error
|
||||||
set -e pipefail
|
set -e pipefail
|
||||||
|
|
||||||
# don't rewrite paths for Windows Git Bash users
|
|
||||||
export MSYS_NO_PATHCONV=1
|
|
||||||
starttime=$(date +%s)
|
starttime=$(date +%s)
|
||||||
CC_SRC_LANGUAGE=golang
|
|
||||||
CC_RUNTIME_LANGUAGE=golang
|
|
||||||
CC_SRC_PATH=../chaincode/marbles02/go
|
|
||||||
|
|
||||||
echo Vendoring Go dependencies ...
|
|
||||||
pushd ../chaincode/marbles02/go
|
|
||||||
GO111MODULE=on go mod vendor
|
|
||||||
popd
|
|
||||||
echo Finished vendoring Go dependencies
|
|
||||||
|
|
||||||
# launch network; create channel and join peer to channel
|
# launch network; create channel and join peer to channel
|
||||||
pushd ../test-network
|
pushd ../test-network
|
||||||
./network.sh down
|
./network.sh down
|
||||||
./network.sh up createChannel -ca -s couchdb
|
./network.sh up createChannel -ca -s couchdb
|
||||||
|
./network.sh deployCC
|
||||||
export PATH=${PWD}/../bin:${PWD}:$PATH
|
|
||||||
export FABRIC_CFG_PATH=${PWD}/../config
|
|
||||||
|
|
||||||
# import environment variables
|
|
||||||
. scripts/envVar.sh
|
|
||||||
|
|
||||||
echo "Packaging the marbles smart contract"
|
|
||||||
|
|
||||||
setGlobals 1
|
|
||||||
|
|
||||||
peer lifecycle chaincode package marbles.tar.gz \
|
|
||||||
--path $CC_SRC_PATH \
|
|
||||||
--lang $CC_RUNTIME_LANGUAGE \
|
|
||||||
--label marblesv1
|
|
||||||
|
|
||||||
echo "Installing smart contract on peer0.org1.example.com"
|
|
||||||
|
|
||||||
peer lifecycle chaincode install marbles.tar.gz
|
|
||||||
|
|
||||||
|
|
||||||
echo "Installing smart contract on peer0.org2.example.com"
|
|
||||||
|
|
||||||
setGlobals 2
|
|
||||||
|
|
||||||
peer lifecycle chaincode install marbles.tar.gz
|
|
||||||
|
|
||||||
|
|
||||||
echo "Query the chaincode package id"
|
|
||||||
|
|
||||||
setGlobals 1
|
|
||||||
|
|
||||||
peer lifecycle chaincode queryinstalled >&log.txt
|
|
||||||
|
|
||||||
PACKAGE_ID=$(sed -n "/marblesv1/{s/^Package ID: //; s/, Label:.*$//; p;}" log.txt)
|
|
||||||
|
|
||||||
echo "Approving the chaincode definition for org1.example.com"
|
|
||||||
|
|
||||||
peer lifecycle chaincode approveformyorg \
|
|
||||||
-o localhost:7050 \
|
|
||||||
--ordererTLSHostnameOverride orderer.example.com \
|
|
||||||
--channelID mychannel \
|
|
||||||
--name marbles \
|
|
||||||
--version 1.0 \
|
|
||||||
--init-required \
|
|
||||||
--signature-policy AND"('Org1MSP.member','Org2MSP.member')" \
|
|
||||||
--sequence 1 \
|
|
||||||
--package-id $PACKAGE_ID \
|
|
||||||
--tls \
|
|
||||||
--cafile ${ORDERER_CA}
|
|
||||||
|
|
||||||
echo "Approving the chaincode definition for org2.example.com"
|
|
||||||
|
|
||||||
setGlobals 2
|
|
||||||
|
|
||||||
peer lifecycle chaincode approveformyorg \
|
|
||||||
-o localhost:7050 \
|
|
||||||
--ordererTLSHostnameOverride orderer.example.com \
|
|
||||||
--channelID mychannel \
|
|
||||||
--name marbles \
|
|
||||||
--version 1.0 \
|
|
||||||
--init-required \
|
|
||||||
--signature-policy AND"('Org1MSP.member','Org2MSP.member')" \
|
|
||||||
--sequence 1 \
|
|
||||||
--package-id $PACKAGE_ID \
|
|
||||||
--tls \
|
|
||||||
--cafile ${ORDERER_CA}
|
|
||||||
|
|
||||||
echo "Checking if the chaincode definition is ready to commit"
|
|
||||||
|
|
||||||
peer lifecycle chaincode checkcommitreadiness \
|
|
||||||
--channelID mychannel \
|
|
||||||
--name marbles \
|
|
||||||
--version 1.0 \
|
|
||||||
--sequence 1 \
|
|
||||||
--output json \
|
|
||||||
--init-required \
|
|
||||||
--signature-policy AND"('Org1MSP.member','Org2MSP.member')" >&log.txt
|
|
||||||
|
|
||||||
rc=0
|
|
||||||
for var in "\"Org1MSP\": true" "\"Org2MSP\": true"
|
|
||||||
do
|
|
||||||
grep "$var" log.txt &>/dev/null || let rc=1
|
|
||||||
done
|
|
||||||
|
|
||||||
if test $rc -eq 0; then
|
|
||||||
echo "Chaincode definition is ready to commit"
|
|
||||||
else
|
|
||||||
sleep 10
|
|
||||||
fi
|
|
||||||
|
|
||||||
parsePeerConnectionParameters 1 2
|
|
||||||
|
|
||||||
echo "Commit the chaincode definition to the channel"
|
|
||||||
|
|
||||||
peer lifecycle chaincode commit \
|
|
||||||
-o localhost:7050 \
|
|
||||||
--ordererTLSHostnameOverride orderer.example.com \
|
|
||||||
--channelID mychannel \
|
|
||||||
--name marbles \
|
|
||||||
--version 1.0 \
|
|
||||||
--init-required \
|
|
||||||
--signature-policy AND"('Org1MSP.member','Org2MSP.member')" \
|
|
||||||
--sequence 1 \
|
|
||||||
--tls \
|
|
||||||
--cafile ${ORDERER_CA} \
|
|
||||||
$PEER_CONN_PARMS
|
|
||||||
|
|
||||||
echo "Check if the chaincode has been committed to the channel ..."
|
|
||||||
|
|
||||||
peer lifecycle chaincode querycommitted \
|
|
||||||
--channelID mychannel \
|
|
||||||
--name marbles >&log.txt
|
|
||||||
|
|
||||||
EXPECTED_RESULT="Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc"
|
|
||||||
VALUE=$(grep -o "Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc" log.txt)
|
|
||||||
echo "$VALUE"
|
|
||||||
|
|
||||||
if [ "$VALUE" = "Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc" ] ; then
|
|
||||||
echo "chaincode has been committed"
|
|
||||||
else
|
|
||||||
sleep 10
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "invoke the marbles chaincode init function ... "
|
|
||||||
|
|
||||||
peer chaincode invoke \
|
|
||||||
-o localhost:7050 \
|
|
||||||
--ordererTLSHostnameOverride orderer.example.com \
|
|
||||||
-C mychannel \
|
|
||||||
-n marbles \
|
|
||||||
--isInit \
|
|
||||||
-c '{"Args":["Init"]}' \
|
|
||||||
--tls \
|
|
||||||
--cafile ${ORDERER_CA} \
|
|
||||||
$PEER_CONN_PARMS
|
|
||||||
|
|
||||||
rm log.txt
|
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tranferMarble.js will transfer ownership a specified marble to a new ownder. Example:
|
* tranferAsset.js will transfer ownership a specified asset to a new ownder. Example:
|
||||||
*
|
*
|
||||||
* $ node transferMarble.js marble102 jimmy
|
* $ node transferAsset.js asset102 jimmy
|
||||||
*
|
*
|
||||||
* The utility is meant to demonstrate update block events.
|
* The utility is meant to demonstrate update block events.
|
||||||
*/
|
*/
|
||||||
|
|
@ -25,7 +25,7 @@ const channelid = config.channelid;
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|
||||||
if (process.argv[2] == undefined && process.argv[3] == undefined) {
|
if (process.argv[2] == undefined && process.argv[3] == undefined) {
|
||||||
console.log("Usage: node changeMarbleOwner.js marbleId owner");
|
console.log("Usage: node transferAsset.js assetId owner");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,8 +34,7 @@ async function main() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Parse the connection profile. This would be the path to the file downloaded
|
// Parse the connection profile.
|
||||||
// from the IBM Blockchain Platform operational console.
|
|
||||||
const ccpPath = path.resolve(__dirname, '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
|
const ccpPath = path.resolve(__dirname, '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
|
||||||
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
|
||||||
|
|
@ -53,10 +52,10 @@ async function main() {
|
||||||
const network = await gateway.getNetwork(channelid);
|
const network = await gateway.getNetwork(channelid);
|
||||||
|
|
||||||
// Get the smart contract from the network channel.
|
// Get the smart contract from the network channel.
|
||||||
const contract = network.getContract('marbles');
|
const contract = network.getContract('basic');
|
||||||
|
|
||||||
await contract.submitTransaction('transferMarble', updatekey, newowner);
|
await contract.submitTransaction('TransferAsset', updatekey, newowner);
|
||||||
console.log("Transferred marble " + updatekey + " to " + newowner);
|
console.log("Transferred asset " + updatekey + " to " + newowner);
|
||||||
|
|
||||||
await gateway.disconnect();
|
await gateway.disconnect();
|
||||||
|
|
||||||
Loading…
Reference in a new issue