目录

部署一个智能合约到某通道

目录

根据前面了解到的《Fabric 链码生命周期》将链码部署到通道。

  • 打包链码
  • 安装链码
  • 核准链码定义
  • 提交链码定义到通道
cd fabric-samples/test-network
./network.sh down
./network.sh up createChannel

createChannel 命令创建一个名为 mychannel 的通道,带有两个通道成员Org1和Org2。该命令还将属于每个组织的对等点加入到通道中。

此步骤不是必需的,但对于链码故障诊断非常有用。要监视智能契约的日志,管理员可以使用 logspout 工具查看一组 Docker 容器的聚合输出。

Logspout工具将不断地将日志流传输到终端,因此需要使用一个新的终端窗口。打开一个新的终端并导航到test-network目录,已经写好了一个安装和配置的脚本了,可直接使用。

cd fabric-samples/test-network
cp ../commercial-paper/organization/digibank/configuration/cli/monitordocker.sh .
./monitordocker.sh net_test

不同语言( Go, JavaScript, 或 Typescript)编写的链码,打包过程是不一样的,以 Go 语言为例。

该示例使用Go模块来安装链码依赖项。 依赖关系列在 asset-transfer-basic/chaincode-go目录的 go.mod 文件中。该文件导入了 Fabric Contract API,可从 asset-transfer-basic/chaincode-go/chaincode/smartcontract.go 中查看定义的 SmartContract 类型。

cd fabric-samples/asset-transfer-basic/chaincode-go

SmartContract 类型用于为智能合约中定义的函数创建事务上下文,这些函数读取和写入数据到区块链账本。

// SmartContract provides functions for managing an Asset
type SmartContract struct {
    contractapi.Contract
}
// Asset describes basic details of what makes up a simple asset
type Asset struct {
	ID             string `json:"ID"`
	Color          string `json:"color"`
	Size           int    `json:"size"`
	Owner          string `json:"owner"`
	AppraisedValue int    `json:"appraisedValue"`
}
// InitLedger adds a base set of assets to the ledger
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
	assets := []Asset{
		{ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
		{ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
		{ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
		{ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
		{ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
		{ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
	}

	for _, asset := range assets {
		assetJSON, err := json.Marshal(asset)
		if err != nil {
			return err
		}

		err = ctx.GetStub().PutState(asset.ID, assetJSON)
		if err != nil {
			return fmt.Errorf("failed to put to world state. %v", err)
		}
	}

	return nil
}
// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
    exists, err := s.AssetExists(ctx, id)
    if err != nil {
        return err
    }
    if exists {
        return fmt.Errorf("the asset %s already exists", id)
    }

    asset := Asset{
        ID:             id,
        Color:          color,
        Size:           size,
        Owner:          owner,
        AppraisedValue: appraisedValue,
    }
    assetJSON, err := json.Marshal(asset)
    if err != nil {
        return err
    }

    return ctx.GetStub().PutState(id, assetJSON)
}

可通过访问API文档智能合约处理来了解有关Go contract API的更多信息。

要安装智能合约依赖关系,从 asset-transfer-basic/chaincode-go 目录运行以下命令。如果命令成功,go包将安装在一个 vendor 文件夹中。

GO111MODULE=on go mod vendor 

现在已经有了依赖项,可以创建链码包了。返回到 test-network 文件夹中的工作目录,这样就可以将链码与其他网络工件打包在一起了。

cd ../../test-network
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=$PWD/../config/
peer lifecycle chaincode package basic.tar.gz --path ../asset-transfer-basic/chaincode-go/ --lang golang --label basic_1.0

这个命令将在当前目录中创建一个名为 basic.tar.gz 的包。--lang标志用于指定链码语言,--path标志提供智能合约代码的位置。该路径必须是完全限定的路径或相对于当前工作目录的路径。--label标志用于指定一个链码标签,该标签将在安装后标识链码。建议标签包括链码名称和版本。

现在我们已经创建了链码包,可以在测试网络的对等节点上安装链码了。

链码需要安装在每一个将认可交易的对等节点上。

先在Org1对等节点上安装链码。设置以下环境变量以作为Org1 admin用户操作对等CLI。核心对等点地址将被设置为指向Org1对等节点,peer0.org1.example.com。

export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
peer lifecycle chaincode install basic.tar.gz

export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051

peer lifecycle chaincode install basic.tar.gz

如果命令成功,对等节点将生成并返回包标识符。这个包ID将在下一步中用于批准链码。

peer lifecycle chaincode queryinstalled
Installed chaincodes on peer:
Package ID: basic_1.0:69de748301770f6ef64b42aa6bb6cb291df20aa39542c3ef94008615704007f3, Label: basic_1.0
export CC_PACKAGE_ID=basic_1.0:69de748301770f6ef64b42aa6bb6cb291df20aa39542c3ef94008615704007f3

peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_ADDRESS=localhost:7051

peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

在足够多的组织批准了链码定义之后,一个组织可以将链码定义提交给通道。

peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 1.0 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --output json

    {
            "Approvals": {
                    "Org1MSP": true,
                    "Org2MSP": true
            }
    }
  
peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
peer lifecycle chaincode querycommitted --channelID mychannel --name basic --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
Committed chaincode definition for chaincode 'basic' on channel 'mychannel':
Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true]

这里以命令行调用为例,也可编写 Node.js 、Go、Java程序。

peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"initLedger","Args":[]}'
peer chaincode query -C mychannel -n basic -c '{"Args":["getAllAssets"]}'
docker stop logspout
docker rm logspout
./network.sh down

Deploying a smart contract to a channel