Hyperledger Fabric1.4.6 环境部署

一 准备工作

命令手册

1.1 Ubuntu

版本:18.04

1.2 go

版本 : 1.14.2

  • go 环境变量配置

vim /etc/profile
export GOROOT=/opt/go
export GOPATH=/opt/gocode
export PATH=GOROOT/bin:PATH
export GOBIN=$GOPATH/bin

  • 刷新环境变量
source /etc/profile
  • 解压 SDK
tar -C /opt/go -xzvf go1.14.2.linux-amd64.tar.gz

1.3 docker

1.3.1 安装Docker-CE

  • 卸载旧版本docker
sudo apt-get remove docker docker-engine docker.io
  • 添加HTTPS协议,允许apt从HTTPS安装软件包
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
  • 安装GPG证书
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
  • 写入软件源信息
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
  • 更新并安装Docker-CE
sudo apt-get -y update
sudo apt-get -y install docker-ce
  • 将当前用户添加到Docker用户组

创建docker用户组

sudo groupadd docker
  • 将当前用户添加到docker用户组
sudo usermod -aG docker $USER
  • 将docker镜像更改为国内镜像

编辑daemon.json文件,如果没有该文件自行创建

sudo vim /etc/docker/daemon.json

文件中添加以下内容

{
"registry-mirrors":["https://obou6wyb.mirror.aliyuncs.com","https://registry.docker-cn.com","http://hub-mirror.c.163.com"]
}
  • 最后重启服务
sudo systemctl daemon-reload
sudo systemctl restart docker
  • 查看docker版本,看是否安装成功
docker version
  • 执行docker info,如果结果中含有如下内容则说明镜像配置成功
...
 Registry Mirrors:
  https://obou6wyb.mirror.aliyuncs.com/
  https://registry.docker-cn.com/
  http://hub-mirror.c.163.com/
 Live Restore Enabled: false
 ...

1.3.2 安装Docker-Compose

  • 方法一:
sudo curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

用方法一下载完后要设置权限

sudo chmod +x /usr/local/bin/docker-compose
  • 方法二:
sudo apt-get install python-pip
sudo pip install docker-compose
  • 下载完成后查看是否安装成功
docker-compose version

1.3.2 拉取fabric镜像

docker pull hyperledger/fabric-peer:1.4.6
docker pull hyperledger/fabric-tools:1.4.6
docker pull hyperledger/fabric-orderer:1.4.6
docker pull hyperledger/fabric-javaenv:1.4.6
docker pull hyperledger/fabric-ca:1.4.6
docker pull hyperledger/fabric-ccenv:1.4.6
docker pull hyperledger/fabric-zookeeper:0.4.18
docker pull hyperledger/fabric-kafka:0.4.18
docker pull hyperledger/fabric-couchdb:0.4.18
docker pull hyperledger/fabric-baseimage:0.4.18
docker pull hyperledger/fabric-baseos:0.4.18
docker pull hyperledger/fabric-membersrvc:latest 

或者

docker pull hyperledger/fabric-peer:1.4.6 && docker pull hyperledger/fabric-tools:1.4.6 && docker pull hyperledger/fabric-orderer:1.4.6 && docker pull hyperledger/fabric-javaenv:1.4.6 && docker pull hyperledger/fabric-ca:1.4.6 && docker pull hyperledger/fabric-ccenv:1.4.6 && docker pull hyperledger/fabric-zookeeper:0.4.18 && docker pull hyperledger/fabric-kafka:0.4.18 && docker pull hyperledger/fabric-couchdb:0.4.18 && docker pull hyperledger/fabric-baseimage:0.4.18 && docker pull hyperledger/fabric-baseos:0.4.18 && docker pull hyperledger/fabric-membersrvc:latest

  • 拉取完镜像镜像后,可以查看docker镜像库
docker images

  • (可忽略)

为了方便配置Docker Compose,将所有镜像的tag都改为latest,执行如下格式的命令:

docker tag IMAGEID(镜像ID) REPOSITORY:TAG(仓库:标签)

例如:

docker tag 68914607b3a5 docker.io/hyperledger/fabric-tools:latest

将标签全部更改完后,再次查看镜像库

docker images

二 Fabric命令行工具的编译安装

编译安装

2.1 创建目录并下载代码

  • 创建hyperledger目录
mkdir -p $GOPATH/src/github.com/hyperledger
cd $GOPATH/src/github.com/hyperledger
  • 下载 fabric 源码
git clone https://github.com/hyperledger/fabric
  • 下载失败,errno 54的解决方案
sudo git config --global http.postBuffer 5242880000
sudo git config --global https.postBuffer 5242880000
  • 或者,从码云上下载
git clone https://gitee.com/mirrors/hyperledger-fabric.git   

2.2 编译Fabric的模块

  • 进入到Fabirc源码所在的文件夹
cd $GOPATH/src/github.com/hyperledger/fabric
  • 切换分支v1.4.6
git checkout  v1.4.6

编译方式一

make release
  • 上述命令执行完成之后,会自动将将编译好的二进制文件存放在以下路径中:

$GOPATH/src/github.com/hyperledger/fabric/==release/linux-amd64/bin==

  • 查看版本是否符合
cd $GOPATH/src/github.com/hyperledger/fabric/release/linux-amd64/bin
./peer version

编译方式二

make orderer
make peer
make configtxlator
make certificategen 
make configtxgen

查看 ==.build/bin== 目录

ll .build/bin/

  • Fabric的模块
模块名称 功能
peer 主节点模块,负责存储区块链数据,运行维护链码
orderer 交易打包、排序模块
certificategen 组织和证书生成模块
configtxgen 区块和交易生成模块
configtxlator 区块和交易解析模块

2.4 Fabric模块的安装

就是将上述模块添加到环境变量里去

  • 第一步 Ubuntu和Centos将Fabric模块编译后的文件复制到系统文件夹中

cp $GOPATH/src/github.com/hyperledger/fabric/release/linux-amd64/bin/* /usr/local/bin

  • MacOS上面将Fabric模块编译后的文件复制到系统文件夹中的方法如下:

cp $GOPATH/src/github.com/hyperledger/fabric/release/darwin-amd64/bin/* /usr/local/bin

复制成功之后通过以下命令修改文件的执行权限,否则可能无法执行。

sudo chmod -R 775  /usr/local/bin/configtxgen
sudo chmod -R 775  /usr/local/bin/configtxlator
sudo chmod -R 775  /usr/local/bin/certificategen
sudo chmod -R 775  /usr/local/bin/peer
sudo chmod -R 775  /usr/local/bin/orderer

通过上面这些命令之后,可以在系统的任何路径下面运行这些模块了。下面通过一组命令来进检查安装过程是否成功。

  • 第二步: 检查安装

采用 version 命令行选项

peer version
orderer version

三 快速启动一个Fabric应用

3.1 第一步:生成组织结构及加密证书

证书等配置文件为crypto-config.yaml

3.1.1 创建存放证书的文件夹

  • 将生成的证书文件存放在/var/hyperledger/certificate文件夹下

mkdir -p /var/hyperledger/certificate

3.1.2 证书文件的生成命令

  • cryptogen 提供了一个命令可以获取cryptogen模块所需要的配置文件的样式,该命令如下所示:

cryptogen showtemplate

  • ==执行如下命令:==

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

该命令执行完成之后我们会发现在文件夹中会新增加一个文件夹crypto-config,里面存放有本例的相关配置文件,可以通过tree命令查看生成证书文件的内容。

org1.binny.com
org2.binny.com

3.1.3 节点域名映射

通过上述步骤所有的证书文件都已经生成完毕,现在需要将测试域名映射到本机的IP地址上面,否则后面的操作可能会出现错误。

执行以下命令以便提取相关的域名,

cd /var/hyperledger/certificate
tree -L 5

使用上述命令就可观察发现,一个orderer 域名和 2 个 peer 域名

  • 打开端映射文件,将域名代理为本机
vi /etc/hosts
192.168.23.79 peer0.org1.binny.com
192.168.23.79 peer0.org2.binny.com
192.168.23.79 peer1.org2.binny.com
192.168.23.79 peer1.org1.binny.com
192.168.23.79 orderer.binny.com
orderer.binny.com
peer0.org1.binny.com
peer0.org2.binny.com

输入以上内容之后保存/etc/hosts文件,然后用ping命令测试以下配置是否正确。

ping peer0.org1.binny.com

3.2 第二步 创建orderer 服务,启动创始块的生成

创建服务启动初始区块及应用通道交易配置文件需要指定 Orderer 服务的相关配置以及当前的联盟信息, 这些信息定义在一个名为 configtx.yaml 文件中。

3.2.1 Orderer服务启动初始区块的创建

  • ==创建 Orderer 服务启动初始区块==

cd /var/hyperledger/certificate
export set FABRIC_CFG_PATH=/var/hyperledger/certificate

  • ==生成创始块文件==

configtxgen -profile TestTwoOrgsOrdererGenesis -outputBlock ./orderer.genesis.block

注意:TestTwoOrgsOrdererGenesis:在配置文件中,orderer.genesis.block:名字随意,但是后续配置文件中要用到

上述命令执行完成之后会在文件夹$GOPATH/src/github.com/hyperledger/certificate中生成文件orderer.genesis.block。这是Fabric系统的创始块文件。

3.2.2 创建必须的应用通道交易配置文件

  • 指定通道名称的环境变量,名字随意,==设置临时变量,channel 名字==

export set CHANNEL_NAME=binnychannel

因为我们后面的命令需要多次使用同一个通道名称,所以先指定一个通道名称将其设为环境变量,后期需要使用该通道名称时时只需要使用对应的环境变量名称即可.


  • ==生成应用通道交易配置文件==

指定使用 configtx.yaml 配置文件中的 TestTwosOrgsChannel 模板, 来生成新建通道的配置交易文件,TestTwosOrgsChannel 模板指定了 Org1 和 Org2 都属于后面新建的应用通道

configtxgen -profile TestTwosOrgsChannel -outputCreateChannelTx ./binnychannel.tx -channelID $CHANNEL_NAME

  • ==node-sdk 测试,创建新通道==

configtxgen -profile TestOneOrgsChannel -outputCreateChannelTx ./binnychannel01.tx -channelID binnychannel01

3.2.3 生成锚节点更新配置文件

同样基于 configtx.yaml 配置文件中的 TestTwosOrgsChannel 模板,为每个组织分别生成锚节点更新配置,且注意指定对应的组织名称。

  • channelID 要和上面的一样
configtxgen -profile TestTwosOrgsChannel -outputAnchorPeersUpdate ./Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
configtxgen -profile TestTwosOrgsChannel -outputAnchorPeersUpdate ./Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP

上面命令执行完成之后会在相应的文件夹下面生成文件 Org1MSPanchors.txOrg2MSPanchors.tx ,这些文件在后面会被使用到。

3.2.4 Orderer节点的启动

在配置文件orderer.yaml所在的目录执行如下命令启动orderer

orderer start
  • 问题:Not bootstrapping because of 2 existing channels

删除目录下的orderer遗留文件

3.3 Peer节点的启动

Please make sure that FABRIC_CFG_PATH is set to a path which contains core.yaml

设置环境变量

export set FABRIC_CFG_PATH=/var/hyperledger/certificate

启动命令

  • 在配置文件core.yaml所在的文件夹中执行以下命令启动order节点

peer node start >> log_peer.log 2>&1 &


ps axu | grep peer

3.4 创建通道

现在我们可以创建通道,创建通道的过程一共需要三个步骤。

3.4.1 设置环境变量

cd $GOPATH/src/github.com/hyperledger/certificate

export set CHANNEL_NAME=binnychannel
export set CORE_PEER_MSPCONFIGPATH=/var/hyperledger/certificate/crypto-config/peerOrganizations/org1.binny.com/users/Admin@org1.binny.com/msp
export set CORE_PEER_LOCALMSPID=Org1MSP

3.4.2 创建通道

peer channel create -t 50s -o orderer.binny.com:7050 -c $CHANNEL_NAME -f /var/hyperledger/certificate/binnychannel.tx

  • -f:指定上次生成的那个文件路径
2020-05-12 01:32:47.537 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-05-12 01:32:47.568 UTC [cli.common] readBlock -> INFO 002 Received block: 0

创建通道完成之后,会在执行命令的当前目录生成名为binnychannel.block的通道创始块文件

<font color="red">

  • 查看已开启的端口
netstat -tupln

  • ==查看channel.tx和genesis.block内容==
configtxgen -inspectChannelCreateTx binnychannel.tx
configtxgen -inspectBlock genesis.block

选项 意义
-o 连接的orderer的地址,hostname:port
-c channel的名称,默认为mychannel
-f 配置的交易信息(暂时还没搞清楚)
–tls 和orderer通信时是否启用tls
–cafile 使用tls时,所使用的orderer的证书

</font>

3.4.3 让已经运行的Peer模块加入通道

export set CORE_PEER_LOCALMSPID=Org1MSP
export set CORE_PEER_ADDRESS=peer0.org1.binny.com:7051
export set CORE_PEER_MSPCONFIGPATH=/var/hyperledger/certificate/crypto-config/peerOrganizations/org1.binny.com/users/Admin@org1.binny.com/msp

peer channel join -b /var/hyperledger/certificate/binnychannel.block

在上述创建通道的命令中-b后面的参数为==上一步==中生成的文件binnychannel.block,需要注意这个文件的路径。

peer channel list

3.4.4 更新锚节点

export set CORE_PEER_LOCALMSPID=Org1MSP
export set CORE_PEER_ADDRESS=peer0.org1.binny.com:7051
export set CORE_PEER_MSPCONFIGPATH=/var/hyperledger/certificate/crypto-config/peerOrganizations/org1.binny.com/users/Admin@org1.binny.com/msp

peer channel update -o orderer.binny.com:7050 -c binnychannel   -f  /var/hyperledger/certificate/Org1MSPanchors.tx

3.5 Chaincode的部署和调用

现在可以部署一个Chaincode(关于Chaincode的详细内容在本书的第七章会有详细的介绍)来测试Peer节点和Orderer节点的部署是否正确。这里采用Fabric源码自带的例子来作为测试Chaincode。测试用Chaincode的源代码路径如下所示:

$GOPATH/src/github.com/hyperledger/fabric-samples/chaincode/chaincode_example02

Chaincode相关的测试一共有四个步骤。

第一步:设置环境变量

export set CORE_PEER_LOCALMSPID=Org1MSP
export set CORE_PEER_ADDRESS=peer0.org1.binny.com:7051
export set CORE_PEER_MSPCONFIGPATH=/var/hyperledger/certificate/crypto-config/peerOrganizations/org1.binny.com/users/Admin@org1.binny.com/msp

第二步: 部署chaincode代码

  • ==使用的chaincode的位置如下==:

$GOPATH/src/github.com/hyperledger/fabric/examples/chaincode/go/example02/cmd


  • ==安装部署==
peer chaincode install -n binny_test_01 -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/example02/cmd
2020-05-12 07:21:24.559 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2020-05-12 07:21:24.559 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2020-05-12 07:21:25.482 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" > 
  • == 可以通过下面命令查看 chaincode的安装信息==
peer chaincode list --installed  

第三步: 实例化chaincode代码

  • 执行下面的步骤,需要系统中已安装 docker

export set CHANNEL_NAME=binnychannel


peer chaincode instantiate -o orderer.binny.com:7050 -C $CHANNEL_NAME -n binny_test_01 -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR('Org1MSP.member','Org2MSP.member')"

第四步:通过chaincode写入数据

  • ==设置临时变量,channel 名字==

export set CHANNEL_NAME=binnychannel
export set CORE_PEER_LOCALMSPID=Org1MSP

export set CORE_PEER_ADDRESS=peer0.org1.binny.com:7051
export set CORE_PEER_MSPCONFIGPATH=/var/hyperledger/certificate/crypto-config/peerOrganizations/org1.binny.com/users/Admin@org1.binny.com/msp


peer chaincode invoke -o orderer.binny.com:7050 -C $CHANNEL_NAME -n binny_test_01 -c '{"Args":["invoke","a","b","1"]}'

第五步:通过chaincode查询数据

export set CORE_PEER_LOCALMSPID=Org1MSP
export set CORE_PEER_ADDRESS=peer0.org1.binny.com:7051
export set CORE_PEER_MSPCONFIGPATH=$GOPATH/src/github.com/hyperledger/certificate/certificate-config/peerOrganizations/org1.binny.com/users/Admin@org1.binny.com/msp


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

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

如果上述命令都能正确执行,那么一个简单的Fabric系统就已经部署完成了。

五 自定义 chaincode

将写好的 chaincode 同步到节点服务器上

5.1 调试模式(IDE中设置)

  • 设置参数
-peer.address=192.168.35.131:7052
  • 配置环境变量
CORE_PEER_ADDRESS=192.168.35.131:7051
CORE_CHAINCODE_ID_NAME=helloworld_1222:1.0//chaincode名字:版本号
CORE_CHAINCODE_LOGGING_LEVEL=debug
CORE_CHAINCODE_LOGGING_SHIM=debug

5.2 进入节点服务

5.2.1 设置环境变量

  • 设置 MSP
export set CORE_PEER_LOCALMSPID=Org1MSP
  • 设置证书路径
export set CORE_PEER_MSPCONFIGPATH=$GOPATH/src/github.com/hyperledger/certificate/crypto-config/peerOrganizations/org1.binny.com/users/Admin@org1.binny.com/msp
  • 设置 core.yaml所在路径
export set FABRIC_CFG_PATH=$GOPATH/src/github.com/hyperledger/certificate
  • 设置一个节点地址
export set CORE_PEER_ADDRESS=peer0.org1.binny.com:7051
  • 设置日志级别
export set FABRIC_LOGGING_SPEC=DEBUG
  • 设置日志级别
export set CORE_LOGGING_LEVEL=DEBUG

5.2.2 打包 chaincode

在以上环境变量的基础上执行下列代码,将指定的链码打包为CDS(Chaincode Deployment Spec)格式,并存入节点上指定的路径。

peer chaincode install -n helloworld_1222 -v 1.0 -p chaincode
  • ==选项说明:==
  • --connectionProfile Connection profile that provides the necessary connection information for the network. Note: currently only supported for providing peer connection information
  • -c, --ctor Constructor message for the chaincode in JSON format (default "{}")
  • -h, --help help for install
  • -l, --lang Language of chaincode, either "golang" (default), "node", or "java"
  • -n, --name Name of the chaincode
  • -p, --path Path to chaincode, for "golang" use relative path from $GOPATH/src, for "node" or "java" use absolute path
  • -v, --version Version of the chaincode specified in install/instantiate/upgrade commands

  • 查看一下
peer chaincode list --installed
  • 输出结果
Get installed chaincodes on peer:
Name: binny_test_01, Version: 1.0, Path: github.com/hyperledger/fabric/examples/chaincode/go/example02/cmd, Id: c405e9d12c9adc4a8773786de35bababed8d41ca8242d00ba402d932d934805e
Name: helloworld_1222, Version: 1.0, Path: fabric_cc_study/1.4.6/binny, Id: 6f9b8c6fff1951402b97ef685741aba144af89f0ab026ad0a31d0d1ce5e73f20

5.2.3 将指定的链码部署到网络上。

执行下面命令会调用 Init方法

peer chaincode instantiate -o orderer.binny.com:7050 -C binnychannel -n helloworld_1222 -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
  • 执行以下命名,查看一下
peer chaincode list --instantiated -C binnychannel

5.2.4 调用指定给的链码,并初始化参数

  • 调用 set 方法
peer chaincode invoke -o orderer.binny.com:7050  --waitForEvent -C binnychannel -n helloworld_1222  -c '{"Args":["set","name","徐耀,曹欢","fabric--2019-12-22"]}'
  • 调用get方法
peer chaincode query -C binnychannel -n helloworld_1222 -c '{"Args":["get","name","参数 2","参数3"]}'

5.2.5 调用get方法

获取并显示链码函数调用的背书结果。该命令不会生成交易。

peer chaincode query -C binnychannel -n helloworld_1222 -c '{"Args":["get","get","key1222","chaincode v 1.5 = query11111"]}'
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343