5.1 Hyperledger Fabric 專題 - 構(gòu)建第一個(gè) Hyperledger Fabric 網(wǎng)絡(luò)

Hyperledger Fabric 專題 - 構(gòu)建第一個(gè) Hyperledger Fabric 網(wǎng)絡(luò)

Hyperledger Fabric 最新的文檔基于版本是 v2.0 Alpha release朋魔。由于示例中相關(guān)的 docker 鏡像的版本是 v1.4.3瓷产,因?yàn)橄嚓P(guān)文檔需要參考的版本為 v1.4.3共耍。這些文檔鏈接在本文的 Reference 部分都有涉及锯岖。

1. 本文目的

參考文檔 Building Your First Network 搭建第一個(gè) Hyperledger Fabric 網(wǎng)絡(luò)。本示例 fabric-samples 需要的 hyperledger fabric 相關(guān)的二進(jìn)制程序安裝參考文檔 Install Samples, Binaries and Docker Images仪芒。而安裝 hyperledger fabric 相關(guān)的二進(jìn)制程序所需要的一些前置軟件需求唁影,請(qǐng)參考文檔 Prerequisites耕陷。

2. 構(gòu)建 Hyperledger Fabric 網(wǎng)絡(luò)

根據(jù) fabric-samples/first-network 示例搭建第一個(gè) Hyperledger Fabric 網(wǎng)絡(luò)。這里据沈,主要是基于提供的腳本 byfn.sh 來快速搭建啃炸。腳本 byfn.sh 提供了許多可配置的參數(shù),如果不提供則使用默認(rèn)值卓舵。這里為了簡(jiǎn)化,先使用默認(rèn)值膀钠。

需要注意的是掏湾,下面的命令需要在 first-network 目錄下運(yùn)行,但是調(diào)用的 hyperledger fabric 相關(guān)的二進(jìn)制程序卻是在 fabric-samples/bin 目錄肿嘲。

2.1 腳本 byfn.sh 的詳細(xì)說明

Usage:
byfn.sh <mode> [-c <channel name>] [-t <timeout>] [-d <delay>] [-f <docker-compose-file>] [-s <dbtype>] [-l <language>] [-o <consensus-type>] [-i <imagetag>] [-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), node, or java"
  -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\")"
  -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"

2.2 使用腳本 byfn.sh 構(gòu)建 Fabric 網(wǎng)絡(luò)的詳細(xì)步驟

Step 1. Generate Network Artifacts

$ ./byfn.sh generate

Step 2. Bring Up the Network

$ sudo ./byfn.sh up

Step 3. Bring Down the Network

$ sudo ./byfn.sh down

3. 拓展

在這一部分融击,將把上述通過腳本 byfn.sh 構(gòu)建的過程每一個(gè)關(guān)鍵步驟進(jìn)行詳細(xì)解構(gòu),并通過手動(dòng)執(zhí)行命令的方式來構(gòu)建 hyperledger fabric 網(wǎng)絡(luò)雳窟。

3.0 前置操作

下面的操作需要將容器 cli 的 FABRIC_LOGGING_SPEC 日志級(jí)別從 INFO 調(diào)整為 DEBUG尊浪。

first-network/docker-compose-cli.yaml

cli:
  container_name: cli
  image: hyperledger/fabric-tools:$IMAGE_TAG
  tty: true
  stdin_open: true
  environment:
    - GOPATH=/opt/gopath
    - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
    - FABRIC_LOGGING_SPEC=DEBUG
    #- FABRIC_LOGGING_SPEC=INFO

3.1 Crypto Generator

  • cryptogen
    • 生成密碼組件的工具
    • 文件位置為 fabric-samples/bin/cryptogen
  • crypto-config.yaml
    • 配置文件,用于說明示例 first-network 需要生成的密碼組件
    • 文件位置為 first-network/crypto-config.yaml
  • crypto-config
    • 示例 first-network 生成的密碼組件
    • 文件位置為 first-network/crypto-config

3.2 Configuration Transaction Generator

  • configtxgen
    • 生成構(gòu)件封救,如
      • orderer genesis block,
      • channel configuration transaction,
      • and two anchor peer transactions - one for each Peer Org.
    • 文件位置為 fabric-samples/bin/configtxgen
  • configtx.yaml.yaml
    • 配置文件拇涤,用于說明示例 first-network 需要的構(gòu)件,一個(gè) Orderer Org (OrdererOrg)誉结,和兩個(gè) Peer Orgs (Org1 & Org2)鹅士。
    • 文件位置為 first-network/configtx.yaml
  • channel-artifacts
    • 示例 first-network 生成構(gòu)件
      • channel.tx
      • genesis.block
      • Org1MSPanchors.tx
      • Org2MSPanchors.tx
    • 文件位置為 first-network/channel-artifacts

3.3 通過工具獨(dú)立運(yùn)行上述命令

下面,我們通過命令 cryptogen 和 configtxgen 手動(dòng)生成上述的密碼組件和示例構(gòu)件惩坑。

3.3.1 手動(dòng)生成密碼組件

參考腳本 byfn.sh 中的函數(shù) generateCerts().

$ ../bin/cryptogen generate --config=./crypto-config.yaml
org1.example.com
org2.example.com

最終的密碼組件會(huì)生成到目錄 first-network/crypto-config 中掉盅。

3.3.2 手動(dòng)生成示例構(gòu)件

參考腳本 byfn.sh 中的函數(shù) generateChannelArtifacts().

$ export FABRIC_CFG_PATH=$PWD
$ ../bin/configtxgen -profile TwoOrgsOrdererGenesis -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

最終的示例構(gòu)件會(huì)生成到目錄 first-network/channel-artifacts 中,如 first-network/channel-artifacts/genesis.block以舒。

3.3.3 Create a Channel Configuration Transaction

注意趾痘,需要設(shè)置環(huán)境變量 CHANNEL_NAME。

創(chuàng)建 first-network/channel-artifacts/channel.tx
# The channel.tx artifact contains the definitions for our sample channel

$ export CHANNEL_NAME=mychannel  && ../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
創(chuàng)建 first-network/channel-artifacts/Org1MSPanchors.tx
$ ../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
創(chuàng)建 first-network/channel-artifacts/Org2MSPanchors.tx
$ ../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP

3.4 Start the network

如果之前利用腳本 byfn.sh 啟動(dòng)了測(cè)試網(wǎng)絡(luò)蔓钟,需要先關(guān)閉永票,如執(zhí)行下列命令

$ sudo ./byfn.sh down

3.4.1 Start the network

$ sudo docker-compose -f docker-compose-cli.yaml up -d

或通過下面的命令啟動(dòng),能夠立刻顯示日志奋刽,但需要另啟一個(gè)窗口運(yùn)行下面的 cli 容器瓦侮。

$ sudo docker-compose -f docker-compose-cli.yaml up

3.4.2 Create & Join Channel

$ sudo docker exec -it cli bash
root@33ab5acc5622:/opt/gopath/src/github.com/hyperledger/fabric/peer#

在容器 cli 中需要設(shè)置的環(huán)境變量。

# Environment variables for PEER0

$ 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
$ 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
$ export CHANNEL_NAME=mychannel

# the channel.tx file is mounted in the channel-artifacts directory within your CLI container
# as a result, we pass the full path for the file
# we also pass the path for the orderer ca-cert in order to verify the TLS handshake
# be sure to export or replace the $CHANNEL_NAME variable appropriately

$ peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

上述命令將會(huì)生成文件 <CHANNEL_NAME.block>佣谐,這里是 mychannel.block肚吏。

將 peer0.org1.example.com 加入到 channel
# By default, this joins ``peer0.org1.example.com`` only
# the <CHANNEL_NAME.block> was returned by the previous command
# if you have not modified the channel name, you will join with mychannel.block
# if you have created a different channel name, then pass in the appropriately named block

$ peer channel join -b mychannel.block
將 peer0.org2.example.com 加入到 channel
$ 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:9051
$ 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
$ peer channel join -b mychannel.block

3.4.3 Update the anchor peers

將 Org1 定義為 peer0.org1.example.com
$ 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
$ 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
$ peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
將 Org2 定義為 peer0.org2.example.com
$ 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:9051
$ 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
$ peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

3.4.4 Install & Instantiate Chaincode

Chaincode 代碼支持 golang, node, java 語言,默認(rèn)為 golang狭魂,本示例中全部使用默認(rèn)值罚攀。

Install peer0 in Org1

首先設(shè)置 Org1 相關(guān)的環(huán)境變量党觅。

$ 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:9051
$ 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
# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
$ peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
Install peer0 in Org2

首先設(shè)置 Org2 相關(guān)的環(huán)境變量。

# Environment variables for PEER0 in Org2

$ 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:9051
$ 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
# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
$ peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
Instantiate the chaincode on the channel
# be sure to replace the $CHANNEL_NAME environment variable if you have not exported it
# if you did not install your chaincode with a name of mycc, then modify that argument as well
$ export CHANNEL_NAME=mychannel
$ peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

注意斋泄,-P "AND ('Org1MSP.peer','Org2MSP.peer')" 中的 AND 可以改為 OR杯瞻,這意味著只需要 Org1 或 Org2 其中一個(gè)組織背書就可以了。

3.4.5 Query

# be sure to set the -C and -n flags appropriately

$ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

3.4.6 Invoke

# be sure to set the -C and -n flags appropriately

$ peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'

3.4.7 Query

# be sure to set the -C and -n flags appropriately

$ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

返回結(jié)果

Query Result: 90

3.4.8 Install peer1 in Org2

首先設(shè)置好環(huán)境變量炫掐。

# Environment variables for PEER1 in Org2

$ 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=peer1.org2.example.com:10051
$ CORE_PEER_LOCALMSPID="Org2MSP"
$ CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt
# this installs the Go chaincode. For go chaincode -p takes the relative path from $GOPATH/src
$ peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

3.4.9 Query by peer1 in Org2

首先需要將 peer1 in Org2 加入到 channel魁莉,之后才能響應(yīng)查詢。

$ 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=peer1.org2.example.com:10051
$ CORE_PEER_LOCALMSPID="Org2MSP"
$ CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt
$ peer channel join -b mychannel.block
# be sure to set the -C and -n flags appropriately

$ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

返回結(jié)果

Query Result: 90

3.4.10 How do I see these transactions?

$ sudo docker logs -f cli

3.4.11 How can I see the chaincode logs?

$ sudo docker logs dev-peer0.org2.example.com-mycc-1.0
ex02 Init
Aval = 100, Bval = 200
ex02 Invoke
Query Response:{"Name":"a","Amount":"100"}
ex02 Invoke
Aval = 90, Bval = 210
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}

$ sudo docker logs dev-peer0.org1.example.com-mycc-1.0
ex02 Invoke
Aval = 90, Bval = 210

$ sudo docker logs dev-peer1.org2.example.com-mycc-1.0
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}

3.5 Using CouchDB

$ sudo docker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml up -d

Reference

項(xiàng)目源代碼

項(xiàng)目源代碼會(huì)逐步上傳到 Github募胃,地址為 https://github.com/windstamp旗唁。

Contributor

  1. Windstamp, https://github.com/windstamp
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痹束,隨后出現(xiàn)的幾起案子检疫,更是在濱河造成了極大的恐慌,老刑警劉巖祷嘶,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屎媳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡论巍,警方通過查閱死者的電腦和手機(jī)烛谊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來环壤,“玉大人晒来,你說我怎么就攤上這事≈O郑” “怎么了湃崩?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)接箫。 經(jīng)常有香客問我攒读,道長(zhǎng),這世上最難降的妖魔是什么辛友? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任薄扁,我火速辦了婚禮,結(jié)果婚禮上废累,老公的妹妹穿的比我還像新娘邓梅。我一直安慰自己,他們只是感情好日缨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掖看,像睡著了一般匣距。 火紅的嫁衣襯著肌膚如雪面哥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天毅待,我揣著相機(jī)與錄音尚卫,去河邊找鬼。 笑死尸红,一個(gè)胖子當(dāng)著我的面吹牛吱涉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播外里,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼邑飒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了级乐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤县匠,失蹤者是張志新(化名)和其女友劉穎风科,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乞旦,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贼穆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兰粉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片故痊。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖玖姑,靈堂內(nèi)的尸體忽然破棺而出愕秫,到底是詐尸還是另有隱情,我是刑警寧澤焰络,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布戴甩,位于F島的核電站,受9級(jí)特大地震影響闪彼,放射性物質(zhì)發(fā)生泄漏甜孤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一畏腕、第九天 我趴在偏房一處隱蔽的房頂上張望缴川。 院中可真熱鬧,春花似錦描馅、人聲如沸把夸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扎即。三九已至吞获,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谚鄙,已是汗流浹背各拷。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闷营,地道東北人烤黍。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像傻盟,于是被迫代替她去往敵國(guó)和親速蕊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容