Fabric1.4:運行 first-network 網絡


說明:運行 first-network 網絡,必須先安裝好 fabric1.4 的預置環境,fabric1.4 的安裝可以參考此博文:https://www.cnblogs.com/zongmin/p/11635686.html 。本文使用 fabric 版本為 v1.4.3,其它版本的 first-network 網絡配置可能不同。

構建你的第一個網絡(Build Your First network,BYFN)提供了一個 fabric 的示例網絡。該示例網絡中由兩個組織構成,每個組織維護兩個 peer 節點,默認使用 solo 共識服務。

1 腳本 byfn.sh

first-network 中有一個啟動腳本 byfn.sh,利用構建的 Docker 鏡像快速啟動網絡。該腳本會啟動一個 orderer 節點和四個歸屬兩個不同組織的 peer 節點,還將啟動一個容器運行腳本,它將 peer 節點加入通道(Channel)、部署和實例化鏈碼,並根據已部署的鏈碼驅動交易執行。

以下是 byfn.sh 腳本的幫助文檔:

$ ./byfn.sh
Usage: 
  byfn.sh <mode> [-c <channel name>] [-t <timeout>] [-d <delay>] [-f <docker-compose-file>] [-s <dbtype>] [-l <language>] [-o <consensus-type>] [-i <imagetag>] [-a] [-n] [-v]
    <mode> - one of 'up', 'down', 'restart', 'generate' or 'upgrade'
      - 'up' - bring up the network with docker-compose up
      - 'down' - clear the network with docker-compose down
      - 'restart' - restart the network
      - 'generate' - generate required certificates and genesis block
      - 'upgrade'  - upgrade the network from version 1.3.x to 1.4.0
    -c <channel name> - channel name to use (defaults to "mychannel")
    -t <timeout> - CLI timeout duration in seconds (defaults to 10)
    -d <delay> - delay duration in seconds (defaults to 3)
    -f <docker-compose-file> - specify which docker-compose file use (defaults to docker-compose-cli.yaml)
    -s <dbtype> - the database backend to use: goleveldb (default) or couchdb
    -l <language> - the chaincode language: golang (default) or node
    -o <consensus-type> - the consensus-type of the ordering service: solo (default), kafka, or etcdraft
    -i <imagetag> - the tag to be used to launch the network (defaults to "latest")
    -a - launch certificate authorities (no certificate authorities are launched by default)
    -n - do not deploy chaincode (abstore chaincode is deployed by default)
    -v - verbose mode
  byfn.sh -h (print this message)

Typically, one would first generate the required certificates and 
genesis block, then bring up the network. e.g.:

  byfn.sh generate -c mychannel
  byfn.sh up -c mychannel -s couchdb
        byfn.sh up -c mychannel -s couchdb -i 1.4.0
  byfn.sh up -l node
  byfn.sh down -c mychannel
        byfn.sh upgrade -c mychannel

Taking all defaults:
  byfn.sh generate
  byfn.sh up
  byfn.sh down
  • -c:設置通道名稱,默認為 mychannel,例:./byfn.sh up -c testchannel
  • -t:設置 CLI 超時參數,不設置的情況下 CLI 將放棄 10 秒后發出查詢請求的默認設置
  • -l:設置智能合約的語言,默認是 go,例:./byfn.sh up -l java
  • -o:設置排序服務方式,默認是 solo,例:./byfn.sh up -o kafka

2 生成證書

執行如下命令:

$ ./byfn.sh generate

運行該命令會為 fabric 網絡的各種實體生成所需的證書與密鑰,保存在 crypto-config 目錄下。

Generating certs and genesis block for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] y
proceeding ...
/Users/xxx/dev/fabric-samples/bin/cryptogen

##########################################################
##### Generate certificates using cryptogen tool #########
##########################################################
org1.example.com
2017-06-12 21:01:37.334 EDT [bccsp] GetDefault -> WARN 001 Before using BCCSP, please call InitFactories(). Falling back to bootBCCSP.
...

/Users/xxx/dev/fabric-samples/bin/configtxgen
##########################################################
#########  Generating Orderer Genesis block ##############
##########################################################
2017-06-12 21:01:37.558 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.562 EDT [msp] getMspConfig -> INFO 002 intermediate certs folder not found at [/Users/xxx/dev/byfn/crypto-config/ordererOrganizations/example.com/msp/intermediatecerts]. Skipping.: [stat /Users/xxx/dev/byfn/crypto-config/ordererOrganizations/example.com/msp/intermediatecerts: no such file or directory]
...
2017-06-12 21:01:37.588 EDT [common/configtx/tool] doOutputBlock -> INFO 00b Generating genesis block
2017-06-12 21:01:37.590 EDT [common/configtx/tool] doOutputBlock -> INFO 00c Writing genesis block

#################################################################
### Generating channel configuration transaction 'channel.tx' ###
#################################################################
2017-06-12 21:01:37.634 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.644 EDT [common/configtx/tool] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx
2017-06-12 21:01:37.645 EDT [common/configtx/tool] doOutputChannelCreateTx -> INFO 003 Writing new channel tx

#################################################################
#######    Generating anchor peer update for Org1MSP   ##########
#################################################################
2017-06-12 21:01:37.674 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.678 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2017-06-12 21:01:37.679 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update

#################################################################
#######    Generating anchor peer update for Org2MSP   ##########
#################################################################
2017-06-12 21:01:37.700 EDT [common/configtx/tool] main -> INFO 001 Loading configuration
2017-06-12 21:01:37.704 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2017-06-12 21:01:37.704 EDT [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update

3 啟動網絡

使用如下命令啟動 fabric 網絡:

$ ./byfn.sh up

注意:該命令會檢查網絡實體的證書是否生成,如果沒有會先生成證書。因此,可以直接執行 ./byfn.sh up 命令,而無需執行 ./byfn.sh generate 命令。

執行日志如下:

Starting for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n]
proceeding ...
Creating network "net_byfn" with the default driver
Creating peer0.org1.example.com
Creating peer1.org1.example.com
Creating peer0.org2.example.com
Creating orderer.example.com
Creating peer1.org2.example.com
Creating cli

 ____    _____      _      ____    _____
/ ___|  |_   _|    / \    |  _ \  |_   _|
\___ \    | |     / _ \   | |_) |   | |
 ___) |   | |    / ___ \  |  _ <    | |
|____/    |_|   /_/   \_\ |_| \_\   |_|

Channel name : mychannel
Creating channel...

啟動所有容器,然后驅動完整的應用程序場景。執行成功后,在終端會出現如下輸出:

Query Result: 90
2017-05-16 17:08:15.158 UTC [main] main -> INFO 008 Exiting.....
===================== Query successful on peer1.org2 on channel 'mychannel' =====================

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


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

屏幕后面發生了什么?

./byfn.sh up 啟動完 orderer 節點、peer 節點和 CLI 容器之后,實際是調用 script.sh 腳本,該腳本是在 CLI 容器中執行,script.sh 腳本完成客戶端交易執行操作。下面是 CLI 容器的定義,CLI 容器其實就是用戶客戶端,只不過是命令行客戶端,運行在容器中。默認情況下,CLI 的身份是 admin.org1,連接 peer0.org1 節點,執行 script.sh 腳本。

  cli:
    container_name: cli
    image: hyperledger/fabric-tools:$IMAGE_TAG
    tty: true
    stdin_open: true
    environment:
      - SYS_CHANNEL=$SYS_CHANNEL
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      #- FABRIC_LOGGING_SPEC=DEBUG
      - FABRIC_LOGGING_SPEC=INFO
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key
      - 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
    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/chaincode
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
        - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
        - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
    depends_on:
      - orderer.example.com
      - peer0.org1.example.com
      - peer1.org1.example.com
      - peer0.org2.example.com
      - peer1.org2.example.com
    networks:
      - byfn

下面簡略描述 script.sh 的基本函數。

3.1 創建通道

createChannel() {
	setGlobals 0 1    # admin.org1 連接 peer0.org1

	if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
                set -x
		peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt
		res=$?
                set +x
	else
				set -x
		peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
		res=$?
				set +x
	fi
	cat log.txt
	verifyResult $res "Channel creation failed"
	echo "===================== Channel '$CHANNEL_NAME' created ===================== "
	echo
}

admin.org1 連接 peer0.org1 節點,向 orderer 節點傳遞要創建的通道名稱 $CHANNEL_NAME (默認為 mychannel)和通道配置交易 channel.tx。如果創建成功,則返回通道的創世區塊 $CHANNEL_NANE.block,該區塊包含 channel.tx 指定的通道配置信息,保存在 CLI 容器中。


3.2 加入通道

joinChannel () {
	for org in 1 2; do
	    for peer in 0 1; do
		joinChannelWithRetry $p$CHANNEL_NAMEeer $org
		echo "===================== peer${peer}.org${org} joined channel '$CHANNEL_NAME' ===================== "
		sleep $DELAY
		echo
	    done
	done
}

第一個 for 循環標識兩個組織 org1 和 org2;第二個 for 循環標識兩個組織 peer0 和 peer1。根據 $peer $org 先后順序,四個節點分別是:

  • peer0.org1.example.com
  • peer1.org1.example.com
  • peer0.org2.example.com
  • peer1.org2.example.com

這是 first-network 的默認網絡結構,在 crypto-config.yaml 文件的 TwoOrgsChannel 部分定義,MSP 路徑在 docker-compose-cli.yaml 文件中定義。

## Sometimes Join takes time hence RETRY at least 5 times
joinChannelWithRetry() {
  PEER=$1
  ORG=$2
  setGlobals $PEER $ORG

  set -x
  peer channel join -b $CHANNEL_NAME.block >&log.txt
  res=$?
  set +x
  cat log.txt
  if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then
    COUNTER=$(expr $COUNTER + 1)
    echo "peer${PEER}.org${ORG} failed to join the channel, Retry after $DELAY seconds"
    sleep $DELAY
    joinChannelWithRetry $PEER $ORG
  else
    COUNTER=1
  fi
  verifyResult $res "After $MAX_RETRY attempts, peer${PEER}.org${ORG} has failed to join channel '$CHANNEL_NAME' "
}

在 joinChannelWithRetry 函數中的 setGlobals 是設置 CLI 容器的環境變量的函數。例如:setGlobals 1 2 設置 CLI 的身份為 admin.org2,連接 peer1.org2 節點。

使用 peer channel join 命令讓節點加入通道,$CHANNEL_NAME.block 就是前面創建通道成功時返回的區塊,該區塊在上面 peer0.org1 創建通道時保持在 CLI 容器內,所以能直接使用。節點成功加入通道后會創建 CHANNEL_NAME.block 開頭的鏈。


3.3 更新錨節點

一個組織只能有一個錨節點,節點加入通道后才能進行更新。

updateAnchorPeers() {
  PEER=$1
  ORG=$2
  setGlobals $PEER $ORG

  if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
    set -x
    peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx >&log.txt
    res=$?
    set +x
  else
    set -x
    peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
    res=$?
    set +x
  fi
  cat log.txt
  verifyResult $res "Anchor peer update failed"
  echo "===================== Anchor peers updated for org '$CORE_PEER_LOCALMSPID' on channel '$CHANNEL_NAME' ===================== "
  sleep $DELAY
  echo
}

更新 org1 的錨節點 peer0.org1.example.com 和 org2 的錨節點 peer0.org2.example.com 。過程是將 Org1MSPanchors.tx 和 Org2MSPanchors.tx 交易連同通道名稱 $CHANNEL_NAME 傳遞給 orderer。


3.4 安裝鏈碼

installChaincode() {
  PEER=$1
  ORG=$2
  setGlobals $PEER $ORG
  VERSION=${3:-1.0}
  set -x
  peer chaincode install -n mycc -v ${VERSION} -l ${LANGUAGE} -p ${CC_SRC_PATH} >&log.txt
  res=$?
  set +x
  cat log.txt
  verifyResult $res "Chaincode installation on peer${PEER}.org${ORG} has failed"
  echo "===================== Chaincode is installed on peer${PEER}.org${ORG} ===================== "
  echo
}

安裝鏈碼需要指定鏈碼的配置信息,-n 是鏈碼的名稱;-v 是版本號;-l 是指定鏈碼的語言,如果不使用該標志默認使用 go 語言;-p 是指向鏈碼路徑,默認是 github.com/chaincode/chaincode_example02/go/


3.5 實例化鏈碼

instantiateChaincode() {
  PEER=$1
  ORG=$2
  setGlobals $PEER $ORG
  VERSION=${3:-1.0}

  # while 'peer chaincode' command can get the orderer endpoint from the peer
  # (if join was successful), let's supply it directly as we know it using
  # the "-o" option
  if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
    set -x
    peer chaincode instantiate -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc -l ${LANGUAGE} -v ${VERSION} -c '{"Args":["init","a","100","b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" >&log.txt
    res=$?
    set +x
  else
    set -x
    peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -l ${LANGUAGE} -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" >&log.txt
    res=$?
    set +x
  fi
  cat log.txt
  verifyResult $res "Chaincode instantiation on peer${PEER}.org${ORG} on channel '$CHANNEL_NAME' failed"
  echo "===================== Chaincode is instantiated on peer${PEER}.org${ORG} on channel '$CHANNEL_NAME' ===================== "
  echo
}

fabric 網絡上的鏈碼是多次安裝,一次實例化,該函數只需運行一遍即可。

鏈碼在通道 $CHANNEL_NAME 上實例化。實例化將鏈碼添加到通道上,啟動目標節點的容器,初始化與鏈碼相關的初始值,這里的初始值為 ["a","100","b","200"]。”實例化“ 過程會產生鏈碼的容器,例如: dev-peer0-org1.example.com-mycc-1.0 。實例化過程需要指定背書策略,通過 -P 參數設置,這里的策略定義為 AND ('Org1MSP.peer','Org2MSP.peer') ,表示任何交易必須要有 org1 和 org2 節點的共同背書。此處背書的節點只能是以下四對組合:

  • peer0.org1.example.compeer0.org2.example.com
  • peer0.org1.example.compeer1.org2.example.com
  • peer1.org1.example.compeer0.org2.example.com
  • peer1.org1.example.compeer1.org2.example.com

3.6 調用鏈碼

# chaincodeInvoke <peer> <org> ...
# Accepts as many peer/org pairs as desired and requests endorsement from each
chaincodeInvoke() {
  parsePeerConnectionParameters $@
  res=$?
  verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters "

  # while 'peer chaincode' command can get the orderer endpoint from the
  # peer (if join was successful), let's supply it directly as we know
  # it using the "-o" option
  if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
    set -x
    peer chaincode invoke -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc $PEER_CONN_PARMS -c '{"Args":["invoke","a","b","10"]}' >&log.txt
    res=$?
    set +x
  else
    set -x
    peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc $PEER_CONN_PARMS -c '{"Args":["invoke","a","b","10"]}' >&log.txt
    res=$?
    set +x
  fi
  cat log.txt
  verifyResult $res "Invoke execution on $PEERS failed "
  echo "===================== Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME' ===================== "
  echo
}

$PEER_CONN_PARM 發送一個調用,以實現從 a 中轉移 10b$PEER_CONN_PARM 就是上面四對組合中的任意一對背書節點。在調用鏈碼的過程中,如果某個節點的鏈碼容器還沒有啟動,就會執行啟動。


3.7 查詢鏈碼

chaincodeQuery() {
  PEER=$1
  ORG=$2
  setGlobals $PEER $ORG
  EXPECTED_RESULT=$3
  echo "===================== Querying on peer${PEER}.org${ORG} on channel '$CHANNEL_NAME'... ===================== "
  local rc=1
  local starttime=$(date +%s)

  # continue to poll
  # we either get a successful response, or reach TIMEOUT
  while
    test "$(($(date +%s) - starttime))" -lt "$TIMEOUT" -a $rc -ne 0
  do
    sleep $DELAY
    echo "Attempting to Query peer${PEER}.org${ORG} ...$(($(date +%s) - starttime)) secs"
    set -x
    peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' >&log.txt
    res=$?
    set +x
    test $res -eq 0 && VALUE=$(cat log.txt | awk '/Query Result/ {print $NF}')
    test "$VALUE" = "$EXPECTED_RESULT" && let rc=0
    # removed the string "Query Result" from peer chaincode query command
    # result. as a result, have to support both options until the change
    # is merged.
    test $rc -ne 0 && VALUE=$(cat log.txt | egrep '^[0-9]+$')
    test "$VALUE" = "$EXPECTED_RESULT" && let rc=0
  done
  echo
  cat log.txt
  if test $rc -eq 0; then
    echo "===================== Query successful on peer${PEER}.org${ORG} on channel '$CHANNEL_NAME' ===================== "
  else
    echo "!!!!!!!!!!!!!!! Query result on peer${PEER}.org${ORG} is INVALID !!!!!!!!!!!!!!!!"
    echo "================== ERROR !!! FAILED to execute End-2-End Scenario =================="
    echo
    exit 1
  fi
}

向裝有鏈碼的節點發送一個查詢查看 a 的值。如果返回值為 90,則表明在之前的交易中 “a” 的值被減去 10。


這證明了什么?

為了能成功地對賬本進行讀/寫操作,鏈碼必須按照在相應的節點上。只有在鏈碼中執行初始化(Init函數)、調用(Invoke函數)交易(讀/寫),或者查詢 “a” 的值,才會為節點啟動一個鏈碼容器(交易引起容器的啟動)。另外,通道中的所有節點都維護賬本的一個精確副本,該副本包括在區塊中存儲不可變、按順序排列的記錄的區塊鏈,以及用戶維護當前賬本的狀態數據庫。


4 關閉網絡

執行下面命令:

$ ./byfn.sh down
Stopping for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] y
proceeding ...
WARNING: The BYFN_CA2_PRIVATE_KEY variable is not set. Defaulting to a blank string.
WARNING: The BYFN_CA1_PRIVATE_KEY variable is not set. Defaulting to a blank string.
Stopping cli                    ... done
Stopping peer1.org1.example.com ... done
Stopping peer1.org2.example.com ... done
Stopping peer0.org1.example.com ... done
Stopping peer0.org2.example.com ... done
Stopping orderer.example.com    ... done
Removing cli                    ... done
Removing peer1.org1.example.com ... done
Removing peer1.org2.example.com ... done
Removing peer0.org1.example.com ... done
Removing peer0.org2.example.com ... done
Removing orderer.example.com    ... done
Removing network net_byfn
Removing volume net_peer0.org3.example.com
WARNING: Volume net_peer0.org3.example.com not found.
Removing volume net_peer1.org3.example.com
WARNING: Volume net_peer1.org3.example.com not found.
Removing volume net_orderer2.example.com
WARNING: Volume net_orderer2.example.com not found.
Removing volume net_orderer.example.com
Removing volume net_peer0.org2.example.com
Removing volume net_peer0.org1.example.com
Removing volume net_peer1.org1.example.com
Removing volume net_peer1.org2.example.com
Removing volume net_orderer5.example.com
WARNING: Volume net_orderer5.example.com not found.
Removing volume net_orderer4.example.com
WARNING: Volume net_orderer4.example.com not found.
Removing volume net_orderer3.example.com
WARNING: Volume net_orderer3.example.com not found.

該命令將關閉網絡,刪除證書材料和組件,並從 Docker 注冊表中刪除鏈碼鏡像。crypto-config 文件夾會被刪除,channel-artifacts 文件夾下的 channel.txgenesis.blockOrg1MSPanchors.txOrg2MSPanchors.tx 這四個文件會被刪除。


參考

  1. 《Hyperledger Fabric 核心技術》

  2. https://hyperledger-fabric.readthedocs.io/en/release-1.4/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM