Hyperledger Fabric v1.4(LTS) 系列(6.3):Building Your First Network

Hyperledger Fabric v1.4(LTS) 系列(6.3):Building Your First Network

Building Your First Network

… note:: These instructions have been verified to work against the
latest stable Docker images and the pre-compiled
setup utilities within the supplied tar file. If you run
these commands with images or tools from the current master
branch, it is possible that you will see configuration and panic
errors.

The build your first network (BYFN) scenario provisions a sample Hyperledger
Fabric network consisting of two organizations, each maintaining two peer
nodes. It also will deploy a “Solo” ordering service by default, though other
ordering service implementations are available.
BYFN场景创建了包含两个组织的Fabric网络,每个组织两个节点。同时还会部署一个Solo
排序服务。

Install prerequisites

参考prereqs文档安装依赖,在first-network目录执行包括byfn.sh等命令。

Before we begin, if you haven’t already done so, you may wish to check that
you have all the :doc:prereqs installed on the platform(s) on which you’ll be
developing blockchain applications and/or operating Hyperledger Fabric.

You will also need to :doc:install. You will notice that there are a number of
samples included in the fabric-samples repository. We will be using the
first-network sample. Let’s open that sub-directory now.

… code:: bash

cd fabric-samples/first-network

… note:: The supplied commands in this documentation MUST be run from your
first-network sub-directory of the fabric-samples repository
clone. If you elect to run the commands from a different location,
the various provided scripts will be unable to find the binaries.

Want to run it now?

byfn.sh命令有详细注释,一般来说,建立第一个网络的步骤可以简化到byfn.sh generate 和byfn.sh up两条。 可以用byfn.sh -l node/-l java来指定chaincode的语言。 -o指定共识协议。

We provide a fully annotated script — byfn.sh — that leverages these Docker
images to quickly bootstrap a Hyperledger Fabric network that by default is
comprised of four peers representing two different organizations, and an orderer
node. It will also launch a container to run a scripted execution that will join
peers to a channel, deploy a chaincode and drive execution of transactions
against the deployed chaincode.

Here’s the help text for the byfn.sh script:

… code:: bash

Usage:
byfn.sh [-c ] [-t ] [-d ] [-f ] [-s ] [-l ] [-o ] [-i ] [-v]"
- one of ‘up’, ‘down’, ‘restart’, ‘generate’ or ‘upgrade’"
- ‘up’ - bring up the network with docker-compose up"
- ‘down’ - clear the network with docker-compose down"
- ‘restart’ - restart the network"
- ‘generate’ - generate required certificates and genesis block"
- ‘upgrade’ - upgrade the network from version 1.3.x to 1.4.0"
-c - channel name to use (defaults to “mychannel”)"
-t - CLI timeout duration in seconds (defaults to 10)"
-d - delay duration in seconds (defaults to 3)"
-f - specify which docker-compose file use (defaults to docker-compose-cli.yaml)"
-s - the database backend to use: goleveldb (default) or couchdb"
-l - the chaincode language: golang (default), node, or java"
-o - the consensus-type of the ordering service: solo (default), kafka, or etcdraft"
-i - the tag to be used to launch the network (defaults to “latest”)"
-v - verbose mode"
byfn.sh -h (print this message)"

Typically, one would first generate the required certificates and
genesis block, then bring up the network. e.g.:"

byfn.sh generate -c mychannel"
byfn.sh up -c mychannel -s couchdb"
byfn.sh up -c mychannel -s couchdb -i 1.4.0"
byfn.sh up -l node"
byfn.sh down -c mychannel"
byfn.sh upgrade -c mychannel"

Taking all defaults:"
byfn.sh generate"
byfn.sh up"
byfn.sh down"

If you choose not to supply a flag, the script will use default values.

Generate Network Artifacts
^^^^^^^^^^^^^^^^^^^^^^^^^^

Ready to give it a go? Okay then! Execute the following command:

… code:: bash

./byfn.sh generate

You will see a brief description as to what will occur, along with a yes/no command line
prompt. Respond with a y or hit the return key to execute the described action.

… code:: bash

Generating certs and genesis block for channel ‘mychannel’ with CLI timeout of ‘10’ seconds and CLI delay of ‘3’ seconds
Continue? [Y/n] y
proceeding …
/Users/xxx/dev/fabric-samples/bin/cryptogen

##########################################################

Generate certificates using cryptogen tool

##########################################################
org1.example.com
2017-06-12 21:01:37.334 EDT [bccsp] GetDefault -> WARN 001 Before using BCCSP, please call InitFactories(). Falling back to bootBCCSP.

/Users/xxx/dev/fabric-samples/bin/configtxgen
##########################################################
######### Generating Orderer Genesis block ##############
##########################################################
2017-06-12 21:01:37.558 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.562 EDT [msp] getMspConfig -> INFO 002 intermediate certs folder not found at [/Users/xxx/dev/byfn/crypto-config/ordererOrganizations/example.com/msp/intermediatecerts]. Skipping.: [stat /Users/xxx/dev/byfn/crypto-config/ordererOrganizations/example.com/msp/intermediatecerts: no such file or directory]

2017-06-12 21:01:37.588 EDT [common/configtx/tool] doOutputBlock -> INFO 00b Generating genesis block
2017-06-12 21:01:37.590 EDT [common/configtx/tool] doOutputBlock -> INFO 00c Writing genesis block

#################################################################

Generating channel configuration transaction ‘channel.tx’

#################################################################
2017-06-12 21:01:37.634 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.644 EDT [common/configtx/tool] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx
2017-06-12 21:01:37.645 EDT [common/configtx/tool] doOutputChannelCreateTx -> INFO 003 Writing new channel tx

#################################################################
####### Generating anchor peer update for Org1MSP ##########
#################################################################
2017-06-12 21:01:37.674 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.678 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2017-06-12 21:01:37.679 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update

#################################################################
####### Generating anchor peer update for Org2MSP ##########
#################################################################
2017-06-12 21:01:37.700 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.704 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2017-06-12 21:01:37.704 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update

This first step generates all of the certificates and keys for our various
network entities, the genesis block used to bootstrap the ordering service,
and a collection of configuration transactions required to configure a
:ref:Channel.

Bring Up the Network
^^^^^^^^^^^^^^^^^^^^

Next, you can bring the network up with one of the following commands:

… code:: bash

./byfn.sh up

The above command will compile Golang chaincode images and spin up the corresponding
containers. Go is the default chaincode language, however there is also support
for Node.js _ and Java _
chaincode. If you’d like to run through this tutorial with node chaincode, pass
the following command instead:

… code:: bash

we use the -l flag to specify the chaincode language forgoing the -l flag will default to Golang

./byfn.sh up -l node

… note:: For more information on the Node.js shim, please refer to its
documentation _.

… note:: For more information on the Java shim, please refer to its
documentation _.

Тo make the sample run with Java chaincode, you have to specify -l java as follows:

… code:: bash

./byfn.sh up -l java

… note:: Do not run both of these commands. Only one language can be tried unless
you bring down and recreate the network between.

In addition to support for multiple chaincode languages, you can also issue a
flag that will bring up a five node Raft ordering service or a Kafka ordering
service instead of the one node Solo orderer. For more information about the
currently supported ordering service implementations, check out :doc:orderer/ordering_service.

To bring up the network with a Raft ordering service, issue:

… code:: bash

./byfn.sh up -o etcdraft

To bring up the network with a Kafka ordering service, issue:

… code:: bash

./byfn.sh up -o kafka

Once again, you will be prompted as to whether you wish to continue or abort.
Respond with a y or hit the return key:

… code:: bash

Starting for channel ‘mychannel’ with CLI timeout of ‘10’ seconds and CLI delay of ‘3’ seconds
Continue? [Y/n]
proceeding …
Creating network “net_byfn” with the default driver
Creating peer0.org1.example.com
Creating peer1.org1.example.com
Creating peer0.org2.example.com
Creating orderer.example.com
Creating peer1.org2.example.com
Creating cli


/ | | | / \ | _ \ | |
_
\ | | / _ \ | |
) | | |
) | | | / ___ \ | _ < | |
|
/ || // _\ || _\ |_|

Channel name : mychannel
Creating channel…

The logs will continue from there. This will launch all of the containers, and
then drive a complete end-to-end application scenario. Upon successful
completion, it should report the following in your terminal window:

… code:: bash

Query Result: 90
2017-05-16 17:08:15.158 UTC [main] main -> INFO 008 Exiting.....
===================== Query successful on peer1.org2 on channel 'mychannel' =====================

===================== All GOOD, BYFN execution completed =====================


 _____   _   _   ____
| ____| | \ | | |  _ \
|  _|   |  \| | | | | |
| |___  | |\  | | |_| |
|_____| |_| \_| |____/

You can scroll through these logs to see the various transactions. If you don’t
get this result, then jump down to the :ref:Troubleshoot section and let’s see
whether we can help you discover what went wrong.

Bring Down the Network
^^^^^^^^^^^^^^^^^^^^^^

Finally, let’s bring it all down so we can explore the network setup one step
at a time. The following will kill your containers, remove the crypto material
and four artifacts, and delete the chaincode images from your Docker Registry:

… code:: bash

./byfn.sh down

Once again, you will be prompted to continue, respond with a y or hit the return key:

… code:: bash

Stopping with channel ‘mychannel’ and CLI timeout of ‘10’
Continue? [Y/n] y
proceeding …
WARNING: The CHANNEL_NAME variable is not set. Defaulting to a blank string.
WARNING: The TIMEOUT variable is not set. Defaulting to a blank string.
Removing network net_byfn
468aaa6201ed

Untagged: dev-peer1.org2.example.com-mycc-1.0:latest
Deleted: sha256:ed3230614e64e1c83e510c0c282e982d2b06d148b1c498bbdcc429e2b2531e91

If you’d like to learn more about the underlying tooling and bootstrap mechanics,
continue reading. In these next sections we’ll walk through the various steps
and requirements to build a fully-functional Hyperledger Fabric network.

… note:: The manual steps outlined below assume that the FABRIC_LOGGING_SPEC in
the cli container is set to DEBUG. You can set this by modifying
the docker-compose-cli.yaml file in the first-network directory.
e.g.

      .. code::

        cli:
          container_name: cli
          image: hyperledger/fabric-tools:$IMAGE_TAG
          tty: true
          stdin_open: true
          environment:
            - GOPATH=/opt/gopath
            - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
            - FABRIC_LOGGING_SPEC=DEBUG
            #- FABRIC_LOGGING_SPEC=INFO

Crypto Generator

证书的生成和管理是Fabric网络管理的重点,这里使用cyrptogen来生成证书,它使用cyrpto-config.yaml来配置。

We will use the cryptogen tool to generate the cryptographic material
(x509 certs and signing keys) for our various network entities. These certificates are
representative of identities, and they allow for sign/verify authentication to
take place as our entities communicate and transact.

How does it work?
^^^^^^^^^^^^^^^^^

Cryptogen consumes a file — crypto-config.yaml — that contains the network
topology and allows us to generate a set of certificates and keys for both the
Organizations and the components that belong to those Organizations. Each
Organization is provisioned a unique root certificate (ca-cert) that binds
specific components (peers and orderers) to that Org. By assigning each
Organization a unique CA certificate, we are mimicking a typical network where
a participating :ref:Member would use its own Certificate Authority.
Transactions and communications within Hyperledger Fabric are signed by an
entity’s private key (keystore), and then verified by means of a public
key (signcerts).

You will notice a count variable within this file. We use this to specify
the number of peers per Organization; in our case there are two peers per Org.
We won’t delve into the minutiae of x.509 certificates and public key infrastructure _
right now. If you’re interested, you can peruse these topics on your own time.

After we run the cryptogen tool, the generated certificates and keys will be
saved to a folder titled crypto-config. Note that the crypto-config.yaml
file lists five orderers as being tied to the orderer organization. While the
cryptogen tool will create certificates for all five of these orderers, unless
the Raft or Kafka ordering services are being used, only one of these orderers
will be used in a Solo ordering service implementation and be used to create the
system channel and mychannel.

Configuration Transaction Generator

The configtxgen tool is used to create four configuration artifacts:

  • orderer genesis block,
  • channel configuration transaction,
  • and two anchor peer transactions - one for each Peer Org.

Please see :doc:commands/configtxgen for a complete description of this tool’s functionality.

The orderer block is the :ref:Genesis-Block for the ordering service, and the
channel configuration transaction file is broadcast to the orderer at :ref:Channel creation
time. The anchor peer transactions, as the name might suggest, specify each
Org’s :ref:Anchor-Peer on this channel.

How does it work?
^^^^^^^^^^^^^^^^^

Configtxgen consumes a file - configtx.yaml - that contains the definitions
for the sample network. There are three members - one Orderer Org (OrdererOrg)
and two Peer Orgs (Org1 & Org2) each managing and maintaining two peer nodes.
This file also specifies a consortium - SampleConsortium - consisting of our
two Peer Orgs. Pay specific attention to the “Profiles” section at the bottom of
this file. You will notice that we have several unique profiles. A few are worth
noting:

  • TwoOrgsOrdererGenesis: generates the genesis block for a Solo ordering
    service.

  • SampleMultiNodeEtcdRaft: generates the genesis block for a Raft ordering
    service. Only used if you issue the -o flag and specify etcdraft.

  • SampleDevModeKafka: generates the genesis block for a Kafka ordering
    service. Only used if you issue the -o flag and specify kafka.

  • TwoOrgsChannel: generates the genesis block for our channel, mychannel.

These headers are important, as we will pass them in as arguments when we create
our artifacts.

… note:: Notice that our SampleConsortium is defined in
the system-level profile and then referenced by
our channel-level profile. Channels exist within
the purview of a consortium, and all consortia
must be defined in the scope of the network at
large.

This file also contains two additional specifications that are worth
noting. Firstly, we specify the anchor peers for each Peer Org
(peer0.org1.example.com & peer0.org2.example.com). Secondly, we point to
the location of the MSP directory for each member, in turn allowing us to store the
root certificates for each Org in the orderer genesis block. This is a critical
concept. Now any network entity communicating with the ordering service can have
its digital signature verified.

Run the tools

You can manually generate the certificates/keys and the various configuration
artifacts using the configtxgen and cryptogen commands. Alternately,
you could try to adapt the byfn.sh script to accomplish your objectives.

Manually generate the artifacts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can refer to the generateCerts function in the byfn.sh script for the
commands necessary to generate the certificates that will be used for your
network configuration as defined in the crypto-config.yaml file. However,
for the sake of convenience, we will also provide a reference here.

First let’s run the cryptogen tool. Our binary is in the bin
directory, so we need to provide the relative path to where the tool resides.

… code:: bash

../bin/cryptogen generate --config=./crypto-config.yaml

You should see the following in your terminal:

… code:: bash

org1.example.com
org2.example.com

The certs and keys (i.e. the MSP material) will be output into a directory - crypto-config -
at the root of the first-network directory.

Next, we need to tell the configtxgen tool where to look for the
configtx.yaml file that it needs to ingest. We will tell it look in our
present working directory:

… code:: bash

export FABRIC_CFG_PATH=$PWD

Then, we’ll invoke the configtxgen tool to create the orderer genesis block:

… code:: bash

../bin/configtxgen -profile TwoOrgsOrdererGenesis -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

To output a genesis block for a Raft ordering service, this command should be:

… code:: bash

…/bin/configtxgen -profile SampleMultiNodeEtcdRaft -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

Note the SampleMultiNodeEtcdRaft profile being used here.

To output a genesis block for a Kafka ordering service, issue:

… code:: bash

…/bin/configtxgen -profile SampleDevModeKafka -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

If you are not using Raft or Kafka, you should see an output similar to the
following:

… code:: bash

2017-10-26 19:21:56.301 EDT [common/tools/configtxgen] main -> INFO 001 Loading configuration
2017-10-26 19:21:56.309 EDT [common/tools/configtxgen] doOutputBlock -> INFO 002 Generating genesis block
2017-10-26 19:21:56.309 EDT [common/tools/configtxgen] doOutputBlock -> INFO 003 Writing genesis block

… note:: The orderer genesis block and the subsequent artifacts we are about to create
will be output into the channel-artifacts directory at the root of this
project. The channelID in the above command is the name of the system channel.

… _createchanneltx:

Create a Channel Configuration Transaction
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Next, we need to create the channel transaction artifact. Be sure to replace $CHANNEL_NAME or
set CHANNEL_NAME as an environment variable that can be used throughout these instructions:

… code:: bash

# The channel.tx artifact contains the definitions for our sample channel

export CHANNEL_NAME=mychannel  && ../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

Note that you don’t have to issue a special command for the channel if you are
using a Raft or Kafka ordering service. The TwoOrgsChannel profile will use
the ordering service configuration you specified when creating the genesis block
for the network.

If you are not using a Raft or Kafka ordering service, you should see an output
similar to the following in your terminal:

… code:: bash

2017-10-26 19:24:05.324 EDT [common/tools/configtxgen] main -> INFO 001 Loading configuration
2017-10-26 19:24:05.329 EDT [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx
2017-10-26 19:24:05.329 EDT [common/tools/configtxgen] doOutputChannelCreateTx -> INFO 003 Writing new channel tx

Next, we will define the anchor peer for Org1 on the channel that we are
constructing. Again, be sure to replace $CHANNEL_NAME or set the environment
variable for the following commands. The terminal output will mimic that of the
channel transaction artifact:

… code:: bash

../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP

Now, we will define the anchor peer for Org2 on the same channel:

… code:: bash

../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP

Start the network

后边通过docker run -ti进入到容器内部,详解byfn.sh up具体执行的操作。此处略。

… note:: If you ran the byfn.sh example above previously, be sure that you
have brought down the test network before you proceed (see
Bring Down the Network_).

We will leverage a script to spin up our network. The
docker-compose file references the images that we have previously downloaded,
and bootstraps the orderer with our previously generated genesis.block.

We want to go through the commands manually in order to expose the
syntax and functionality of each call.

First let’s start our network:

… code:: bash

docker-compose -f docker-compose-cli.yaml up -d

If you want to see the realtime logs for your network, then do not supply the -d flag.
If you let the logs stream, then you will need to open a second terminal to execute the CLI calls.

… _createandjoin:

… _peerenvvars:

Create & Join Channel
^^^^^^^^^^^^^^^^^^^^^

Recall that we created the channel configuration transaction using the
configtxgen tool in the :ref:createchanneltx section, above. You can
repeat that process to create additional channel configuration transactions,
using the same or different profiles in the configtx.yaml that you pass
to the configtxgen tool. Then you can repeat the process defined in this
section to establish those other channels in your network.

We will enter the CLI container using the docker exec command:

… code:: bash

    docker exec -it cli bash

If successful you should see the following:

… code:: bash

    root@0d78bb69300d:/opt/gopath/src/github.com/hyperledger/fabric/peer#

For the following CLI commands to work, we need to preface our commands with the
four environment variables given below. These variables for
peer0.org1.example.com are baked into the CLI container, therefore we can
operate without passing them. HOWEVER, if you want to send calls to other peers
or the orderer, override the environment variables as seen in the example below
when you make any CLI calls:

… code:: bash

# Environment variables for PEER0

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt

… _createandjoin:

Next, we are going to pass in the generated channel configuration transaction
artifact that we created in the :ref:createchanneltx section (we called
it channel.tx) to the orderer as part of the create channel request.

We specify our channel name with the -c flag and our channel configuration
transaction with the -f flag. In this case it is channel.tx, however
you can mount your own configuration transaction with a different name. Once again
we will set the CHANNEL_NAME environment variable within our CLI container so that
we don’t have to explicitly pass this argument. Channel names must be all lower
case, less than 250 characters long and match the regular expression
[a-z][a-z0-9.-]*.

… code:: bash

    export CHANNEL_NAME=mychannel

    # the channel.tx file is mounted in the channel-artifacts directory within your CLI container
    # as a result, we pass the full path for the file
    # we also pass the path for the orderer ca-cert in order to verify the TLS handshake
    # be sure to export or replace the $CHANNEL_NAME variable appropriately

    peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

… note:: Notice the --cafile that we pass as part of this command. It is
the local path to the orderer’s root cert, allowing us to verify the
TLS handshake.

This command returns a genesis block - - which we will use to join the channel.
It contains the configuration information specified in channel.tx If you have not
made any modifications to the default channel name, then the command will return you a
proto titled mychannel.block.

… note:: You will remain in the CLI container for the remainder of
these manual commands. You must also remember to preface all commands
with the corresponding environment variables when targeting a peer other than
peer0.org1.example.com.

Now let’s join peer0.org1.example.com to the channel.

… code:: bash

    # By default, this joins ``peer0.org1.example.com`` only
    # the  was returned by the previous command
    # if you have not modified the channel name, you will join with mychannel.block
    # if you have created a different channel name, then pass in the appropriately named block

     peer channel join -b mychannel.block

You can make other peers join the channel as necessary by making appropriate
changes in the four environment variables we used in the :ref:peerenvvars
section, above.

Rather than join every peer, we will simply join peer0.org2.example.com so that
we can properly update the anchor peer definitions in our channel. Since we are
overriding the default environment variables baked into the CLI container, this full
command will be the following:

… code:: bash

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 CORE_PEER_LOCALMSPID=“Org2MSP” CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel join -b mychannel.block

… note:: Prior to v1.4.1 all peers within the docker network used port 7051.
If using a version of fabric-samples prior to v1.4.1, modify all
occurrences of CORE_PEER_ADDRESS in this tutorial to use port 7051.

Alternatively, you could choose to set these environment variables individually
rather than passing in the entire string. Once they’ve been set, you simply need
to issue the peer channel join command again and the CLI container will act
on behalf of peer0.org2.example.com.

Start the network

后边通过docker run -ti进入到容器内部,详解byfn.sh up具体执行的操作。此处略。

后边通过docker run -ti进入到容器内部,详解byfn.sh up具体执行的操作,包括install,query,invoke。按照原文操作即可,此处略。

日志可以通过如下方式查看

… code:: bash

    docker logs -f cli

也可以通过 docker logs 加节点名称查看日志。

… code:: bash

    docker logs dev-peer0.org2.example.com-mycc-1.0

另外还可以把内置的LevelDB切换成CouchDB。

如果需要清理环境,可以byfn.sh down以后,删除所有docker实例和镜像。

    docker rm -f $(docker ps -aq)
    docker rmi -f $(docker images -q)

以及删除所有已创建的网络

    docker network prune

… note:: If you ran the byfn.sh example above previously, be sure that you
have brought down the test network before you proceed (see
Bring Down the Network_).

We will leverage a script to spin up our network. The
docker-compose file references the images that we have previously downloaded,
and bootstraps the orderer with our previously generated genesis.block.

We want to go through the commands manually in order to expose the
syntax and functionality of each call.

First let’s start our network:

… code:: bash

docker-compose -f docker-compose-cli.yaml up -d

If you want to see the realtime logs for your network, then do not supply the -d flag.
If you let the logs stream, then you will need to open a second terminal to execute the CLI calls.

… _createandjoin:

… _peerenvvars:

Create & Join Channel
^^^^^^^^^^^^^^^^^^^^^

Recall that we created the channel configuration transaction using the
configtxgen tool in the :ref:createchanneltx section, above. You can
repeat that process to create additional channel configuration transactions,
using the same or different profiles in the configtx.yaml that you pass
to the configtxgen tool. Then you can repeat the process defined in this
section to establish those other channels in your network.

We will enter the CLI container using the docker exec command:

… code:: bash

    docker exec -it cli bash

If successful you should see the following:

… code:: bash

    root@0d78bb69300d:/opt/gopath/src/github.com/hyperledger/fabric/peer#

For the following CLI commands to work, we need to preface our commands with the
four environment variables given below. These variables for
peer0.org1.example.com are baked into the CLI container, therefore we can
operate without passing them. HOWEVER, if you want to send calls to other peers
or the orderer, override the environment variables as seen in the example below
when you make any CLI calls:

… code:: bash

# Environment variables for PEER0

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt

… _createandjoin:

Next, we are going to pass in the generated channel configuration transaction
artifact that we created in the :ref:createchanneltx section (we called
it channel.tx) to the orderer as part of the create channel request.

We specify our channel name with the -c flag and our channel configuration
transaction with the -f flag. In this case it is channel.tx, however
you can mount your own configuration transaction with a different name. Once again
we will set the CHANNEL_NAME environment variable within our CLI container so that
we don’t have to explicitly pass this argument. Channel names must be all lower
case, less than 250 characters long and match the regular expression
[a-z][a-z0-9.-]*.

… code:: bash

    export CHANNEL_NAME=mychannel

    # the channel.tx file is mounted in the channel-artifacts directory within your CLI container
    # as a result, we pass the full path for the file
    # we also pass the path for the orderer ca-cert in order to verify the TLS handshake
    # be sure to export or replace the $CHANNEL_NAME variable appropriately

    peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

… note:: Notice the --cafile that we pass as part of this command. It is
the local path to the orderer’s root cert, allowing us to verify the
TLS handshake.

This command returns a genesis block - - which we will use to join the channel.
It contains the configuration information specified in channel.tx If you have not
made any modifications to the default channel name, then the command will return you a
proto titled mychannel.block.

… note:: You will remain in the CLI container for the remainder of
these manual commands. You must also remember to preface all commands
with the corresponding environment variables when targeting a peer other than
peer0.org1.example.com.

Now let’s join peer0.org1.example.com to the channel.

… code:: bash

    # By default, this joins ``peer0.org1.example.com`` only
    # the  was returned by the previous command
    # if you have not modified the channel name, you will join with mychannel.block
    # if you have created a different channel name, then pass in the appropriately named block

     peer channel join -b mychannel.block

You can make other peers join the channel as necessary by making appropriate
changes in the four environment variables we used in the :ref:peerenvvars
section, above.

Rather than join every peer, we will simply join peer0.org2.example.com so that
we can properly update the anchor peer definitions in our channel. Since we are
overriding the default environment variables baked into the CLI container, this full
command will be the following:

… code:: bash

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 CORE_PEER_LOCALMSPID=“Org2MSP” CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel join -b mychannel.block

… note:: Prior to v1.4.1 all peers within the docker network used port 7051.
If using a version of fabric-samples prior to v1.4.1, modify all
occurrences of CORE_PEER_ADDRESS in this tutorial to use port 7051.

Alternatively, you could choose to set these environment variables individually
rather than passing in the entire string. Once they’ve been set, you simply need
to issue the peer channel join command again and the CLI container will act
on behalf of peer0.org2.example.com.

Update the anchor peers
^^^^^^^^^^^^^^^^^^^^^^^

The following commands are channel updates and they will propagate to the definition
of the channel. In essence, we adding additional configuration information on top
of the channel’s genesis block. Note that we are not modifying the genesis block, but
simply adding deltas into the chain that will define the anchor peers.

Update the channel definition to define the anchor peer for Org1 as peer0.org1.example.com:

… code:: bash

peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

Now update the channel definition to define the anchor peer for Org2 as peer0.org2.example.com.
Identically to the peer channel join command for the Org2 peer, we will need to
preface this call with the appropriate environment variables.

… code:: bash

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 CORE_PEER_LOCALMSPID=“Org2MSP” CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

Install & Instantiate Chaincode
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

… note:: We will utilize a simple existing chaincode. To learn how to write
your own chaincode, see the :doc:chaincode4ade tutorial.

Applications interact with the blockchain ledger through chaincode. As
such we need to install the chaincode on every peer that will execute and
endorse our transactions, and then instantiate the chaincode on the channel.

First, install the sample Go, Node.js or Java chaincode onto the peer0
node in Org1. These commands place the specified source
code flavor onto our peer’s filesystem.

… note:: You can only install one version of the source code per chaincode name
and version. The source code exists on the peer’s file system in the
context of chaincode name and version; it is language agnostic. Similarly
the instantiated chaincode container will be reflective of whichever
language has been installed on the peer.

Golang

… code:: bash

# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

Node.js

… code:: bash

# this installs the Node.js chaincode
# make note of the -l flag to indicate "node" chaincode
# for node chaincode -p takes the absolute path to the node.js chaincode
peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/

Java

… code:: bash

# make note of the -l flag to indicate "java" chaincode
# for java chaincode -p takes the absolute path to the java chaincode
peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/

When we instantiate the chaincode on the channel, the endorsement policy will be
set to require endorsements from a peer in both Org1 and Org2. Therefore, we
also need to install the chaincode on a peer in Org2.

Modify the following four environment variables to issue the install command
against peer0 in Org2:

… code:: bash

Environment variables for PEER0 in Org2

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer0.org2.example.com:9051
CORE_PEER_LOCALMSPID=“Org2MSP”
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt

Now install the sample Go, Node.js or Java chaincode onto a peer0
in Org2. These commands place the specified source
code flavor onto our peer’s filesystem.

Golang

… code:: bash

# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

Node.js

… code:: bash

# this installs the Node.js chaincode
# make note of the -l flag to indicate "node" chaincode
# for node chaincode -p takes the absolute path to the node.js chaincode
peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/

Java

… code:: bash

# make note of the -l flag to indicate "java" chaincode
# for java chaincode -p takes the absolute path to the java chaincode
peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/

Next, instantiate the chaincode on the channel. This will initialize the
chaincode on the channel, set the endorsement policy for the chaincode, and
launch a chaincode container for the targeted peer. Take note of the -P
argument. This is our policy where we specify the required level of endorsement
for a transaction against this chaincode to be validated.

In the command below you’ll notice that we specify our policy as
-P "AND ('Org1MSP.peer','Org2MSP.peer')". This means that we need
“endorsement” from a peer belonging to Org1 AND Org2 (i.e. two endorsement).
If we changed the syntax to OR then we would need only one endorsement.

Golang

… code:: bash

# be sure to replace the $CHANNEL_NAME environment variable if you have not exported it
# if you did not install your chaincode with a name of mycc, then modify that argument as well

peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

Node.js

… note:: The instantiation of the Node.js chaincode will take roughly a minute.
The command is not hanging; rather it is installing the fabric-shim
layer as the image is being compiled.

… code:: bash

# be sure to replace the $CHANNEL_NAME environment variable if you have not exported it
# if you did not install your chaincode with a name of mycc, then modify that argument as well
# notice that we must pass the -l flag after the chaincode name to identify the language

peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -l node -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

Java

… note:: Please note, Java chaincode instantiation might take time as it compiles chaincode and
downloads docker container with java environment.

… code:: bash

peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -l java -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

See the endorsement policies __
documentation for more details on policy implementation.

If you want additional peers to interact with ledger, then you will need to join
them to the channel, and install the same name, version and language of the
chaincode source onto the appropriate peer’s filesystem. A chaincode container
will be launched for each peer as soon as they try to interact with that specific
chaincode. Again, be cognizant of the fact that the Node.js images will be slower
to compile.

Once the chaincode has been instantiated on the channel, we can forgo the l
flag. We need only pass in the channel identifier and name of the chaincode.

Query
^^^^^

Let’s query for the value of a to make sure the chaincode was properly
instantiated and the state DB was populated. The syntax for query is as follows:

… code:: bash

be sure to set the -C and -n flags appropriately

peer chaincode query -C $CHANNEL_NAME -n mycc -c ‘{“Args”:[“query”,“a”]}’

Invoke
^^^^^^

Now let’s move 10 from a to b. This transaction will cut a new block and
update the state DB. The syntax for invoke is as follows:

… code:: bash

# be sure to set the -C and -n flags appropriately

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'

Query
^^^^^

Let’s confirm that our previous invocation executed properly. We initialized the
key a with a value of 100 and just removed 10 with our previous
invocation. Therefore, a query against a should return 90. The syntax
for query is as follows.

… code:: bash

be sure to set the -C and -n flags appropriately

peer chaincode query -C $CHANNEL_NAME -n mycc -c ‘{“Args”:[“query”,“a”]}’

We should see the following:

… code:: bash

Query Result: 90

Feel free to start over and manipulate the key value pairs and subsequent
invocations.

Install
^^^^^^^

Now we will install the chaincode on a third peer, peer1 in Org2. Modify the
following four environment variables to issue the install command
against peer1 in Org2:

… code:: bash

Environment variables for PEER1 in Org2

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
CORE_PEER_ADDRESS=peer1.org2.example.com:10051
CORE_PEER_LOCALMSPID=“Org2MSP”
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt

Now install the sample Go, Node.js or Java chaincode onto peer1
in Org2. These commands place the specified source
code flavor onto our peer’s filesystem.

Golang

… code:: bash

# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

Node.js

… code:: bash

# this installs the Node.js chaincode
# make note of the -l flag to indicate "node" chaincode
# for node chaincode -p takes the absolute path to the node.js chaincode
peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/

Java

… code:: bash

# make note of the -l flag to indicate "java" chaincode
# for java chaincode -p takes the absolute path to the java chaincode
peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/

Query
^^^^^

Let’s confirm that we can issue the query to Peer1 in Org2. We initialized the
key a with a value of 100 and just removed 10 with our previous
invocation. Therefore, a query against a should still return 90.

peer1 in Org2 must first join the channel before it can respond to queries. The
channel can be joined by issuing the following command:

… code:: bash

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp CORE_PEER_ADDRESS=peer1.org2.example.com:10051 CORE_PEER_LOCALMSPID=“Org2MSP” CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt peer channel join -b mychannel.block

After the join command returns, the query can be issued. The syntax
for query is as follows.

… code:: bash

be sure to set the -C and -n flags appropriately

peer chaincode query -C $CHANNEL_NAME -n mycc -c ‘{“Args”:[“query”,“a”]}’

We should see the following:

… code:: bash

Query Result: 90

Feel free to start over and manipulate the key value pairs and subsequent
invocations.

… _behind-scenes:

What’s happening behind the scenes?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

… note:: These steps describe the scenario in which
script.sh is run by ‘./byfn.sh up’. Clean your network
with ./byfn.sh down and ensure
this command is active. Then use the same
docker-compose prompt to launch your network again

  • A script - script.sh - is baked inside the CLI container. The
    script drives the createChannel command against the supplied channel name
    and uses the channel.tx file for channel configuration.

  • The output of createChannel is a genesis block -
    .block - which gets stored on the peers’ file systems and contains
    the channel configuration specified from channel.tx.

  • The joinChannel command is exercised for all four peers, which takes as
    input the previously generated genesis block. This command instructs the
    peers to join and create a chain starting with .block.

  • Now we have a channel consisting of four peers, and two
    organizations. This is our TwoOrgsChannel profile.

  • peer0.org1.example.com and peer1.org1.example.com belong to Org1;
    peer0.org2.example.com and peer1.org2.example.com belong to Org2

  • These relationships are defined through the crypto-config.yaml and
    the MSP path is specified in our docker compose.

  • The anchor peers for Org1MSP (peer0.org1.example.com) and
    Org2MSP (peer0.org2.example.com) are then updated. We do this by passing
    the Org1MSPanchors.tx and Org2MSPanchors.tx artifacts to the ordering
    service along with the name of our channel.

  • A chaincode - chaincode_example02 - is installed on peer0.org1.example.com and
    peer0.org2.example.com

  • The chaincode is then “instantiated” on mychannel. Instantiation
    adds the chaincode to the channel, starts the container for the target peer,
    and initializes the key value pairs associated with the chaincode. The initial
    values for this example are [“a”,“100” “b”,“200”]. This “instantiation” results
    in a container by the name of dev-peer0.org2.example.com-mycc-1.0 starting.

  • The instantiation also passes in an argument for the endorsement
    policy. The policy is defined as
    -P "AND ('Org1MSP.peer','Org2MSP.peer')", meaning that any
    transaction must be endorsed by a peer tied to Org1 and Org2.

  • A query against the value of “a” is issued to peer0.org2.example.com.
    A container for Org2 peer0 by the name of dev-peer0.org2.example.com-mycc-1.0
    was started when the chaincode was instantiated. The result
    of the query is returned. No write operations have occurred, so
    a query against “a” will still return a value of “100”.

  • An invoke is sent to peer0.org1.example.com and peer0.org2.example.com
    to move “10” from “a” to “b”

  • A query is sent to peer0.org2.example.com for the value of “a”. A
    value of 90 is returned, correctly reflecting the previous
    transaction during which the value for key “a” was modified by 10.

  • The chaincode - chaincode_example02 - is installed on peer1.org2.example.com

  • A query is sent to peer1.org2.example.com for the value of “a”. This starts a
    third chaincode container by the name of dev-peer1.org2.example.com-mycc-1.0. A
    value of 90 is returned, correctly reflecting the previous
    transaction during which the value for key “a” was modified by 10.

What does this demonstrate?
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Chaincode MUST be installed on a peer in order for it to
successfully perform read/write operations against the ledger.
Furthermore, a chaincode container is not started for a peer until an init or
traditional transaction - read/write - is performed against that chaincode (e.g. query for
the value of “a”). The transaction causes the container to start. Also,
all peers in a channel maintain an exact copy of the ledger which
comprises the blockchain to store the immutable, sequenced record in
blocks, as well as a state database to maintain a snapshot of the current state.
This includes those peers that do not have chaincode installed on them
(like peer1.org1.example.com in the above example) . Finally, the chaincode is accessible
after it is installed (like peer1.org2.example.com in the above example) because it
has already been instantiated.

How do I see these transactions?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Check the logs for the CLI Docker container.

… code:: bash

    docker logs -f cli

You should see the following output:

… code:: bash

  2017-05-16 17:08:01.366 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing local MSP
  2017-05-16 17:08:01.366 UTC [msp] GetDefaultSigningIdentity -> DEBU 005 Obtaining default signing identity
  2017-05-16 17:08:01.366 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0AB1070A6708031A0C08F1E3ECC80510...6D7963631A0A0A0571756572790A0161
  2017-05-16 17:08:01.367 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: E61DB37F4E8B0D32C9FE10E3936BA9B8CD278FAA1F3320B08712164248285C54
  Query Result: 90
  2017-05-16 17:08:15.158 UTC [main] main -> INFO 008 Exiting.....
  ===================== Query successful on peer1.org2 on channel 'mychannel' =====================

  ===================== All GOOD, BYFN execution completed =====================


   _____   _   _   ____
  | ____| | \ | | |  _ \
  |  _|   |  \| | | | | |
  | |___  | |\  | | |_| |
  |_____| |_| \_| |____/

You can scroll through these logs to see the various transactions.

How can I see the chaincode logs?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Inspect the individual chaincode containers to see the separate
transactions executed against each container. Here is the combined
output from each container:

… code:: bash

    $ docker logs dev-peer0.org2.example.com-mycc-1.0
    04:30:45.947 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
    ex02 Init
    Aval = 100, Bval = 200

    $ docker logs dev-peer0.org1.example.com-mycc-1.0
    04:31:10.569 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
    ex02 Invoke
    Query Response:{"Name":"a","Amount":"100"}
    ex02 Invoke
    Aval = 90, Bval = 210

    $ docker logs dev-peer1.org2.example.com-mycc-1.0
    04:31:30.420 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
    ex02 Invoke
    Query Response:{"Name":"a","Amount":"90"}

你可能感兴趣的:(区块链)