Fabric First-Network

Scroll Down

Fabric First-Network

官方bynf

在安装好前期的依赖后,我们运行官方的byfn.sh,进行官方的Example的测试

# 在fabric-exsamples/first-network下执行
# 按照配置文件初始化
./byfn.sh -m generate
# 启动第一个测试网络
./byfn.sh up

启动后根据环境的不同,会出现所谓的panic

如果一切顺利,会输出这样的日志信息

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


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

此时只需要执行

# 关闭测试网络
./byfn.sh down

当然,我们可以部署不同语言的链码

# 使用Java语言编写的链码启动
./byfn.sh up -l java

注意:此处一定要先执行一下./byfn.sh down把现有的网络给清除,两种语言的链码不能共存

My First-Network

自己动手配置一个网络

简述一下项目文件结构,由tree命令打印出的项目目录如下

├── basic-network
│   ├── README.md
│   ├── config
│   ├── configtx.yaml
│   ├── connection.yaml
│   ├── crypto-config
│   ├── crypto-config.yaml
│   ├── docker-compose.yml
│   ├── generate.sh
│   ├── init.sh
│   ├── start.sh
│   ├── stop.sh
│   └── teardown.sh
├── chaincode
│   ├── hyperledger
│   └── test1
└── startup.sh

其中basic-network中是保存的和网络相关的文件

chaincode中保存的是链码

配置一个简单的Fabric网络

fabric-samples中获取一个基础网络的例子,我们可以基于这个例子对网络进行配置

配置身份信息文件

身份信息配置文件为crypto-config.yaml

OrdererOrgs:
  - Name: Orderer
    Domain: example.com
    Specs:
      - Hostname: orderer
PeerOrgs:
  - Name: Org1
    Domain: org1.example.com
    Template:
      Count: 1
    Users:
      Count: 1

配置身份信息主要用于以后的认证,这里配置了一个Orderer和一个Organization,其中这个组织只有一个Peer一个User

配置交易文件

交易配置文件为configtx.yaml

Organizations:
		# 基本组织信息的配置,主要是身份信息的认证
    - &OrdererOrg
        Name: OrdererOrg
        ID: OrdererMSP
        MSPDir: crypto-config/ordererOrganizations/example.com/msp

    - &Org1
        Name: Org1MSP
        ID: Org1MSP
        MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
        AnchorPeers:
            - Host: peer0.org1.example.com
              Port: 7051

Application: &ApplicationDefaults
    Organizations:
Orderer: &OrdererDefaults
		# Orderer排序节点的配置信息
    # Orderer Type: The orderer implementation to start
    # Available types are "solo" and "kafka"
    OrdererType: solo
    Addresses:
        - orderer.example.com:7050
    BatchTimeout: 2s
    BatchSize:
        MaxMessageCount: 10
        AbsoluteMaxBytes: 99 MB
        PreferredMaxBytes: 512 KB

    Kafka:
        Brokers:
            - 127.0.0.1:9092
    Organizations:
		# 创世区块的配置
    OneOrgOrdererGenesis:
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *OrdererOrg
        Consortiums:
            SampleConsortium:
                Organizations:
                    - *Org1
    # channel的配置
    OneOrgChannel:
        Consortium: SampleConsortium
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Org1

这里主要配置和网络交易相关的信息,包括排序节点,创世区块,Channel的配置,都可以在这个里面进行。

生成配置文件

我们可以一步一步来生成这个配置文件

执行这个步骤时,请务必保证first-network中的bin目录已经加入$PATH中了

生成证书等身份信息文件

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

根据配置文件生成对应的证书

生成创世区块

mkdir config
configtxgen -profile OneOrgOrdererGenesis -outputBlock ./config/genesis.block

创世区块的生成在当前目录的/config目录下,执行这个命令之前需要执行新建目录执行

生成Channel相关文件

configtxgen -profile OneOrgChannel -outputCreateChannelTx ./config/channel.tx -channelID $CHANNEL_NAME

这里的$CHANNEL_NAME在实际应用中可以修改,但是需要保持文件之间的一致性

生成锚节点相关文件

configtxgen -profile OneOrgChannel -outputAnchorPeersUpdate ./config/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP

使用一个脚本来代替刚刚的步骤

当然我们不会手动去一个一个执行这个命令,一个脚本能解决大部分问题

#!/bin/sh

export PATH=$GOPATH/src/github.com/hyperledger/fabric/build/bin:${PWD}/../bin:${PWD}:$PATH
export FABRIC_CFG_PATH=${PWD}
CHANNEL_NAME=fishchannel

# remove previous crypto material and config transactions
rm -fr config/*
rm -fr crypto-config/*

mkdir config

# generate crypto material
cryptogen generate --config=./crypto-config.yaml
if [ "$?" -ne 0 ]; then
  echo "Failed to generate crypto material..."
  exit 1
fi

# generate genesis block for orderer
configtxgen -profile OneOrgOrdererGenesis -outputBlock ./config/genesis.block
if [ "$?" -ne 0 ]; then
  echo "Failed to generate orderer genesis block..."
  exit 1
fi

# generate channel configuration transaction
configtxgen -profile OneOrgChannel -outputCreateChannelTx ./config/channel.tx -channelID $CHANNEL_NAME
if [ "$?" -ne 0 ]; then
  echo "Failed to generate channel configuration transaction..."
  exit 1
fi

# generate anchor peer transaction
configtxgen -profile OneOrgChannel -outputAnchorPeersUpdate ./config/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
if [ "$?" -ne 0 ]; then
  echo "Failed to generate anchor peer update for Org1MSP..."
  exit 1
fi

配置docker-compose.yml

这关乎我们能否成功搭建网络,我们需要通过这个文件来启动网络,这个的核心就是配置一些参数和映射,要保持和刚刚的几个文件相对应,后面如果出现错误有可能是这个没有对应上导致的

#
# Copyright IBM Corp All Rights Reserved
#
# SPDX-License-Identifier: Apache-2.0
#
version: '2'

networks:
  basic:

services:
  ca.example.com:
    image: hyperledger/fabric-ca
    environment:
      - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
      - FABRIC_CA_SERVER_CA_NAME=ca.example.com
      - FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
      - FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/26c967a4ac4d03185e92b58c0b01c580715890605e0627b5319ca0be78da80e2_sk
    # 主要注意这里的FABRIC_CA_SERVER_CA_KEYFILE,和对应目录下的CA密钥要能对应上
    ports:
      - "7054:7054"
    command: sh -c 'fabric-ca-server start -b admin:adminpw'
    volumes:
      - ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config/msp/keystore
    container_name: ca.example.com
    networks:
      - basic

  orderer.example.com:
    container_name: orderer.example.com
    image: hyperledger/fabric-orderer
    environment:
      - FABRIC_LOGGING_SPEC=info
      - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
      - ORDERER_GENERAL_GENESISMETHOD=file
      - ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/configtx/genesis.block
      - ORDERER_GENERAL_LOCALMSPID=OrdererMSP
      - ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/msp/orderer/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/orderer
    command: orderer
    ports:
      - 7050:7050
    volumes:
        - ./config/:/etc/hyperledger/configtx
        - ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/:/etc/hyperledger/msp/orderer
        - ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/:/etc/hyperledger/msp/peerOrg1
    networks:
      - basic

  peer0.org1.example.com:
    container_name: peer0.org1.example.com
    image: hyperledger/fabric-peer
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_PEER_ID=peer0.org1.example.com
      - FABRIC_LOGGING_SPEC=info
      - CORE_CHAINCODE_LOGGING_LEVEL=info
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/peer/
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      # # the following setting starts chaincode containers on the same
      # # bridge network as the peers
      - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_basic
      - CORE_LEDGER_STATE_STATEDATABASE=CouchDB
      - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984
      - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
      - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric
    # command: peer node start
    command: peer node start --peer-chaincodedev=true
    ports:
      - 7051:7051
      - 7053:7053
    volumes:
        - /var/run/:/host/var/run/
        - ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/msp/peer
        - ./crypto-config/peerOrganizations/org1.example.com/users:/etc/hyperledger/msp/users
        - ./config:/etc/hyperledger/configtx
    depends_on:
      - orderer.example.com
      - couchdb
    networks:
      - basic

  couchdb:
    container_name: couchdb
    image: hyperledger/fabric-couchdb
    # Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
    # for CouchDB.  This will prevent CouchDB from operating in an "Admin Party" mode.
    environment:
      - COUCHDB_USER=
      - COUCHDB_PASSWORD=
    ports:
      - 5984:5984
    networks:
      - basic

  cli:
    container_name: cli
    image: hyperledger/fabric-tools
    tty: true
    environment:
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - FABRIC_LOGGING_SPEC=info
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
      - CORE_CHAINCODE_KEEPALIVE=10
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: /bin/bash
    volumes:
        - /var/run/:/host/var/run/
        - ./../chaincode/:/opt/gopath/src/github.com/
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
    networks:
        - basic

编写启动脚本

#!/bin/bash
#
# Copyright IBM Corp All Rights Reserved
#
# SPDX-License-Identifier: Apache-2.0
#
# Exit on first error, print all commands.
set -ev

# don't rewrite paths for Windows Git Bash users
export MSYS_NO_PATHCONV=1

# 关闭正在运行的容器
docker-compose -f docker-compose.yml down

# 启动容器
docker-compose -f docker-compose.yml up -d ca.example.com orderer.example.com peer0.org1.example.com couchdb cli
docker ps -a

# wait for Hyperledger Fabric to start
# incase of errors when running later commands, issue export FABRIC_START_TIMEOUT=<larger number>
export FABRIC_START_TIMEOUT=10
#echo ${FABRIC_START_TIMEOUT}
sleep ${FABRIC_START_TIMEOUT}

# 创建频道
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c fishchannel -f /etc/hyperledger/configtx/channel.tx
# Join peer0.org1.example.com to the channel.
# 加入频道
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel join -b fishchannel.block

至此,总体上的网络配置基本结束

Trouble Shooting

一个成功的配置后的输出中所有镜像都是正常运行的

其中包括

  1. Fabric-ca
  2. peer
  3. orderer
  4. couchdb
  5. cli

如果有缺少的,那么需要通过修改配置 ,或者根据提示信息修改

在启动网络前,请务必清理干净docker容器中的镜像

使用docker stop $(docker ps -aq)docker rm $(docker ps -aq)移除全部镜像

编写一个简单的链码

网上的教程大多都是基于Gradle的,我使用maven较多,其实官方的例子里是有maven的例子的,我们拷贝下来就可以了

附上网址: https://github.com/hyperledger/fabric-chaincode-java

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>my_java_chaincode</groupId>
	<artifactId>my_java_chaincode</artifactId>
	<version>1.0-SNAPSHOT</version>
	<properties>

		<!-- Generic properties -->
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- fabric-chaincode-java -->
		<fabric-chaincode-java.version>1.4.4</fabric-chaincode-java.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

		<!-- Test -->
		<junit.version>4.11</junit.version>

	</properties>
	
    <repositories>
        <repository>
            <id>jitpack.io</id>
            <url>https://www.jitpack.io</url>
        </repository>
		<repository>
            <id>nexus</id>
            <url>https://nexus.hyperledger.org/content/repositories/snapshots/</url>
        </repository>
    </repositories>

	<dependencies>

		<!-- fabric-chaincode-java -->
		<dependency>
			<groupId>org.hyperledger.fabric-chaincode-java</groupId>
			<artifactId>fabric-chaincode-shim</artifactId>
			<version>${fabric-chaincode-java.version}</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.hyperledger.fabric-chaincode-java</groupId>
			<artifactId>fabric-chaincode-protos</artifactId>
			<version>${fabric-chaincode-java.version}</version>
			<scope>compile</scope>
		</dependency>


		<!-- fabric-sdk-java -->

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- Test Artifacts -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>


		<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.11.2</version>
		</dependency>



	</dependencies>
	<build>
		<sourceDirectory>src</sourceDirectory>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-shade-plugin</artifactId>
				<version>3.1.0</version>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>shade</goal>
						</goals>
						<configuration>
							<finalName>chaincode</finalName>
							<transformers>
								<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
									<mainClass>chaincode.example.SimpleChaincode</mainClass>
								</transformer>
							</transformers>
							<filters>
								<filter>
									<!-- filter out signature files from signed dependencies, else repackaging fails with security ex -->
									<artifact>*:*</artifact>
									<excludes>
										<exclude>META-INF/*.SF</exclude>
										<exclude>META-INF/*.DSA</exclude>
										<exclude>META-INF/*.RSA</exclude>
									</excludes>
								</filter>
							</filters>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>


</project>

这里我就添加了一个Jackson依赖,主要用于处理Json数据

编写链码

模型实体

/**
 * @author lucasgao
 */
public class Fish {
    private String vessel;
    private String location;
    private String time;
    private String holder;

    public Fish(String vessel, String location, String time, String holder) {
        this.vessel = vessel;
        this.location = location;
        this.time = time;
        this.holder = holder;
    }

    public String getVessel() {
        return vessel;
    }

    public void setVessel(String vessel) {
        this.vessel = vessel;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }

    public String getHolder() {
        return holder;
    }

    public void setHolder(String holder) {
        this.holder = holder;
    }

    @Override
    public String toString() {
        return "Fish{" +
                "vessel='" + vessel + '\'' +
                ", location='" + location + '\'' +
                ", time=" + time +
                ", holder='" + holder + '\'' +
                '}';
    }
}

这个当然是可以更换的,这里因为以渔业数据上链为背景

链码主体

/**
 * @author lucasgao
 */
public class SimpleChaincode extends ChaincodeBase {

	//日志
	private static Log _logger = LogFactory.getLog(SimpleChaincode.class);

  //Jackson核心对象
	public ObjectMapper objectMapper = new ObjectMapper();
	
  //初始化方法
	@Override
	public Response init(ChaincodeStub stub) {
		_logger.info("Init java simple ChainCode");
    //生成三条鱼
		ArrayList<Fish> fishList = new ArrayList<Fish>();
		fishList.add(new Fish("a1","67,-70","2020-9-1","111"));
		fishList.add(new Fish("b2","64,-70","2020-9-5","222"));
		fishList.add(new Fish("c3","67,-20","2020-9-2","333"));

		try {
      //逐个放入世界状态中
			for (int i = 0; i < fishList.size(); i++) {
				_logger.info("put:"+fishList.get(i));
        //这个是核心方法,根据键值的方式放入世界状态
				stub.putStringState("fish"+i, objectMapper.writeValueAsString(fishList.get(i)));
			}
		}catch (Exception e){
			e.printStackTrace();
		}
    //作出回应
		return newSuccessResponse("init success!!!");
	}

	@Override
	public Response invoke(ChaincodeStub stub) {
    //invoke方法,调用链码的时候会使用这个方法
		try {
			_logger.info("Invoke java simple ChainCode");
      //获取方法名
			String func = stub.getFunction();
      //获取参数
			List<String> params = stub.getParameters();
      //方法分发
			if ("invoke".equals(func)) {
				return invoke(stub, params);
			}
			if ("delete".equals(func)) {
				return delete(stub, params);
			}
			if ("query".equals(func)) {
				return query(stub, params);
			}
			return newErrorResponse("Invalid invoke function name. Expecting one of: [\"invoke\", \"delete\", \"query\"]");
		} catch (Throwable e) {
			return newErrorResponse(e+"\t function name:"+stub.getFunction());
		}
	}

  //重载方法,在世界状态中存入鱼
	private Response invoke(ChaincodeStub stub, List<String> args) {
		if (args.size() < 5) {
			return newErrorResponse("Incorrect number of arguments. Expecting 5, Invoke fail");
		}

		Fish fish = new Fish(args.get(1), args.get(2), args.get(3), args.get(4));
		_logger.info("add:  "+fish);
		try {
			stub.putStringState(args.get(0), objectMapper.writeValueAsString(fish));
		}catch (Exception e){
			e.printStackTrace();
		}
		return newSuccessResponse("invoke finished successfully");
	}

	// Deletes an entity from state
  // 删除鱼
	private Response delete(ChaincodeStub stub, List<String> args) {
		if (args.size() != 1) {
			return newErrorResponse("Incorrect number of arguments. Expecting 1");
		}
		String key = args.get(0);
		// Delete the key from the state in ledger
		stub.delState(key);
		return newSuccessResponse("delete success");
	}

	// query callback representing the query of a chaincode
  // 查询鱼
	private Response query(ChaincodeStub stub, List<String> args) {

		if (args.size() < 1) {
			return newErrorResponse("Incorrect number of arguments. Expecting 1");
		}
		String fishId = args.get(0);
		//从世界状态中查询
		String fishString = stub.getStringState(fishId);

		Fish fish = null;
		try{
			fish = objectMapper.readValue(fishString,Fish.class);
		}catch (Exception e){
			e.printStackTrace();
		}
		_logger.info("query response: "+fish);
		String val = "queryId:"+fishId+"              result:"+fishString+"";
		return newSuccessResponse("query success", ByteString.copyFrom(val, UTF_8).toByteArray());
	}
	
	public static void main(String[] args) {

		new SimpleChaincode().start(args);
	}
}

代码也是比较简单的,链码所写的就是业务逻辑相关的一些信息,区块链就像一个数据库可以实现查询,删除等操作

部署链码

我们可以编写一个脚本部署链码,但是在此之前,我建议先手动部署一下链码试试看

首先先进入cli中

docker exec -it cli bash

然后先安装链码,因为我们只有一个peer结点,只需要安装一遍

peer chaincode install -p /opt/gopath/src/github.com/test1 -v 1.0 -n mycc -l java

其中-p后面的跟路径的,-v是版本,-n是链码名称,-l是语言

然后我们实例化链码,这一步等待的时间可能比较长

peer chaincode instantiate -n mycc -v 1.0 -c '{"Args":[]}' -C fishchannel -o orderer.example.com:7050

然后我们就可以去测试一下链码了

peer chaincode invoke -n mycc -c '{"Args":["query","fish0"]}' -C fishchannel

如果没有报错,那么就成功了

编写一个类似byfn.sh的脚本

#!/bin/bash
#
# Copyright IBM Corp All Rights Reserved
#
# SPDX-License-Identifier: Apache-2.0
#
# Exit on first error
set -e

# don't rewrite paths for Windows Git Bash users
export MSYS_NO_PATHCONV=1
starttime=$(date +%s)
CC_SRC_LANGUAGE=java
CC_SRC_LANGUAGE=`echo "$CC_SRC_LANGUAGE" | tr [:upper:] [:lower:]`

CC_RUNTIME_LANGUAGE=java
CC_SRC_PATH=/opt/gopath/src/github.com/test1

# clean the keystore
rm -rf ./hfc-key-store

# launch network; create channel and join peer to channel
cd basic-network
echo y | ./stop.sh
./start.sh

CONFIG_ROOT=/opt/gopath/src/github.com/hyperledger/fabric/peer
ORG1_MSPCONFIGPATH=${CONFIG_ROOT}/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
ORG1_TLS_ROOTCERT_FILE=${CONFIG_ROOT}/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
ORG2_MSPCONFIGPATH=${CONFIG_ROOT}/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
ORG2_TLS_ROOTCERT_FILE=${CONFIG_ROOT}/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
ORDERER_TLS_ROOTCERT_FILE=${CONFIG_ROOT}/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
set -x

echo "Installing smart contract on peer0.org1.example.com"
docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_ADDRESS=peer0.org1.example.com:7051 \
  -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \
  -e CORE_PEER_TLS_ROOTCERT_FILE=${ORG1_TLS_ROOTCERT_FILE} \
  cli \
  peer chaincode install \
    -n mycc \
    -v 1.0 \
    -p "$CC_SRC_PATH" \
    -l "$CC_RUNTIME_LANGUAGE"

sleep 20

echo "Instantiating smart contract on mychannel"
docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \
  cli \
  peer chaincode instantiate \
    -o orderer.example.com:7050 \
    -C fishchannel \
    -n mycc \
    -l "$CC_RUNTIME_LANGUAGE" \
    -v 1.0 \
    -c '{"Args":[]}' 


echo "Waiting for instantiation request to be committed ..."
# sleep 10

echo "Submitting initLedger transaction to smart contract on mychannel"
echo "                                                             "
sleep 10


docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \
  cli \
  peer chaincode invoke \
    -o orderer.example.com:7050 \
    -C fishchannel \
    -n mycc \
    -c '{"function":"query","Args":["fish0"]}' 
set +x

echo "                                                             "

docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \
  cli \
  peer chaincode invoke \
    -o orderer.example.com:7050 \
    -C fishchannel \
    -n mycc \
    -c '{"function":"query","Args":["fish1"]}' 

set +x

echo "                                                             "

docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \
  cli \
  peer chaincode invoke \
    -o orderer.example.com:7050 \
    -C fishchannel \
    -n mycc \
    -c '{"function":"query","Args":["fish2"]}' 
set +x

# 测试向世界状态写数据
docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \
  cli \
  peer chaincode invoke \
    -o orderer.example.com:7050 \
    -C fishchannel \
    -n mycc \
    -c '{"function":"invoke","Args":["fish4","d4","90,23","2020-9-9","lucas"]}' 
set +x

echo "                                                                "

sleep 30

docker exec \
  -e CORE_PEER_LOCALMSPID=Org1MSP \
  -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \
  cli \
  peer chaincode invoke \
    -o orderer.example.com:7050 \
    -C fishchannel \
    -n mycc \
    -c '{"function":"query","Args":["fish4"]}' 

set +x

cat <<EOF

Total setup execution time : $(($(date +%s) - starttime)) secs ...

all good!!!!!!!!!!!

EOF

总结

在编写Fabric的时候遇到了很多坑,因为很多错误都不能人性化地显示出来,我们只能逐个去分析,先保证这个网络能跑通,再去部署链码