Hyperledger Fabric定制联盟链网络工程实践
Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475
前言
总体来看,网络上成体系的可用的 Fabric 教程极少——不是直接在 Fabric 官网复制内容大谈基础理论就是在描述一个几乎无法复现的项目实践,以至于学习 Fabric 的效率极低,印象最深刻的就是我曾经花费几天时间尝试按照官方教程 CA Deployment steps 搭建自己的 CA 服务,却始终无法成功也找不到原因。因此,为了提高生产效率,本项目虚拟了一个工作室联盟链需求并将逐步实现,致力于提供一个易理解、可复现的Fabric学习项目(尽管它比较简单),其中项目部署步骤的各个环节都清晰可见,并且将所有过程打包为脚本使之能够被快速复现在任何一台主机上。
工程介绍
组织架构
有一启明星工作室,其中包含三大组织:软件组、WEB组、硬件组、理事会,不同组织间相互独立,偶尔有业务往来。现理事会决定搭建一个启明星工作室的联盟链网络,使不同组织间加强合作,期望最终实现以下工程架构:
- 组织说明
council
:理事会,负责工作室各组间协调管理,由三组抽调人员共同组成soft
:软件组,专注软件开发hard
:硬件组,专注硬件开发web
:WEB组,专注网站开发orderer
:过渡排序组织,为联盟链网络提供排序服务,后期会舍弃
- 成员说明
council
:一个Orderer节点、三个Admin账号,每组拥有一个Admin账号权限soft
:一个Orderer节点、一个Peer节点、一个Admin账号、一个User账号hard
:一个Orderer节点、一个Peer节点、一个Admin账号、一个User账号web
:一个Orderer节点、一个Peer节点、一个Admin账号、一个User账号orderer
:一个Orderer节点、一个Admin账号
- 根CA服务器(域名)
council.fantasy.com
:提供/管理组织间的TLS
证书,又叫TLS CA服务器soft.fantasy.com
:提供/管理组织内TLS
证书hard.fantasy.com
:提供/管理组织内TLS
证书web.fantasy.com
:提供/管理组织内TLS
证书orderer.fantasy.com
:提供/管理组织内TLS
证书
实验准备
在开始前,如果你对 Fabric 命令知之甚少,可以先学习fabric的test-network启动过程Bash源码详解;此外应准备好 Fabric 的开发环境,具体环境搭建和软件版本可参考基于Debian搭建Hyperledger Fabric 2.4开发环境及运行简单案例。为了方便区分各组织和节点,本工程使用域名的方式进行各节点间的通信,以 web 组织为例,域名分配规范如下:
域名 | 说明 |
---|---|
peer1.web.ifantasy.net
|
web组第一个 peer 节点地址 |
peer2.web.ifantasy.net
|
web组第二个 peer 节点地址 |
orderer1.web.ifantasy.net
|
web组第一个 orderer 节点地址 |
此外,还需要在 \etc\hosts
文件添加 DNS
地址:
bash
echo "127.0.0.1 council.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 orderer.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 soft.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 web.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 hard.ifantasy.net" >> /etc/hostsecho "127.0.0.1 orderer1.soft.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 orderer1.web.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 orderer1.hard.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 orderer1.orderer.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 orderer2.orderer.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 orderer3.orderer.ifantasy.net" >> /etc/hostsecho "127.0.0.1 peer1.soft.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 peer1.web.ifantasy.net" >> /etc/hosts
echo "127.0.0.1 peer1.hard.ifantasy.net" >> /etc/hosts
本文工作
本项目主要以学习为主,所以并未期望一次实现所有架构和功能。本文所实现的具体内容为,搭建一个简单的工作室联盟链网络,包含 council
、 orderer
、 soft
、 web
四个组织,并将测试链码部署在通道 mychannel
,网络结构为(实验代码已上传至:https://github.com/wefantasy/FabricLearn 的 1_3Org2Peer1Orderer1TLS 目录下):
项 | 运行端口 | 说明 |
---|---|---|
council.ifantasy.net
|
7050 | council 组织的 CA 服务, 为联盟链网络提供 TLS-CA 服务 |
orderer.ifantasy.net
|
7150 | orderer 组织的 CA 服务, 为联盟链网络提供排序服务 |
orderer1.orderer.ifantasy.net
|
7151 | orderer 组织的 orderer1 成员节点 |
soft.ifantasy.net
|
7250 | soft 组织的 CA 服务, 包含成员: peer1 、 admin1 |
peer1.soft.ifantasy.net
|
7251 | soft 组织的 peer1 成员节点 |
web.ifantasy.net
|
7350 | web 组织的 CA 服务, 包含成员: peer1 、 admin1 |
peer1.web.ifantasy.net
|
7351 | web 组织的 peer1 成员节点 |
其它说明
个人觉得 Fabric 官方示例的证书结构过于冗余,为了便于自己理解,本工程证书结构跟一般 Fabric 有所出入,先将本工程各文件目录说明如下:
mipsasm
1\_3Org2Peer1Orderer1TLS
├── 0\_Restart.sh # 启动基本 CA 网络脚本
├── 1\_RegisterUser.sh # 注册账户脚本
├── 2\_EnrollUser.sh # 登录账户脚本
├── 3\_Configtxgen.sh # 生成创世区块脚本
├── 4\_TestChaincode.sh # 链码测试脚本
├── asset-transfer-basic # 测试链码目录
├── basic.tar.gz # 打包后的链码包
├── compose # Docker配置目录
│ ├── docker-base.yaml # 基础通用配置
│ └── docker-compose.yaml # 具体 Docker 配置
├── config # Fabric 公共配置目录
│ ├── config-msp.yaml # 节点组织单元配置文件
│ ├── configtx.yaml # 初始通道配置
│ ├── orderer.yaml # orderer 节点配置,osnadmin 的配置文件
│ └── core.yaml # peer 配置
├── data # 临时数据目录
├── envpeer1soft # soft 组织的 peer1 cli环境变量
├── envpeer1web # web 组织的peer1 cli环境变量
├── orgs # 组织成员证书目录
│ ├── council.ifantasy.net # council 组织目录
│ ├── orderer.ifantasy.net # orderer 组织目录
│ ├── web.ifantasy.net # web组织目录
│ └── soft.ifantasy.net # soft 组织目录
│ ├── assets # 组织公共材料目录
│ │ ├── ca-cert.pem # 本组织根证书
│ │ ├── mychannel.block # mychannel 通道创世区块
│ │ └── tls-ca-cert.pem # TLS-CA 服务根证书
│ ├── ca # 本组织 CA 服务目录
│ │ ├── admin # 本组织 CA 服务引导管理员 msp 目录
│ │ └── crypto # 本组织 CA 服务默认证书目录
│ ├── msp # 组织 MSP 目录
│ │ ├── admincerts # 组织管理员签名证书目录
│ │ ├── cacerts # 组织 CA 服务根证书目录
│ │ ├── config.yaml # 组织节点单元配置文件
│ │ ├── tlscacerts # TLS-CA 服务根证书目录
│ │ └── users # 空目录,msp 规范所需
│ └── registers # 本组织注册的账户目录
│ ├── admin1 # 管理员账户
│ └── peer1 # 节点账户
└── README.md
实验步骤
实验准备
首先将基于Debian搭建Hyperledger Fabric 2.4开发环境及运行简单案例中的/usr/local/fabric/config
目录复制到根目录下。如无特殊说明,环境变量FABRIC_CFG_PATH
总是默认指向根目录的config
目录(建议直接将本案例仓库 FabricLearn 下的 1_3Org2Peer1Orderer1TLS
目录拷贝到本地运行)。
fabric 提供一个 fabric-tools
镜像用于提供操作 peer
节点的命令行,其实现方式是在启动 fabric-tools
时指定 peer
节点的身份证书等环境变量,此外我们也可以直接将这些环境变量写入一个文件中然后通过 source
命令激活。在根目录下创建 envpeer1soft
文件,用于保存 soft
组织的环境变量,写入以下内容:
yaml
export LOCAL\_ROOT\_PATH=$PWD
export LOCAL\_CA\_PATH=$LOCAL\_ROOT\_PATH/orgs
export DOCKER\_CA\_PATH=/tmp
export COMPOSE\_PROJECT\_NAME=fabriclearn
export DOCKER\_NETWORKS=network
export FABRIC\_BASE\_VERSION=2.4
export FABRIC\_CA\_VERSION=1.5
配置TLS服务
- 在根目录下创建
compose
文件夹,用于储存docker-compose
配置文件。 - 在
compose
下创建docker-base.yaml
文件,用于写入公共服务配置,先写入以下内容:
yaml
version: "2"services:ca-base:image: hyperledger/fabric-ca:${FABRIC\_CA\_VERSION}environment:- FABRIC\_CA\_SERVER\_HOME=${DOCKER\_CA\_PATH}/ca/crypto- FABRIC\_CA\_SERVER\_TLS\_ENABLED=true- FABRIC\_CA\_SERVER\_DEBUG=truenetworks:- ${DOCKER\_NETWORKS}
- 在
compose
下创建docker-compose.yaml
文件,用于配置工程 docker 容器,写入council
、orderer
、soft
、web
的 TLS 服务配置:
yaml
version: '2'
networks:network:services:council.ifantasy.net:container\_name: council.ifantasy.netextends:file: docker-base.yamlservice: ca-basecommand: sh -c 'fabric-ca-server start -d -b ca-admin:ca-adminpw --port 7050'environment:- FABRIC\_CA\_SERVER\_CSR\_CN=council.ifantasy.net- FABRIC\_CA\_SERVER\_CSR\_HOSTS=council.ifantasy.netvolumes:- ${LOCAL\_CA\_PATH}/council.ifantasy.net/ca:${DOCKER\_CA\_PATH}/caports:- 7050:7050orderer.ifantasy.net:container\_name: orderer.ifantasy.netextends:file: docker-base.yamlservice: ca-basecommand: sh -c 'fabric-ca-server start -d -b ca-admin:ca-adminpw --port 7050'environment:- FABRIC\_CA\_SERVER\_CSR\_CN=orderer.ifantasy.net- FABRIC\_CA\_SERVER\_CSR\_HOSTS=orderer.ifantasy.netvolumes:- ${LOCAL\_CA\_PATH}/orderer.ifantasy.net/ca:${DOCKER\_CA\_PATH}/caports:- 7150:7050soft.ifantasy.net:container\_name: soft.ifantasy.netextends:file: docker-base.yamlservice: ca-basecommand: sh -c 'fabric-ca-server start -d -b ca-admin:ca-adminpw --port 7050'environment:- FABRIC\_CA\_SERVER\_CSR\_CN=soft.ifantasy.net- FABRIC\_CA\_SERVER\_CSR\_HOSTS=soft.ifantasy.netvolumes:- ${LOCAL\_CA\_PATH}/soft.ifantasy.net/ca:${DOCKER\_CA\_PATH}/caports:- 7250:7050web.ifantasy.net:container\_name: web.ifantasy.netextends:file: docker-base.yamlservice: ca-basecommand: sh -c 'fabric-ca-server start -d -b ca-admin:ca-adminpw --port 7050'environment:- FABRIC\_CA\_SERVER\_CSR\_CN=web.ifantasy.net- FABRIC\_CA\_SERVER\_CSR\_HOSTS=web.ifantasy.netvolumes:- ${LOCAL\_CA\_PATH}/web.ifantasy.net/ca:${DOCKER\_CA\_PATH}/caports:- 7350:7050
- 启动各组织的 TLS 服务:
yaml
source envpeer1soft
docker-compose -f $LOCAL\_ROOT\_PATH/compose/docker-compose.yaml up -d council.ifantasy.net orderer.ifantasy.net soft.ifantasy.net web.ifantasy.net
注册账户
- 注册
council
组织账户。
首先设置council
的环境变量:
bash
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/council.ifantasy.net/ca/crypto/ca-cert.pem
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/council.ifantasy.net/ca/admin
然后使用 enroll
登录引导账户, 它会以 FABRIC_CA_CLIENT_TLS_CERTFILES
指向的 CA 服务器根证书加密通信,并将生成的身份证书保存在 FABRIC_CA_CLIENT_HOME
指向的工作目录下[1]:
bash
fabric-ca-client enroll -d -u https://ca-admin:ca-adminpw@council.ifantasy.net:7050
注意:
enroll
操作结果是保存账号的身份证书至指定目录下,以后就可以直接根据该目录下的证书来使用对应身份,跟传统 web 系统的登录操作类似所以被我称为登录,但实际上有所区别。
然后便可以 ca-admin
身份进行注册其它用户的操作:
bash
fabric-ca-client register -d --id.name orderer1 --id.secret orderer1 --id.type orderer -u https://council.ifantasy.net:7050
fabric-ca-client register -d --id.name peer1soft --id.secret peer1soft --id.type peer -u https://council.ifantasy.net:7050
fabric-ca-client register -d --id.name peer1web --id.secret peer1web --id.type peer -u https://council.ifantasy.net:7050
council
为其它组织提供 TLS-CA 服务的具体实现就是为其它组织提供 council
可验证的合法账户,其他组织使用这些账户进行通信就是可信的。后面注册步骤与上面类似,故不再赘述。
2. 注册 orderer
组织账户:
bash
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/orderer.ifantasy.net/ca/crypto/ca-cert.pem
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/orderer.ifantasy.net/ca/admin
fabric-ca-client enroll -d -u https://ca-admin:ca-adminpw@orderer.ifantasy.net:7150
fabric-ca-client register -d --id.name orderer1 --id.secret orderer1 --id.type orderer -u https://orderer.ifantasy.net:7150
fabric-ca-client register -d --id.name admin1 --id.secret admin1 --id.type admin -u https://orderer.ifantasy.net:7150
- 注册
soft
组织账户:
bash
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/soft.ifantasy.net/ca/crypto/ca-cert.pem
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/soft.ifantasy.net/ca/admin
fabric-ca-client enroll -d -u https://ca-admin:ca-adminpw@soft.ifantasy.net:7250
fabric-ca-client register -d --id.name peer1 --id.secret peer1 --id.type peer -u https://soft.ifantasy.net:7250
fabric-ca-client register -d --id.name admin1 --id.secret admin1 --id.type admin -u https://soft.ifantasy.net:7250
- 注册
web
组织账户:
bash
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/web.ifantasy.net/ca/crypto/ca-cert.pem
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/web.ifantasy.net/ca/admin
fabric-ca-client enroll -d -u https://ca-admin:ca-adminpw@web.ifantasy.net:7350
fabric-ca-client register -d --id.name peer1 --id.secret peer1 --id.type peer -u https://web.ifantasy.net:7350
fabric-ca-client register -d --id.name admin1 --id.secret admin1 --id.type admin -u https://web.ifantasy.net:7350
构造组织成员证书
- 在各组织下创建
assets
目录,用于储存本组织根证书和用于组间通信的 LTS-CA 根证书:
bash
mkdir -p $LOCAL\_CA\_PATH/orderer.ifantasy.net/assets
cp $LOCAL\_CA\_PATH/orderer.ifantasy.net/ca/crypto/ca-cert.pem $LOCAL\_CA\_PATH/orderer.ifantasy.net/assets/ca-cert.pem
cp $LOCAL\_CA\_PATH/council.ifantasy.net/ca/crypto/ca-cert.pem $LOCAL\_CA\_PATH/orderer.ifantasy.net/assets/tls-ca-cert.pemmkdir -p $LOCAL\_CA\_PATH/soft.ifantasy.net/assets
cp $LOCAL\_CA\_PATH/soft.ifantasy.net/ca/crypto/ca-cert.pem $LOCAL\_CA\_PATH/soft.ifantasy.net/assets/ca-cert.pem
cp $LOCAL\_CA\_PATH/council.ifantasy.net/ca/crypto/ca-cert.pem $LOCAL\_CA\_PATH/soft.ifantasy.net/assets/tls-ca-cert.pemmkdir -p $LOCAL\_CA\_PATH/web.ifantasy.net/assets
cp $LOCAL\_CA\_PATH/web.ifantasy.net/ca/crypto/ca-cert.pem $LOCAL\_CA\_PATH/web.ifantasy.net/assets/ca-cert.pem
cp $LOCAL\_CA\_PATH/council.ifantasy.net/ca/crypto/ca-cert.pem $LOCAL\_CA\_PATH/web.ifantasy.net/assets/tls-ca-cert.pem
- 构造
orderer
组织成员证书。
登录orderer
管理员账户admin1
:
bash
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/admin1
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/orderer.ifantasy.net/assets/ca-cert.pem
export FABRIC\_CA\_CLIENT\_MSPDIR=msp
fabric-ca-client enroll -d -u https://admin1:admin1@orderer.ifantasy.net:7150
注意:这里是登录上节我们注册的管理员账户而非启动
CA
服务时的引导账户,引导账户跟管理员账户的区别也是我至今难以理解的地方
以上命令成功后便可以看到 FABRIC_CA_CLIENT_HOME/FABRIC_CA_CLIENT_MSPDIR
目录下生成的证书文件。然后需要构造 admin1
的 msp
目录:
bash
mkdir -p $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/admin1/msp/admincerts
cp $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/admin1/msp/admincerts/cert.pem
这里的操作仅仅是将 admin1
的签名证书复制到新建的 admincerts
文件夹下,这样做的原因是 Fabric 的 MSP 规范要求其下需有 admincerts
目录,否则后面操作组织 peer
节点时会报错,因此建议在所有联盟链网络服务节点的 msp 目录下添加 admincerts 证书。然后登录 orderer
的 orderer1
的组织内账户:
bash
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/orderer1
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/orderer.ifantasy.net/assets/ca-cert.pem
export FABRIC\_CA\_CLIENT\_MSPDIR=msp
fabric-ca-client enroll -d -u https://orderer1:orderer1@orderer.ifantasy.net:7150
mkdir -p $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/orderer1/msp/admincerts
cp $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/orderer1/msp/admincerts/cert.pem
然后登录 orderer
的 orderer1
的组织间 TLS-CA 账户:
bash
export FABRIC\_CA\_CLIENT\_MSPDIR=tls-msp
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/orderer.ifantasy.net/assets/tls-ca-cert.pem
fabric-ca-client enroll -d -u https://orderer1:orderer1@council.ifantasy.net:7050 --enrollment.profile tls --csr.hosts orderer1.orderer.ifantasy.net
cp $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/orderer1/tls-msp/keystore/*\_sk $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/orderer1/tls-msp/keystore/key.pem
最后一步便是构造 orderer
的组织 MSP 目录[2](MSP 目录说明可参考 MSP结构):
bash
mkdir -p $LOCAL\_CA\_PATH/orderer.ifantasy.net/msp/admincerts
mkdir -p $LOCAL\_CA\_PATH/orderer.ifantasy.net/msp/cacerts
mkdir -p $LOCAL\_CA\_PATH/orderer.ifantasy.net/msp/tlscacerts
mkdir -p $LOCAL\_CA\_PATH/orderer.ifantasy.net/msp/users
cp $LOCAL\_CA\_PATH/orderer.ifantasy.net/assets/ca-cert.pem $LOCAL\_CA\_PATH/orderer.ifantasy.net/msp/cacerts/
cp $LOCAL\_CA\_PATH/orderer.ifantasy.net/assets/tls-ca-cert.pem $LOCAL\_CA\_PATH/orderer.ifantasy.net/msp/tlscacerts/
cp $LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/orderer.ifantasy.net/msp/admincerts/cert.pem
cp $LOCAL\_ROOT\_PATH/config/config-msp.yaml $LOCAL\_CA\_PATH/orderer.ifantasy.net/msp/config.yaml
上面命令最后一行会在组织 msp
目录下添加节点组织单元配置文件 config.yaml
,其原理可以参考 节点组织单元和MSP ,如果缺少该文件或者文件内容错误,会报以下错误:
bash
loadLocalMSP -> Failed to setup local msp with config: administrators must be declared when no admin ou classification is set
后面的流程跟这里类似,因此不再赘述。
3. 构造 soft
组织成员证书:
bash
echo "Start Soft============================="
echo "Enroll Admin"
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/soft.ifantasy.net/registers/admin1
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/soft.ifantasy.net/assets/ca-cert.pem
export FABRIC\_CA\_CLIENT\_MSPDIR=msp
fabric-ca-client enroll -d -u https://admin1:admin1@soft.ifantasy.net:7250
mkdir -p $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/admin1/msp/admincerts
cp $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/admin1/msp/admincerts/cert.pemecho "Enroll Peer1"
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/soft.ifantasy.net/registers/peer1
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/soft.ifantasy.net/assets/ca-cert.pem
export FABRIC\_CA\_CLIENT\_MSPDIR=msp
fabric-ca-client enroll -d -u https://peer1:peer1@soft.ifantasy.net:7250
# for TLS
export FABRIC\_CA\_CLIENT\_MSPDIR=tls-msp
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/soft.ifantasy.net/assets/tls-ca-cert.pem
fabric-ca-client enroll -d -u https://peer1soft:peer1soft@council.ifantasy.net:7050 --enrollment.profile tls --csr.hosts peer1.soft.ifantasy.net
cp $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/peer1/tls-msp/keystore/*\_sk $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/peer1/tls-msp/keystore/key.pem
mkdir -p $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/peer1/msp/admincerts
cp $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/peer1/msp/admincerts/cert.pemmkdir -p $LOCAL\_CA\_PATH/soft.ifantasy.net/msp/admincerts
mkdir -p $LOCAL\_CA\_PATH/soft.ifantasy.net/msp/cacerts
mkdir -p $LOCAL\_CA\_PATH/soft.ifantasy.net/msp/tlscacerts
mkdir -p $LOCAL\_CA\_PATH/soft.ifantasy.net/msp/users
cp $LOCAL\_CA\_PATH/soft.ifantasy.net/assets/ca-cert.pem $LOCAL\_CA\_PATH/soft.ifantasy.net/msp/cacerts/
cp $LOCAL\_CA\_PATH/soft.ifantasy.net/assets/tls-ca-cert.pem $LOCAL\_CA\_PATH/soft.ifantasy.net/msp/tlscacerts/
cp $LOCAL\_CA\_PATH/soft.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/soft.ifantasy.net/msp/admincerts/cert.pem
cp $LOCAL\_ROOT\_PATH/config/config-msp.yaml $LOCAL\_CA\_PATH/soft.ifantasy.net/msp/config.yaml
echo "End Soft============================="
- 构造
web
组织成员证书:
bash
echo "Start Web============================="
echo "Enroll Admin"
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/web.ifantasy.net/registers/admin1
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/web.ifantasy.net/assets/ca-cert.pem
export FABRIC\_CA\_CLIENT\_MSPDIR=msp
fabric-ca-client enroll -d -u https://admin1:admin1@web.ifantasy.net:7350
mkdir -p $LOCAL\_CA\_PATH/web.ifantasy.net/registers/admin1/msp/admincerts
cp $LOCAL\_CA\_PATH/web.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/web.ifantasy.net/registers/admin1/msp/admincerts/cert.pemecho "Enroll Peer1"
# for identity
export FABRIC\_CA\_CLIENT\_HOME=$LOCAL\_CA\_PATH/web.ifantasy.net/registers/peer1
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/web.ifantasy.net/assets/ca-cert.pem
export FABRIC\_CA\_CLIENT\_MSPDIR=msp
fabric-ca-client enroll -d -u https://peer1:peer1@web.ifantasy.net:7350
# for TLS
export FABRIC\_CA\_CLIENT\_MSPDIR=tls-msp
export FABRIC\_CA\_CLIENT\_TLS\_CERTFILES=$LOCAL\_CA\_PATH/web.ifantasy.net/assets/tls-ca-cert.pem
fabric-ca-client enroll -d -u https://peer1web:peer1web@council.ifantasy.net:7050 --enrollment.profile tls --csr.hosts peer1.web.ifantasy.net
cp $LOCAL\_CA\_PATH/web.ifantasy.net/registers/peer1/tls-msp/keystore/*\_sk $LOCAL\_CA\_PATH/web.ifantasy.net/registers/peer1/tls-msp/keystore/key.pem
mkdir -p $LOCAL\_CA\_PATH/web.ifantasy.net/registers/peer1/msp/admincerts
cp $LOCAL\_CA\_PATH/web.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/web.ifantasy.net/registers/peer1/msp/admincerts/cert.pemmkdir -p $LOCAL\_CA\_PATH/web.ifantasy.net/msp/admincerts
mkdir -p $LOCAL\_CA\_PATH/web.ifantasy.net/msp/cacerts
mkdir -p $LOCAL\_CA\_PATH/web.ifantasy.net/msp/tlscacerts
mkdir -p $LOCAL\_CA\_PATH/web.ifantasy.net/msp/users
cp $LOCAL\_CA\_PATH/web.ifantasy.net/assets/ca-cert.pem $LOCAL\_CA\_PATH/web.ifantasy.net/msp/cacerts/
cp $LOCAL\_CA\_PATH/web.ifantasy.net/assets/tls-ca-cert.pem $LOCAL\_CA\_PATH/web.ifantasy.net/msp/tlscacerts/
cp $LOCAL\_CA\_PATH/web.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL\_CA\_PATH/web.ifantasy.net/msp/admincerts/cert.pem
cp $LOCAL\_ROOT\_PATH/config/config-msp.yaml $LOCAL\_CA\_PATH/web.ifantasy.net/msp/config.yaml
echo "End Web============================="
配置系统通道及测试通道
- 在
config
目录下创建configtx.yaml
配置文件,文件太长在此不做展示,可以在 FabricLearn 中查看,其中各项已加说明注释[^3]。 - 通过
configtxgen
生成创世区块和测试通道:bash
configtxgen -profile OrgsOrdererGenesis -outputBlock $LOCAL\_ROOT\_PATH/data/genesis.block -channelID syschannel
configtxgen -profile OrgsChannel -outputCreateChannelTx $LOCAL\_ROOT\_PATH/data/mychannel.tx -channelID mychannel
- 在
compose/docker-compose.yaml
文件中添加peer
和orderer
服务相关配置:yaml
peer1.soft.ifantasy.net:container\_name: peer1.soft.ifantasy.netextends:file: docker-base.yamlservice: peer-baseenvironment:- CORE\_PEER\_ID=peer1.soft.ifantasy.net- CORE\_PEER\_ADDRESS=peer1.soft.ifantasy.net:7051- CORE\_PEER\_LOCALMSPID=softMSP- CORE\_PEER\_GOSSIP\_EXTERNALENDPOINT=peer1.soft.ifantasy.net:7051volumes:- ${LOCAL\_CA\_PATH}/soft.ifantasy.net/registers/peer1:${DOCKER\_CA\_PATH}/peerports:- 7251:7051
peer1.web.ifantasy.net:container\_name: peer1.web.ifantasy.netextends:file: docker-base.yamlservice: peer-baseenvironment:- CORE\_PEER\_ID=peer1.web.ifantasy.net- CORE\_PEER\_ADDRESS=peer1.web.ifantasy.net:7051- CORE\_PEER\_LOCALMSPID=webMSP- CORE\_PEER\_GOSSIP\_EXTERNALENDPOINT=peer1.web.ifantasy.net:7051volumes:- ${LOCAL\_CA\_PATH}/web.ifantasy.net/registers/peer1:${DOCKER\_CA\_PATH}/peerports:- 7351:7051
orderer1.orderer.ifantasy.net:container\_name: orderer1.orderer.ifantasy.netextends:file: docker-base.yamlservice: orderer-baseenvironment:- ORDERER\_HOST=orderer1.orderer.ifantasy.net- ORDERER\_GENERAL\_LOCALMSPID=ordererMSPvolumes:- ${LOCAL\_CA\_PATH}/orderer.ifantasy.net/registers/orderer1:${DOCKER\_CA\_PATH}/orderer- ${LOCAL\_ROOT\_PATH}/data/genesis.block:${DOCKER\_CA\_PATH}/orderer/genesis.blockports:- 7151:7777
- 启动
peer
和orderer
服务:bash
docker-compose -f $LOCAL\_ROOT\_PATH/compose/docker-compose.yaml up -d peer1.soft.ifantasy.net peer1.web.ifantasy.net orderer1.orderer.ifantasy.net
此时我们已经启动了所有联盟链网络所需的容器如下:bash
(base) root@DebianA:1\_3Org2Peer1Orderer1TLS# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c59b88fa847 hyperledger/fabric-peer:2.4 "peer node start" 8 seconds ago Up 6 seconds 0.0.0.0:7251->7051/tcp peer1.soft.ifantasy.net
3906338f6861 hyperledger/fabric-peer:2.4 "peer node start" 8 seconds ago Up 6 seconds 0.0.0.0:7351->7051/tcp peer1.web.ifantasy.net
9f127a054343 hyperledger/fabric-orderer:2.4 "orderer" 8 seconds ago Up 6 seconds 7050/tcp, 0.0.0.0:7151->7777/tcp orderer1.orderer.ifantasy.net
949abd8f7070 hyperledger/fabric-ca:1.5 "sh -c 'fabric-ca-se…" About an hour ago Up About an hour 7054/tcp, 0.0.0.0:7150->7050/tcp orderer.ifantasy.net
011fe2b36c01 hyperledger/fabric-ca:1.5 "sh -c 'fabric-ca-se…" About an hour ago Up About an hour 0.0.0.0:7050->7050/tcp, 7054/tcp council.ifantasy.net
207879f5bb33 hyperledger/fabric-ca:1.5 "sh -c 'fabric-ca-se…" About an hour ago Up About an hour 7054/tcp, 0.0.0.0:7350->7050/tcp web.ifantasy.net
d1850c86e096 hyperledger/fabric-ca:1.5 "sh -c 'fabric-ca-se…" About an hour ago Up About an hour 7054/tcp, 0.0.0.0:7250->7050/tcp soft.ifantasy.net
- 补全根目录中
envpeer1soft
的环境变量:bash
export LOCAL\_ROOT\_PATH=$PWD
export LOCAL\_CA\_PATH=$LOCAL\_ROOT\_PATH/orgs
export DOCKER\_CA\_PATH=/tmp
export COMPOSE\_PROJECT\_NAME=fabriclearn
export DOCKER\_NETWORKS=network
export FABRIC\_BASE\_VERSION=2.4
export FABRIC\_CA\_VERSION=1.5
echo "init terminal soft"
export FABRIC\_CFG\_PATH=$LOCAL\_ROOT\_PATH/config
export CORE\_PEER\_TLS\_ENABLED=true
export CORE\_PEER\_LOCALMSPID="softMSP"
export CORE\_PEER\_ADDRESS=peer1.soft.ifantasy.net:7251
export CORE\_PEER\_TLS\_ROOTCERT\_FILE=$LOCAL\_CA\_PATH/soft.ifantasy.net/assets/tls-ca-cert.pem
export CORE\_PEER\_MSPCONFIGPATH=$LOCAL\_CA\_PATH/soft.ifantasy.net/registers/admin1/msp
export ORDERER\_CA=$LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/orderer1/tls-msp/tlscacerts/tls-council-ifantasy-net-7050.pem
- 复制
envpeer1soft
为envpeer1web
作为 web 组织的环境变量:bash
export LOCAL\_ROOT\_PATH=$PWD
export LOCAL\_CA\_PATH=$LOCAL\_ROOT\_PATH/orgs
export DOCKER\_CA\_PATH=/tmp
export COMPOSE\_PROJECT\_NAME=fabriclearn
export DOCKER\_NETWORKS=network
export FABRIC\_BASE\_VERSION=2.4
export FABRIC\_CA\_VERSION=1.5
echo "init terminal web"
export FABRIC\_CFG\_PATH=$LOCAL\_ROOT\_PATH/config
export CORE\_PEER\_TLS\_ENABLED=true
export CORE\_PEER\_LOCALMSPID="webMSP"
export CORE\_PEER\_ADDRESS=peer1.web.ifantasy.net:7351
export CORE\_PEER\_TLS\_ROOTCERT\_FILE=$LOCAL\_CA\_PATH/web.ifantasy.net/assets/tls-ca-cert.pem
export CORE\_PEER\_MSPCONFIGPATH=$LOCAL\_CA\_PATH/web.ifantasy.net/registers/admin1/msp
export ORDERER\_CA=$LOCAL\_CA\_PATH/orderer.ifantasy.net/registers/orderer1/tls-msp/tlscacerts/tls-council-ifantasy-net-7050.pem
- 通过
soft
创建mychannel
测试通道的创世区块:bash
source envpeer1soft
peer channel create -c mychannel -f $LOCAL\_ROOT\_PATH/data/mychannel.tx -o orderer1.orderer.ifantasy.net:7151 --tls --cafile $ORDERER\_CA --outputBlock $LOCAL\_ROOT\_PATH/data/mychannel.block
如果出现以下错误,请检查环境变量 FABRIC_CFG_PATH
所指目录是否包含 core.yaml
配置文件:bash
InitCmd -> ERRO 001 Fatal error when initializing core config : Could not find config file. Please make sure that FABRIC\_CFG\_PATH is set to a path which contains core.yaml
如果出现以下错误,请检查环境变量 CORE_PEER_TLS_ROOTCERT_FILE
是否只想 TLS-CA
根证书、 环境变量 ORDERER_CA
是否指向 orderer1
的 TLS-CA
根证书:bash
ERRO 002 Client TLS handshake failed after 1.116738ms with error: x509: certificate is not valid for any names, but wanted to match localhost remoteaddress=127.0.0.1:7151
- 将
mychannel
创世区块复制到其成员组织目录:bash
cp $LOCAL\_ROOT\_PATH/data/mychannel.block $LOCAL\_CA\_PATH/soft.ifantasy.net/assets/
cp $LOCAL\_ROOT\_PATH/data/mychannel.block $LOCAL\_CA\_PATH/web.ifantasy.net/assets/
- 分别通过成员组织的 cli 加入通道:bash
source envpeer1soft
peer channel join -b $LOCAL\_CA\_PATH/soft.ifantasy.net/assets/mychannel.block
source envpeer1web
peer channel join -b $LOCAL\_CA\_PATH/web.ifantasy.net/assets/mychannel.block
如果出现以下错误,请检查环境变量 CORE_PEER_ADDRESS
是否与对应组织的 docker
配置中的 peer
地址和端口是否一致:bash
Client TLS handshake failed after 1.554615ms with error: x509: certificate is valid for peer1soft, peer1.soft.ifantasy.net, not soft.ifantasy.net remoteaddress=127.0.0.1:7251
成功后,便可通过 peer channel getinfo -c mychannel
查看已加入通道:
安装/测试链码
soft
打包并安装链码:bash
source envpeer1soft
peer lifecycle chaincode package basic.tar.gz --path asset-transfer-basic/chaincode-go --label basic\_1
peer lifecycle chaincode install basic.tar.gz
安装成功后,可使用 peer lifecycle chaincode queryinstalled
命令查询已安装链码信息(其中 Package ID
需要记下来):bash
(base) root@DebianA:1\_3Org2Peer1Orderer1TLS# peer lifecycle chaincode queryinstalled
Installed chaincodes on peer:
Package ID: basic\_1:06613e463ef6694805dd896ca79634a2de36fdf019fa7976467e6e632104d718, Label: basic\_1
web
安装链码:bash
source envpeer1web
peer lifecycle chaincode install basic.tar.gz
- 设置链码
ID
环境变量:bash
export CHAINCODE\_ID=basic\_1:06613e463ef6694805dd896ca79634a2de36fdf019fa7976467e6e632104d718
soft
和web
批准链码:bash
source envpeer1soft
peer lifecycle chaincode approveformyorg -o orderer1.orderer.ifantasy.net:7151 --tls --cafile $ORDERER\_CA --channelID mychannel --name basic --version 1.0 --sequence 1 --waitForEvent --init-required --package-id $CHAINCODE\_ID
source envpeer1web
peer lifecycle chaincode approveformyorg -o orderer1.orderer.ifantasy.net:7151 --tls --cafile $ORDERER\_CA --channelID mychannel --name basic --version 1.0 --sequence 1 --waitForEvent --init-required --package-id $CHAINCODE\_ID
批准后,可使用以下命令查看本组织的链码批准情况:bash
(base) root@DebianA:1\_3Org2Peer1Orderer1TLS# peer lifecycle chaincode queryapproved -C mychannel -n basic --sequence 1
Approved chaincode definition for chaincode 'basic' on channel 'mychannel':
sequence: 1, version: 1.0, init-required: true, package-id: basic\_1:06613e463ef6694805dd896ca79634a2de36fdf019fa7976467e6e632104d718, endorsement plugin: escc, validation plugin: vscc
也可使用以下命令查看指定链码是否已准备好被提交:bash
(base) root@DebianA:1\_3Org2Peer1Orderer1TLS# peer lifecycle chaincode checkcommitreadiness -o orderer1.orderer.ifantasy.net:7151 --tls --cafile $ORDERER\_CA --channelID mychannel --name basic --version 1.0 --sequence 1 --init-required
Chaincode definition for chaincode 'basic', version '1.0', sequence '1' on channel 'mychannel' approval status by org:
softMSP: true
webMSP: true
- 使用任意合法组织提交链码:bash
source envpeer1soft
peer lifecycle chaincode commit -o orderer1.orderer.ifantasy.net:7151 --tls --cafile $ORDERER\_CA --channelID mychannel --name basic --init-required --version 1.0 --sequence 1 --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE\_PEER\_TLS\_ROOTCERT\_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE\_PEER\_TLS\_ROOTCERT\_FILE
可使用以下命令查看链码提交情况:bash
peer lifecycle chaincode querycommitted --channelID mychannel --name basic -o orderer1.orderer.ifantasy.net:7151 --tls --cafile $ORDERER\_CA --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE\_PEER\_TLS\_ROOTCERT\_FILE
- 初始化链码(非必须):bash
peer chaincode invoke --isInit -o orderer1.orderer.ifantasy.net:7151 --tls --cafile $ORDERER\_CA --channelID mychannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE\_PEER\_TLS\_ROOTCERT\_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE\_PEER\_TLS\_ROOTCERT\_FILE -c '{"Args":["InitLedger"]}'
其中带 --isInit 参数表示当前调用为链码初始化调用,在不需要初始化的链码中可以省略此步骤。如果出现下列错误,请检查:
* 整个 docker
内的 networks
的值必须为 ${DOCKER_NETWORKS}
* 环境变量中 COMPOSE_PROJECT_NAME
和 DOCKER_NETWORKS
是否被赋值、
* docker-base.yaml
中 CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE
的值必须是 ${COMPOSE_PROJECT_NAME}_${DOCKER_NETWORKS}
bash
error starting container: error starting container: API error (404): network hyperledger\_fabric-ca not found"
- 调用链码:bash
peer chaincode invoke -o orderer1.orderer.ifantasy.net:7151 --tls --cafile $ORDERER\_CA --channelID mychannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE\_PEER\_TLS\_ROOTCERT\_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE\_PEER\_TLS\_ROOTCERT\_FILE -c '{"Args":["GetAllAssets"]}'
如果出现下列错误,请检查 approveformyorg
的链码包 ID
与 install
的链码包 ID
必须一致:bash
endorsement failure during invoke. response: status:500 message:"make sure the chaincode fabcar has been successfully defined on channel mychannel and try again: chaincode definition for 'basic' exists, but chaincode is not installed"
调用成功后可在控制台查看链码输出:bash
2022-04-05 14:25:16.529 CST 0001 INFO [chaincodeCmd] chaincodeInvokeOrQuery -> Chaincode invoke successful. result: status:200 payload:"[{\"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}]"
参考
- Nisen. Fabric账号、cryptogen和fabirc-ca. github.io. [2018-06-19] ↩︎
- Hyperledger. 成员服务提供者 (MSP). hyperledger-fabric.readthedocs.io. [2021-05-22] ↩︎
前言
工程介绍
组织架构
实验准备
本文工作
其它说明
实验步骤
实验准备
配置TLS服务
注册账户
构造组织成员证书
配置系统通道及测试通道
安装/测试链码
参考
__EOF__
风色幻想 - 本文链接: https://blog.csdn.net/ifantasy/p/16115632.html
- 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角**【[推荐](javascript:void(0)
Hyperledger Fabric定制联盟链网络工程实践相关推荐
- Hyperledger Fabric是区块链中联盟链架构详细介绍
区块链开源实现HYPERLEDGER FABRIC架构详解 区块链开源实现HYPERLEDGER FABRIC架构详解 hyperledger fabric是区块链中联盟链的优秀实现,主要代码由IBM ...
- HyperLedger Fabric Introduction——区块链超级账本介绍
介绍 HyperLedger Fabric是一个基于模块化架构的分布式账本解决方案平台,它拥有深度加密.便捷扩展.部署灵活及可插拔等特性.它设计之初的目的是支持不同组件的可插拔实现,并适应整个经济生态 ...
- Hyperledger Fabric构建第一个网络(官方教程)
前言 官方英文:Building Your First Network 中文:构建第一个fabric网络 感谢中文翻译!不然这么长的英文以及各种名词解释实在头疼.有一些和官方英文不一样的地方我已经按官 ...
- 基于fabric的联盟链管理项目
manageChain 项目地址 https://github.com/lushena/manageChain.git 在建设联盟链时,发现更新联盟链组织成员不是太方便,例如增删链内成员:利用一些空余 ...
- Hyperledger Fabric explorer区块链浏览器搭建
https://github.com/hyperledger-labs/blockchain-explorer 官方浏览器的github地址 根据文档,采用docker容器的方法搭建explorer. ...
- 【天光学术】网络工程论文:网络工程实践中安全技术的应用(节选)
摘要:随着我国经济的不断发展,信息化已经深入到我国各个领域.而网络信息技术的使用,改变了传统的信息交流方式,给人们的工作与生活带来了极大的便捷,其重要性不断提高.尤其是近年来,信息技术已经深入到经济发 ...
- Hyperledger Fabric金融区块链项目总结 之一 概述
这半年从零开始做了一个金融区块链项目,做的事情比较杂,因为人手不够(只有一个campus hire帮忙),几乎所有事情都要自己做,但人手少也有很明显的好处:项目/人员管理简单,几乎把所有精力都放在研发 ...
- 搭建基于hyperledger fabric的联盟社区(九) --检索状态数据库
一.启动elasticsearch服务 官网下载压缩包解压,进入bin目录启动: ./elasticsearch 通过ip访问 localhost:9200,可以看到如下信息 { name: &quo ...
- Hyperledger Fabric无系统通道启动及通道的创建和删除
前言 在Hyperledger Fabric组织的动态添加和删除中,我们已经完成了在运行着的网络中动态添加和删除组织,但目前为止,我们启动 orderer 节点的方式都是通过系统通道的方式,这样自带系 ...
- Hyperledger Fabric 部署在多个主机上
前言 在实验Hyperledger Fabric无排序组织以Raft协议启动多个Orderer服务.TLS组织运行维护Orderer服务中,我们已经完成了使用提供 TLS-CA 服务的 council ...
最新文章
- feign post 传递空值_HTTP中GET与POST的区别,99 %的人都理解错了
- HDFS中常用的shell命令总结
- c ajax 上传图片,ajax +NodeJS 实现图片上传实例
- 为什么要用Hibernate框架? 把SessionFactory,Session,Transcational封装成包含crud的工具类并且处理了事务,那不是用不着spring了?...
- git 创建webpack项目_使用webpack手动创建一个完整项目的全过程
- python 课后作业_python 学习笔记 五 课后作业
- docker php kafka,docker安装kafka
- linux运行sqlite,Linux上安装sqlite3
- c语言银行家算法模拟程序,银行家算法C语言模拟程序
- JSK-369 字符逆序【入门】
- batch批处理(转载)
- 公司设备损坏了,怎么处理
- Unity实现2D小游戏
- Google Earth Engine —— LandTrendr JavaScript 模块API(2万多字长文)
- 屏蔽全部统计代码(51.la cnzz 百度统计 谷歌分析师adsense、屏蔽淘宝客广告代码)的方法...
- linux systemd.service说明
- MySQL --- 多表查询 - 七种JOINS实现、集合运算、多表查询练习
- Ubuntu 18.04 配置ibus中文拼音输入法
- uniapp微信小程序证件识别
- python股市_如何使用python和破折号创建仪表板来主导股市
热门文章
- paypal的rest api接入
- 博客广告 何乐而不为?
- jmeter 生成随机数(手机号)随机中文字符
- mysql 订单表 订单详情表 关联 排序 统计
- Java---(SpringBlade框架)后台从数据库读取所有点的经度和纬度,传输到前端显示在地图上
- 计算机英语吕,吕的英语_我姓吕托福注册姓名的英文姓是写LYU还是LV还是LU我的护照上写的是LYU_淘题吧...
- 获取中文拼音或拼音首字母方法
- Vue 使用 PinyinMatch实现拼音匹配搜索
- 怎么把html导入iphone,电脑上的视频怎么导入iphone8?电脑视频导入苹果手机教程...
- System.out.println()标准输出方法性能影响一窥
- Hyperledger Fabric是区块链中联盟链架构详细介绍