在本系列第一部分中,我们介绍了搭建Fabric测试网络的目的、前提,并对其主要组件作了简介。在本部分中,我们将继续搭建Fabric测试网络的过程。
创建通道¶
既然我们的机器上运行起了Peers节点和Ordering排序节点,我们就可以使用该脚本为Org1和Org2之间的事务创建一个Fabric通道(Channel)。
通道是特定网络成员之间通信的专用层。通道只能由受邀加入该通道的组织使用,并且对网络的其他成员不可见。每个通道都有一个单独的区块链账本。已被邀请的组织将其同行“加入”通道,以存储通道分类账并验证通道上的交易。
您可以使用network.sh脚本在Org1和Org2之间创建一个通道,并将其Peers加入该通道。运行以下命令以创建默认名称为mychannel的通道:
./network.sh createChannel
如果命令成功,您可以在日志中看到以下消息:
Channel 'mychannel' joined
也可以使用通道标志创建具有自定义名称的通道。例如,以下命令将创建一个名为channel1的通道:
./network.sh createChannel -c channel1
在我的机器上,完整显示内容如下:
Using docker and docker-compose
Creating channel 'channel1'.
If network is not up, starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb
Network Running Already
Using docker and docker-compose
Generating channel genesis block 'channel1.block'
/Users/zxzpc/go/src/github.com/zxz-googleman/fabric-samples/test-network/../bin/configtxgen
+ configtxgen -profile TwoOrgsApplicationGenesis -outputBlock ./channel-artifacts/channel1.block -channelID channel1
2023-08-26 11:43:27.408 CST 0001 INFO [common.tools.configtxgen] main -> Loading configuration
2023-08-26 11:43:27.426 CST 0002 INFO [common.tools.configtxgen.localconfig] completeInitialization -> orderer type: etcdraft
2023-08-26 11:43:27.426 CST 0003 INFO [common.tools.configtxgen.localconfig] completeInitialization
2023-08-26 11:43:27.426 CST 0004 INFO [common.tools.configtxgen.localconfig] Load -> Loaded configuration: /Users/zxzpc/go/src/github.com/zxz-googleman/fabric-samples/test-network/configtx/configtx.yaml
2023-08-26 11:43:27.481 CST 0005 INFO [common.tools.configtxgen] doOutputBlock -> Generating genesis block
2023-08-26 11:43:27.481 CST 0006 INFO [common.tools.configtxgen] doOutputBlock -> Creating application channel genesis block
2023-08-26 11:43:27.482 CST 0007 INFO [common.tools.configtxgen] doOutputBlock -> Writing genesis block
+ res=0
Creating channel channel1
Using organization 1
+ osnadmin channel join --channelID channel1 --config-block ./channel-artifacts/channel1.block -o localhost:7053 --ca-file /Users/zxzpc/go/src/github.com/zxz-googleman/fabric-samples/test-network/organizations/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem --client-cert /Users/zxzpc/go/src/github.com/zxz-googleman/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt --client-key /Users/zxzpc/go/src/github.com/zxz-googleman/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.key
+ res=0
Status: 201
{
"name": "channel1",
"url": "/participation/v1/channels/channel1",
"consensusRelation": "consenter",
"status": "active",
"height": 1
}
Channel 'channel1' created
Joining org1 peer to the channel...
Using organization 1
+ peer channel join -b ./channel-artifacts/channel1.block
+ res=0
2023-08-26 11:43:35.526 CST 0001 INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
2023-08-26 11:43:35.593 CST 0002 INFO [channelCmd] executeJoin -> Successfully submitted proposal to join channel
Joining org2 peer to the channel...
Using organization 2
+ peer channel join -b ./channel-artifacts/channel1.block
+ res=0
2023-08-26 11:43:38.739 CST 0001 INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
2023-08-26 11:43:38.786 CST 0002 INFO [channelCmd] executeJoin -> Successfully submitted proposal to join channel
Setting anchor peer for org1...
Using organization 1
Fetching channel config for channel channel1
Using organization 1
Fetching the most recent configuration block for the channel
+ peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c channel1 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/organizations/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem
2023-08-26 03:43:39.506 UTC 0001 INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
2023-08-26 03:43:39.576 UTC 0002 INFO [cli.common] readBlock -> Received block: 0
2023-08-26 03:43:39.577 UTC 0003 INFO [channelCmd] fetch -> Retrieving last config block: 0
2023-08-26 03:43:39.579 UTC 0004 INFO [cli.common] readBlock -> Received block: 0
+ configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json
Decoding config block to JSON and isolating config to Org1MSPconfig.json
+ jq '.data.data[0].payload.data.config' config_block.json
+ jq '.channel_group.groups.Application.groups.Org1MSP.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "peer0.org1.example.com","port": 7051}]},"version": "0"}}' Org1MSPconfig.json
Generating anchor peer update transaction for Org1 on channel channel1
+ configtxlator proto_encode --input Org1MSPconfig.json --type common.Config --output original_config.pb
+ configtxlator proto_encode --input Org1MSPmodified_config.json --type common.Config --output modified_config.pb
+ configtxlator compute_update --channel_id channel1 --original original_config.pb --updated modified_config.pb --output config_update.pb
+ configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate --output config_update.json
+ jq .
++ cat config_update.json
+ echo '{"payload":{"header":{"channel_header":{"channel_id":"channel1", "type":2}},"data":{"config_update":{' '"channel_id":' '"channel1",' '"isolated_data":' '{},' '"read_set":' '{' '"groups":' '{' '"Application":' '{' '"groups":' '{' '"Org1MSP":' '{' '"groups":' '{},' '"mod_policy":' '"",' '"policies":' '{' '"Admins":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Endorsement":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Readers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Writers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '}' '},' '"values":' '{' '"MSP":' '{' '"mod_policy":' '"",' '"value":' null, '"version":' '"0"' '}' '},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '},' '"write_set":' '{' '"groups":' '{' '"Application":' '{' '"groups":' '{' '"Org1MSP":' '{' '"groups":' '{},' '"mod_policy":' '"Admins",' '"policies":' '{' '"Admins":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Endorsement":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Readers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Writers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '}' '},' '"values":' '{' '"AnchorPeers":' '{' '"mod_policy":' '"Admins",' '"value":' '{' '"anchor_peers":' '[' '{' '"host":' '"peer0.org1.example.com",' '"port":' 7051 '}' ']' '},' '"version":' '"0"' '},' '"MSP":' '{' '"mod_policy":' '"",' '"value":' null, '"version":' '"0"' '}' '},' '"version":' '"1"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '}}}}'
+ configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope --output Org1MSPanchors.tx
2023-08-26 03:43:40.666 UTC 0001 INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
2023-08-26 03:43:40.681 UTC 0002 INFO [channelCmd] update -> Successfully submitted channel update
Anchor peer set for org 'Org1MSP' on channel 'channel1'
Setting anchor peer for org2...
Using organization 2
Fetching channel config for channel channel1
Using organization 2
Fetching the most recent configuration block for the channel
+ peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c channel1 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/organizations/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem
2023-08-26 03:43:41.418 UTC 0001 INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
2023-08-26 03:43:41.420 UTC 0002 INFO [cli.common] readBlock -> Received block: 1
2023-08-26 03:43:41.421 UTC 0003 INFO [channelCmd] fetch -> Retrieving last config block: 1
2023-08-26 03:43:41.423 UTC 0004 INFO [cli.common] readBlock -> Received block: 1
Decoding config block to JSON and isolating config to Org2MSPconfig.json
+ configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json
+ jq '.data.data[0].payload.data.config' config_block.json
Generating anchor peer update transaction for Org2 on channel channel1
+ jq '.channel_group.groups.Application.groups.Org2MSP.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "peer0.org2.example.com","port": 9051}]},"version": "0"}}' Org2MSPconfig.json
+ configtxlator proto_encode --input Org2MSPconfig.json --type common.Config --output original_config.pb
+ configtxlator proto_encode --input Org2MSPmodified_config.json --type common.Config --output modified_config.pb
+ configtxlator compute_update --channel_id channel1 --original original_config.pb --updated modified_config.pb --output config_update.pb
+ configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate --output config_update.json
+ jq .
++ cat config_update.json
+ echo '{"payload":{"header":{"channel_header":{"channel_id":"channel1", "type":2}},"data":{"config_update":{' '"channel_id":' '"channel1",' '"isolated_data":' '{},' '"read_set":' '{' '"groups":' '{' '"Application":' '{' '"groups":' '{' '"Org2MSP":' '{' '"groups":' '{},' '"mod_policy":' '"",' '"policies":' '{' '"Admins":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Endorsement":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Readers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Writers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '}' '},' '"values":' '{' '"MSP":' '{' '"mod_policy":' '"",' '"value":' null, '"version":' '"0"' '}' '},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '},' '"write_set":' '{' '"groups":' '{' '"Application":' '{' '"groups":' '{' '"Org2MSP":' '{' '"groups":' '{},' '"mod_policy":' '"Admins",' '"policies":' '{' '"Admins":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Endorsement":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Readers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Writers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '}' '},' '"values":' '{' '"AnchorPeers":' '{' '"mod_policy":' '"Admins",' '"value":' '{' '"anchor_peers":' '[' '{' '"host":' '"peer0.org2.example.com",' '"port":' 9051 '}' ']' '},' '"version":' '"0"' '},' '"MSP":' '{' '"mod_policy":' '"",' '"value":' null, '"version":' '"0"' '}' '},' '"version":' '"1"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '}}}}'
+ configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope --output Org2MSPanchors.tx
2023-08-26 03:43:41.767 UTC 0001 INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
2023-08-26 03:43:41.782 UTC 0002 INFO [channelCmd] update -> Successfully submitted channel update
Anchor peer set for org 'Org2MSP' on channel 'channel1'
Channel 'channel1' joined
通道标志还允许您通过指定不同的通道名称来创建多个通道。创建mychannel或channel1后,可以使用下面的命令创建第二个名为channel2的通道:
./network.sh createChannel-c channel2
注意:确保通道名称适用以下限制:
- 仅包含小写ASCII字母数字,点“.”,和短划线“-”
- 短于250个字符
- 以字母开头
如果您想通过一步操作来打开网络并创建通道,您可以同时使用up和createChannel模式:
./network.sh up createChannel
在通道上启动链码(也称“智能合约”)¶
创建通道后,您可以开始使用智能合约与通道分类账进行交互。
智能合约包含管理区块链账本上资产的业务逻辑。网络成员运行的应用程序可以调用智能合约在账本上创建资产,以及更改和转移这些资产。应用程序还查询智能合约以读取分类账上的数据。
为了确保交易有效,使用智能合约创建的交易通常需要由多个组织签署才能提交到通道分类账。多个签名是Fabric信任模型的组成部分。要求对一笔交易进行多次背书可以防止通道上的一个组织篡改其同行的分类账或使用未经同意的业务逻辑。要签署交易,每个组织都需要在其同行上调用并执行智能合约,然后由智能合约签署交易的输出。如果输出是一致的,并且已经有足够多的组织签名,则可以将交易提交到分类账。指定通道上需要执行智能合约的集合组织的策略被称为背书策略,它是为每个链代码设置的,作为链代码定义的一部分。
在Fabric中,智能合约以称为链代码的包的形式部署在网络上。Chaincode安装在组织的Peers上,然后部署到一个通道,在那里它可以用于背书交易并与区块链账本交互。在将链代码部署到通道之前,通道的成员需要就建立链代码管理的链代码定义达成一致。当所需数量的组织达成一致时,可以将链代码定义提交到通道,并且链代码就可以使用了。
使用network.sh创建通道后,可以使用以下命令在通道上启动链码:
./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
上述命令中,deployCC子命令将在peer0.org.example.com和peer0.org2.example.com上安装资产转移(基本)链代码,然后在使用通道标志指定的通道上部署链代码(如果未指定通道,则部署mychannel)。如果您是第一次部署链代码,脚本将安装链代码依赖项。您可以使用语言标志-ccl来安装Go、typescript或javascript版本的链代码。您可以在fabric-samples目录的asset-transfer-basic文件夹中找到资产转移(基本)链代码。此文件夹包含示例链代码,本文将使用这些示例链代码来突出显示Fabric功能。