Off Chain data

离链数据

This sample demonstrates how you can use Peer channel-based event services
to replicate the data on your blockchain network to an off chain database.
Using an off chain database allows you to analyze the data from your network or
build a dashboard without degrading the performance of your application.

该示例演示了如何使用[基于peer的事件服务](https://hyperledger-fabric.readthedocs.io/en/release-1.4/peer_event_services.html)
将您的区块链网络上的数据复制到链下数据库。
使用链下数据库可让您分析来自网络或网络的数据
构建仪表板而不会降低应用程序的性能。

This sample uses the Fabric network event listener from the Node.JS Fabric SDK to write data to local instance of
CouchDB.

此示例使用Node.JS Fabric SDK中的[Fabric网络事件监听器](https://fabric-sdk-node.github.io/release-1.4/tutorial-listening-to-events.html)将数据写入 的本地实例
CouchDB。

Getting started

入门

This sample uses Node Fabric SDK application code similar to the fabcar sample
to connect to a network created using the first-network sample.

本示例使用类似于fabcar示例的Node Fabric SDK应用程序代码
连接到使用“ first-network”示例创建的网络。

Install dependencies

安装依赖

You need to install Node.js version 8.9.x to use the sample application code.
Execute the following commands to install the required dependencies:

您需要安装Node.js版本8.9.x才能使用示例应用程序代码。
执行以下命令以安装必需的依赖项:

1
2
cd fabric-samples/off_chain_data
npm install

Configuration

配置

The configuration for the listener is stored in the config.json file:

被侦听的配置存储在config.json文件中:

1
2
3
4
5
6
7
{
"peer_name": "peer0.org1.example.com",
"channelid": "mychannel",
"use_couchdb":true,
"create_history_log":true,
"couchdb_address": "http://localhost:5990"
}

peer_name: is the target peer for the listener.
channelid: is the channel name for block events.
use_couchdb: If set to true, events will be stored in a local instance of
CouchDB. If set to false, only a local log of events will be stored.
create_history_log: If true, a local log file will be created with all of the
block changes.
couchdb_address: is the local address for an off chain CouchDB database.

Create an instance of CouchDB

创建一个CouchDB实例

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:

如果您在config.json中将“ use_couchdb”选项设置为true,则可以运行
以下命令使用docker启动CouchDB的本地实例:

1
2
docker run --publish 5990:5984 --detach --name offchaindb hyperledger/fabric-couchdb
docker start offchaindb

Starting the Network

启动网络

Use the following command to start the sample network:

使用以下命令启动示例网络:

1
./startFabric.sh

This command uses the first-network sample to deploy a fabric network with an
ordering service, two peer organizations with two peers each, and a channel
named mychannel. The marbles chaincode will be installed on all four peers and
instantiated on the channel.

该命令使用“ first-network”示例来部署具有以下功能的光纤网络:
订购服务,两个对等组织(每个都有两个peer)和一个渠道
名为“ mychannel”。 大理石链代码将安装在所有四个peer上,并且
在通道上实例化。

Starting the Channel Event Listener

启动频道事件监听器

Once the network has started, we can use the Node.js SDK to create the user and
certificates our listener application will use to interact with the network. Run
the following command to enroll the admin user:

网络启动后,我们可以使用Node.js SDK创建用户并
我们的侦听器应用程序将用于与网络交互的证书。 跑
以下命令来注册管理员用户:

1
node enrollAdmin.js

You can then run the following command to register and enroll an application
user:

然后,您可以运行以下命令来注册和注册应用程序
用户:

1
node registerUser.js

We can then use our application user to start the block event listener:

然后,我们可以使用我们的应用程序用户来启动块事件监听器:

1
node blockEventListener.js

If the command is successful, you should see the output of the listener reading
the first 4 configuration blocks of mychannel:

如果命令成功执行,您应该看到监听器读取的输出
mychannel的前4个配置块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Added block 0 to ProcessingMap
Added block 1 to ProcessingMap
Added block 2 to ProcessingMap
Added block 3 to ProcessingMap
------------------------------------------------
Block Number: 0
------------------------------------------------
Block Number: 1
------------------------------------------------
Block Number: 2
------------------------------------------------
Block Number: 3
Added block 4 to ProcessingMap
------------------------------------------------
Block Number: 4

blockEventListener.js creates a listener named “offchain-listener” on the
channel mychannel. The listener writes each block added to the channel to a
processing map called BlockMap for temporary storage and ordering purposes.
blockEventListener.js uses nextblock.txt to keep track of the latest block
that was retrieved by the listener. The block number in nextblock.txt may be
set to a previous block number in order to replay previous blocks. The file
may also be deleted and all blocks will be replayed when the block listener is
started.

blockEventListener.js在目录上创建一个名为“ offchain-listener”的监听器。
频道“ mychannel”。 侦听器将添加到通道的每个块写入到
处理称为BlockMap的映射以用于临时存储和订购。
blockEventListener.js使用nextblock.txt来跟踪最新的块
由侦听器检索到的。 “ nextblock.txt”中的块号可能是
设置为先前的块号以重播先前的块。 文件
可能还会被删除,并且当块侦听器处于
开始。

BlockProcessing.js runs as a daemon and pulls each block in order from the
BlockMap. It then uses the read-write set of that block to extract the latest
key value data and store it in the database. The first four configuration blocks
of mychannel did not any data to the database because the blocks did not
contain a read-write set.

BlockProcessing.js作为守护进程运行,并从
方块图。 然后,它使用该块的读写集来提取最新的
键值数据并将其存储在数据库中。 前四个配置块
mychannel的数据没有任何数据到数据库,因为块没有
包含一个读写集。

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
a file named mychannel_marbles.log. This allows you to record a history of
changes made by each block for each key in addition to storing the latest value
of the world state.

通道事件侦听器还将每个块的元数据写入日志文件
定义为channelid_chaincodeid.log。 在此示例中,事件将被写入
一个名为“ mychannel_marbles.log”的文件。 这使您可以记录历史
除存储最新值外,每个块对每个键所做的更改
世界国家。

Note: Leave the blockEventListener.js running in a terminal window. Open a
new window to execute the next parts of the demo.

注意:让blockEventListener.js在终端窗口中运行。 打开一个
新窗口执行演示的下一部分。

Generate data on the blockchain

在区块链上生成数据

Now that our listener is setup, we can generate data using the marbles chaincode
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.

You can use the addMarbles.js file to add random sample data to blockchain.
The file uses the configuration information stored in addMarbles.json to
create a series of marbles. This file will be created during the first execution
of addMarbles.js if it does not exist. This program can be run multiple times
without changing the properties. The nextMarbleNumber will be incremented and
stored in the addMarbles.json file.

1
2
3
4
{
"nextMarbleNumber": 100,
"numberMarblesToAdd": 20
}

Open a new window and run the following command to add 20 marbles to the
blockchain:

1
node addMarbles.js

After the marbles have been added to the ledger, use the following command to
transfer one of the marbles to a new owner:

1
node transferMarble.js marble110 james

Now run the following command to delete the marble that was transferred:

1
node deleteMarble.js marble110

Offchain CouchDB storage:

脱链CouchDB存储:

If you followed the instructions above and set use_couchdb to true,
blockEventListener.js will create two tables in the local instance of CouchDB.
blockEventListener.js is written to create two tables for each channel and for
each chaincode.

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
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
after the channelid and chaincodeid, and is named mychannel_marbles in this
example. You can navigate to this table using your browser:
http://127.0.0.1:5990/mychannel_marbles/_all_docs

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
history to the name of the first table, and is named mychannel_marbles_history
in this example. You can also navigate to this table using your browser:
http://127.0.0.1:5990/mychannel_marbles_history/_all_docs

Configure a map/reduce view for summarizing counts of marbles by color:

配置地图/缩小视图以按颜色汇总大理石的数量:

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
index to support a more complex query. Note that if the blockEventListener.js
is not running, the database commands below may fail since the database is only
created when events are received.

Open a new terminal window and execute the following:

1
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'

Execute a query to retrieve the total number of marbles (reduce function):

1
curl -X GET http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign/_view/colorview?reduce=true

If successful, this command will return the number of marbles in the blockchain
world state, without having to query the blockchain ledger:

1
2
3
{"rows":[
{"key":null,"value":19}
]}

Execute a new query to retrieve the number of marbles by color (map function):

1
curl -X GET http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign/_view/colorview?group=true

The command will return a list of marbles by color from the CouchDB database.

1
2
3
4
5
6
7
8
{"rows":[
{"key":"blue","value":2},
{"key":"green","value":2},
{"key":"purple","value":3},
{"key":"red","value":4},
{"key":"white","value":6},
{"key":"yellow","value":2}
]}

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 support a query that traces the history of each marble. Execute the
following command to create the index:

1
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'

Now execute a query to retrieve the history for the marble we transferred and
then deleted:

1
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'

You should see the transaction history of the marble that was created,
transferred, and then removed from the ledger.

1
2
3
4
5
{"docs":[
{"blocknumber":12,"is_delete":false,"value":"{\"docType\":\"marble\",\"name\":\"marble110\",\"color\":\"blue\",\"size\":60,\"owner\":\"debra\"}"},
{"blocknumber":22,"is_delete":false,"value":"{\"docType\":\"marble\",\"name\":\"marble110\",\"color\":\"blue\",\"size\":60,\"owner\":\"james\"}"},
{"blocknumber":23,"is_delete":true,"value":""}
]}

Getting historical data from the network

从网络获取历史数据

You can also use the blockEventListener.js program to retrieve historical data
from your network. This allows you to create a database that is up to date with
the latest data from the network or recover any blocks that the program may
have missed.

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
longer running, use the following command to add 20 more marbles to the
ledger:

1
node addMarbles.js

The listener will not be able to add the new marbles to your CouchDB database.
If you check the current state table using the reduce command, you will only
be able to see the original marbles in your database.

1
curl -X GET http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign/_view/colorview?reduce=true

To add the new data to your off-chain database, remove the nextblock.txt
file that kept track of the latest block read by blockEventListener.js:

1
rm nextblock.txt

You can new re-run the channel listener to read every block from the channel:

1
node blockEventListener.js

This will rebuild the CouchDB tables and include the 20 marbles that have been
added to the ledger. If you run the reduce command against your database one
more time,

1
curl -X GET http://127.0.0.1:5990/mychannel_marbles/_design/colorviewdesign/_view/colorview?reduce=true

you will be able to see that all of the marbles have been added to your
database:

1
2
3
{"rows":[
{"key":null,"value":39}
]}

Clean up

If you are finished using the sample application, you can bring down the network
and any accompanying artifacts.

  • Change to fabric-samples/first-network directory.
  • To stop the network, run ./byfn.sh down.
  • Change back to fabric-samples/off_chain_data directory.
  • Remove the certificates you generated by deleting the wallet folder.
  • Delete nextblock.txt so you can start with the first block next time you
    operate the listener. You can also reset the nextMarbleNumber in
    addMarbles.json to 100.
  • To take down the local CouchDB database, first stop and then remove the
    docker container:
    1
    2
    docker stop offchaindb
    docker rm offchaindb