多机上启动多组织(4org)的fabric网络

启动命令与过程与官方2org的相似,但由于组织增至了4个,所以无法用官方提供的scripts/scripts.sh脚本一键启动网络,具体分步操作如下:

一、初始化fabric环境

1.1 启动Fabric环境的容器

首先来启动orderer节点,在orderer服务器上运行:

docker-compose -f docker-compose-orderer.yaml up –d

运行完毕后我们可以使用docker ps看到运行了一个名字为orderer.example.com的节点。
然后我们切换到peer0.org1.example.com服务器,启动本服务器的peer节点和cli,命令为:

docker-compose -f docker-compose-peer.yaml up –d

运行完毕后我们使用docker ps应该可以看到2个正在运行的容器。
接下来依次在另外3台服务器运行启动peer节点容器的命令:

docker-compose -f docker-compose-peer.yaml up –d

现在我们整个Fabric4+1服务器网络已经成型。

1.2 创建channel

切换到peer0.org1.example.com服务器上,使用该服务器上的cli来运行创建Channel和运行ChainCode的操作。首先进入cli容器:

docker exec -it cli bash

进入容器后我们可以看到命令提示变为:

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

说明我们已经以root的身份进入到cli容器内部。
创建Channel的命令是peer channel create,我们前面创建Channel的配置区块时,指定了Channel的名字是mychannel,那么这里我们必须创建同样名字的Channel。

ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls true --cafile $ORDERER_CA

执行该命令后,系统会提示:

2017-08-29 20:36:47.486 UTC [channelCmd] readBlock -> DEBU 020 Received block:0

系统会在cli内部的当前目录创建一个mychannel.block文件,这个文件非常重要,接下来其他节点要加入这个Channel就必须使用这个文件。

1.3 各个peer加入channel

#peer1加入channel
peer channel join -b mychannel.block

#修改环境变量,使peer2加入channel
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 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 
CORE_PEER_ADDRESS=peer0.org2.example.com:7051

peer channel join -b mychannel.block

#修改环境变量,使peer3加入channel
CORE_PEER_LOCALMSPID="Org3MSP" 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp 
CORE_PEER_ADDRESS=peer0.org3.example.com:7051

peer channel join -b mychannel.block

#修改环境变量,使peer4加入channel
CORE_PEER_LOCALMSPID="Org4MSP" 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/users/Admin@org4.example.com/msp 
CORE_PEER_ADDRESS=peer0.org4.example.com:7051

peer channel join -b mychannel.block

1.4 更新锚节点

对于Org1来说,peer0.org1是锚节点,我们需要连接上它并更新锚节点,其他组织同理,peer0为锚节点:

#更新org1的锚节点
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 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 
CORE_PEER_ADDRESS=peer0.org1.example.com:7051

peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls true --cafile $ORDERER_CA

#更新org2的锚节点
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 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 
CORE_PEER_ADDRESS=peer0.org2.example.com:7051

peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org2MSPanchors.tx --tls true --cafile $ORDERER_CA

#更新org3的锚节点
CORE_PEER_LOCALMSPID="Org3MSP" 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp 
CORE_PEER_ADDRESS=peer0.org3.example.com:7051

peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org3MSPanchors.tx --tls true --cafile $ORDERER_CA

#更新org4的锚节点
CORE_PEER_LOCALMSPID="Org4MSP" 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/users/Admin@org4.example.com/msp 
CORE_PEER_ADDRESS=peer0.org4.example.com:7051

peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org4MSPanchors.tx --tls true --cafile $ORDERER_CA

二、链上代码的安装与运行

以上,整个Fabric网络和Channel都准备完毕,接下来我们来安装和运行ChainCode。这里我们不用官方提供的Example02等链码,而是自己实现了一个招标投标的合约链码,放在/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go/mychaincode 目录下。该链码可以实现一个公司提出一条链路需求,其他公司针对该链路的每一段进行投标,最后招标公司每一段选出一个投标公司达成合约,或者关闭合约的功能。

2.1 Install ChainCode安装链上代码

#在peer1上安装链码
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 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/mychaincode

#在peer2上安装链码
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 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 
CORE_PEER_ADDRESS=peer0.org2.example.com:7051

peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/mychaincode

#在peer3上安装链码
CORE_PEER_LOCALMSPID="Org3MSP" 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp 
CORE_PEER_ADDRESS=peer0.org3.example.com:7051

peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/mychaincode

#在peer4上安装链码
CORE_PEER_LOCALMSPID="Org4MSP" 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/users/Admin@org4.example.com/msp 
CORE_PEER_ADDRESS=peer0.org4.example.com:7051

peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/mychaincode

2.2 Instantiate ChainCode 实例化链上代码

实例化链上代码主要是在Peer所在的机器上对前面安装好的链上代码进行包装,生成对应Channel的Docker镜像和Docker容器。并且在实例化时我们可以指定背书策略。注意:一定要记得指定背书策略,且只有指定可以背书的组织才有写入账本的权利。实测4个组织时如果只指定Org1MSP和Org2MSP背书的话,则org3和org4无法写入账本。我们运行以下命令完成实例化:

peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1.0 -c '{"Args":[]}' -P "OR ('Org1MSP.member','Org2MSP.member','Org3MSP.member','Org4MSP.member')"

2.3升级链码

当需要对链码进行修改时,可以安装新的链码并进行升级。
注意:如果直接修改存放链码的目录或者修改链码文件本身,可能运行docker时链码并不会被修改,本人也很困惑。这种方法目前是可行的修改链码的方式。

2.3.1 安装新版本的链码

下面的命令跟之前的安装链码的命令相比,修改了-p后链码的路径,和-v后链码的版本,这里也说明1.0版本和1是不同的。

#在peer1上安装新版本的链码
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 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
peer chaincode install -n mycc -v 1 -p github.com/hyperledger/fabric/examples/chaincode/go/algoblu

#在peer2上安装新版本的链码
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 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 
CORE_PEER_ADDRESS=peer0.org2.example.com:7051

peer chaincode install -n mycc -v 1 -p github.com/hyperledger/fabric/examples/chaincode/go/algoblu

#在peer3上安装新版本的链码
CORE_PEER_LOCALMSPID="Org3MSP" 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp 
CORE_PEER_ADDRESS=peer0.org3.example.com:7051

peer chaincode install -n mycc -v 1 -p github.com/hyperledger/fabric/examples/chaincode/go/algoblu

#在peer4上安装新版本的链码
CORE_PEER_LOCALMSPID="Org4MSP" 
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/tls/ca.crt 
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/users/Admin@org4.example.com/msp 
CORE_PEER_ADDRESS=peer0.org4.example.com:7051

peer chaincode install -n mycc -v 1 -p github.com/hyperledger/fabric/examples/chaincode/go/algoblu
2.3.2 升级链码

升级链码的命令和初始化类似,同样需要指定背书策略。

peer chaincode upgrade -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1 -c '{"Args":[]}' -P "OR ('Org1MSP.member','Org2MSP.member','Org3MSP.member','Org4MSP.member')"

2.4 调用链码

2.4.1 合约创建

首先在peer1(192.168.3.222)上发起(创建)一个链路交易,Args为需要传入的json格式的参数,因为含有双引号,且不能用单引号和三引号替代,所以需有转义字符。

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1 -c '{"Args":["link_contract_create","{\"task_id\": \"1522112000\",\"user_code\": \"CMCC\",\"contract_code\":\"lc1522512008\",\"purchaser_user_code\":\"CMCC\",\"biding_start_time\": \"1525104000\",\"biding_end_time\":\"1525881600\",\"deal_time\":\"\",\"close_time\":\"\",\"contract_start_time\": \"1527782400\",\"contract_end_time\":\"1559318400\",\"link_start\": \"Beijing\",\"link_end\": \"NewYork\",\"path_list\":[{\"path_start\": \"Beijing\",\"path_end\":\"Tokyo\",\"path_bandwidth\":\"50Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_resource_list\":[]},{\"path_start\": \"Tokyo\",\"path_end\":\"NewYork\",\"path_bandwidth\":\"50Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_resource_list\":[]}],\"contract_status\":\"yes\"}"]}' -C mychannel
2.4.2 合约投标

接着在peer2(192.168.3.224)、peer3(192.168.3.225)、peer4(192.168.3.228)上依此对合约进行投标,投标的函数名为link_contract_biding,投标的参数内包含了投标公司的名称,价格,及可以提供的链路资源,如丢包率、带宽、传输速率等。

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -n mycc -v 1 -c '{"Args":["link_contract_biding","{\"task_id\": \"1522112006\",\"user_code\": \"CTCC\",\"contract_code\": \"lc1522512008\",\"path_list\": [{\"path_start\": \"Beijing\",\"path_end\": \"Tokyo\",\"path_bandwidth\": \"50Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_resource_list\": [{\"supplyer_user_code\": \"CTCC\",\"path_bandwidth\": \"100Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_bandwidth_unit_price\": \"200yuan\",\"path_deal_status\": \"\"}]},{\"path_start\": \"Tokyo\",\"path_end\": \"NewYork\",\"path_bandwidth\": \"50Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_resource_list\": [{\"supplyer_user_code\": \"CTCC\",\"path_bandwidth\": \"100Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_bandwidth_unit_price\": \"200yuan\",\"path_deal_status\": \"\"}]}],\"contract_status\": \"\"}"]}' -C mychannel

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -n mycc -v 1 -c '{"Args":["link_contract_biding","{\"task_id\": \"1522112006\",\"user_code\": \"CUCC\",\"contract_code\": \"lc1522512008\",\"path_list\": [{\"path_start\": \"Beijing\",\"path_end\": \"Tokyo\",\"path_bandwidth\": \"50Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_resource_list\": [{\"supplyer_user_code\": \"CUCC\",\"path_bandwidth\": \"100Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_bandwidth_unit_price\": \"200yuan\",\"path_deal_status\": \"\"}]},{\"path_start\": \"Tokyo\",\"path_end\": \"NewYork\",\"path_bandwidth\": \"50Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_resource_list\": [{\"supplyer_user_code\": \"CUCC\",\"path_bandwidth\": \"100Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_bandwidth_unit_price\": \"200yuan\",\"path_deal_status\": \"\"}]}],\"contract_status\": \"\"}"]}' -C mychannel

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -n mycc -v 1 -c '{"Args":["link_contract_biding","{\"task_id\": \"1522112006\",\"user_code\": \"algoblu\",\"contract_code\": \"lc1522512008\",\"path_list\": [{\"path_start\": \"Beijing\",\"path_end\": \"Tokyo\",\"path_bandwidth\": \"50Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_resource_list\": [{\"supplyer_user_code\": \"algoblu\",\"path_bandwidth\": \"100Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_bandwidth_unit_price\": \"200yuan\",\"path_deal_status\": \"\"}]},{\"path_start\": \"Tokyo\",\"path_end\": \"NewYork\",\"path_bandwidth\": \"50Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_resource_list\": [{\"supplyer_user_code\": \"algoblu\",\"path_bandwidth\": \"100Mbps\",\"path_packet_loss_rate\": \"5%\",\"path_avg_rtt\": \"200ms\",\"path_bandwidth_unit_price\": \"200yuan\",\"path_deal_status\": \"\"}]}],\"contract_status\": \"\"}"]}' -C mychannel
2.4.3 合约达成

发起合约的公司对投标方进行选择,每段链路有且只有一个投标方中标。

peer chaincode invoke -o orderer.example.com:7050  --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1.0 -c'{"Args":["link_contract_deal","{\"task_id\": \"1522112000\",\"user_code\": \"CMCC\",\"contract_code\":\"lc1522512008\",\"deal_time\":\"\",\"path_list\":[{\"path_start\": \"Beijing\",\"path_end\":\"Tokyo\",\"path_resource_list\":[{\"supplyer_user_code\": \"CTCC\",\"path_deal_status\": \"yes\"}]},{\"path_start\": \"Tokyo\",\"path_end\":\"NewYork\",\"path_resource_list\":[{\"supplyer_user_code\": \"algoblu\",\"path_deal_status\": \"yes\"}]}],\"contract_status\":\"deal\"}"]}'
2.4.4 合约关闭

如果没有人投标或者投标没有合适的,则发起方关闭合约。一个合约的最终状态为达成/关闭。已经达成/关闭的合约无法再进行投标。

peer chaincode invoke -o orderer.example.com:7050  --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1.0 -c'{"Args":["link_contract_close","{\"task_id\": \"1522112000\",\"user_code\": \"CMCC\",\"contract_code\":\"lc1522512008\",\"close_time\": \"1525881600\",\"contract_status\":\"close\"}"]}'
2.4.5 查询合约

查询分为两种,查询全部和查询合约的最新状态,"version_type" 的值分别为lastwhole 。如果是发起方进行查询,则可以查出该合约所有相关的投标招标信息,如果是投标方进行查询,则只能查到和自己有关的投标招标信息,而不能查出其他投标方的信息。

peer chaincode invoke -o orderer.example.com:7050  --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1 -c '{"Args":["query","{\"task_id\": \"1527228705614\",\"user_code\": \"CMCC\",\"contract_code\": \"lc1522512008\",\"version_type\": \"last\"}"]}'

在一个peer上操作,在任意的peer上都可以查询出合约账本的内容,且每一个peer都可以进行读写操作,四个org的网络才算正常配置并启动。

三、遇到的问题

Error: exspect MspId Org4MSP, receice org4MSP

原因:Org4的docker-compose-peer.yaml把环境变量CORE_PEER_LOCALMSPID=“Org4MSP”错写成小写的org4MSP了。

chaincode error (status: 500, message: Cannot create ledger from genesis block, due to LedgerID already exists))

原因:重复加入同一个channel

Cannot run peer because error when setting up MSP from directory /etc/hyperledger/fabric/msp: err Could not load a valid signer certificate from directory /etc/hyperledger/fabric/msp/signcerts, err stat /etc/hyperledger/fabric/msp/signcerts: no such file or directory

原因:MSP证书没有生成或者和不对应

Error: Error endorsing invoke: rpc error: code = Unknown desc = Error executing chaincode: Failed to execute transaction (Timeout expired while executing transaction) -nil

原因:输入的参数格式不正确或者网络不好网速太慢
参考网址:
深入理解Fabric环境搭建的详细过程(步骤非常详细)
Hyperledger Fabric 链码的介绍(讲了如何升级链码)
Hyperledger Fabric Business Network 4 Organizations(我找到唯一一篇4个组织的参考,步骤也很细,讲了如何不用generateArtifacts.sh自己用命令行生成证书公私钥等)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值