EOS基礎(chǔ)全家桶(十三)智能合約基礎(chǔ)

簡(jiǎn)介

智能合約是現(xiàn)在區(qū)塊鏈的一大特色,而不同的鏈?zhǔn)褂玫闹悄芎霞s的虛擬機(jī)各不相同船响,編碼語言也有很大差異躬拢。而今天我們開始學(xué)習(xí)EOS的智能合約,我也是從EOS初期一直開發(fā)合約至今见间,期間踩過無數(shù)坑聊闯,也在Stack Overflow上提過問(最后自己解決了),在實(shí)際生產(chǎn)中也積累了很多經(jīng)驗(yàn)米诉,所以我會(huì)連續(xù)幾周分多次分享合約開發(fā)的經(jīng)驗(yàn)菱蔬,今天先來點(diǎn)基礎(chǔ)的。

一些C++的編程基礎(chǔ)

EOS就是使用C++開發(fā)的史侣,這也為它帶來了諸多好處拴泌,而合約也沿用C++作為開發(fā)語言,雖然合約中無法直接使用Boost等框架(你可以自己引入抵窒,但這也意味著合約會(huì)很大弛针,會(huì)占用大量賬號(hào)的內(nèi)存),但是我們還是可以使用很多C++的小型庫李皇,并伴隨著eosio.cdt的發(fā)展,融入了更多實(shí)用的合約功能宙枷。

如果你之前沒有使用C系列的開發(fā)語言做過開發(fā)掉房,比如:C語言、C++或者是C#慰丛,那么你需要先學(xué)習(xí)下C語言的基本語法和數(shù)據(jù)結(jié)構(gòu)卓囚,這里我不做展開,在我們的系列文章的開篇就介紹了我推薦的Learn EOS - c/c++ 教程英文版诅病,有一定英語基礎(chǔ)的朋友可以直接看這個(gè)哪亿,其他朋友也可以在網(wǎng)上找一些C++的入門教程看下。

如果你已經(jīng)有了一定的C語言基礎(chǔ)贤笆,那么寫合約的話蝇棉,你會(huì)發(fā)現(xiàn)需要的基礎(chǔ)也并不多,依葫蘆畫瓢就能寫出各種基礎(chǔ)功能了芥永,所以篡殷,你并不需要擔(dān)心太多語言上的門檻,畢竟合約只是一個(gè)特定環(huán)境下運(yùn)行的程序埋涧,你能用到的東西并不會(huì)很多板辽。

CDT選擇

EOS的早期版本進(jìn)行合約開發(fā)還沒有CDT工具奇瘦,那時(shí)的合約借助的是源碼中的工具eosiocpp,所以你看2018年的博客劲弦,進(jìn)行合約編譯都是用它耳标,但你現(xiàn)在是見不到了。隨著官方CDT的迭代邑跪,在CDT的1.4版本開始被官方推薦使用次坡,CDT后面也經(jīng)歷了幾個(gè)大的版本更新,逐步改善合約編寫方式呀袱,更加趨于簡(jiǎn)潔贸毕、直觀。

但是不同的CDT版本夜赵,也意味著編譯器的不同明棍,所以合約開發(fā)也會(huì)有所區(qū)別,比如一些語法變了寇僧,一些庫名稱變了摊腋,增加了一些新的標(biāo)注……

我們的教程側(cè)重還是介紹最新的語法,所以推薦使用1.6以上的版本嘁傀。我也會(huì)盡量在后面的介紹中補(bǔ)充說明老的CDT的寫法兴蒸,方便大家對(duì)照網(wǎng)上其他老博客的合約。

來個(gè)HelloWorld

學(xué)習(xí)任何編程细办,我們都不能少了Mr.HelloWorld橙凳,先來給大家打個(gè)招呼吧。

#include <eosio/eosio.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public contract
{
public:
    using contract::contract;

    [[eosio::action]] void hi(name user)
    {
        print("Hello, ", user);
    }
};

基本合約結(jié)構(gòu)及類型

hello合約就是一個(gè)最簡(jiǎn)單的合約了笑撞,而且還有一個(gè)可調(diào)用的action為hi岛啸。我們首先還是來介紹下一個(gè)合約的程序結(jié)構(gòu)吧。

  • 程序頭

包含了引入的頭文件茴肥、庫文件等坚踩,還有全局的命名空間的引入等。

#include <eosio/eosio.hpp>

using namespace eosio;

這里eosio庫是我們的合約基礎(chǔ)庫瓤狐,所有和eos相關(guān)的類型和方法瞬铸,都在這個(gè)庫里面,而這個(gè)庫里面eosio.hpp是基礎(chǔ)础锐,包含了contract等的定義嗓节,所以所有的合約都要引入。

【CDT老版本】早期cdt版本中庫名稱不是eosio郁稍,而是eosiolib

默認(rèn)的赦政,我們引入了eosio的命名空間,因?yàn)閑osio的所有內(nèi)容都是在這個(gè)命名空間下的,所以我們?nèi)忠牖肿牛瑫?huì)方便我們后續(xù)的代碼編寫桐愉。

  • 合約類定義

其實(shí)就是定義了一個(gè)class,繼承contract掰派,并通過[[eosio::contract]]標(biāo)注這個(gè)類是一個(gè)合約从诲。使用using引入contract也是為了后續(xù)代碼可以更簡(jiǎn)潔。

class [[eosio::contract]] hello : public contract{
public:
    using contract::contract;
}

【CDT老版本】早期cdt版本中直接使用了CONTRACT來定義合約類靡羡,比如:CONTRACT hello: public contract {}

  • action定義

寫一個(gè)public的方法系洛,參數(shù)盡量用簡(jiǎn)單或者是eosio內(nèi)置的類型定義,無返回值(合約調(diào)用無法返回任何結(jié)果略步,除非報(bào)錯(cuò))描扯,然后在用[[eosio::action]]標(biāo)注這個(gè)方法是一個(gè)合約action就行。

注意:action的名稱要求符合name類型的規(guī)則趟薄,name規(guī)則請(qǐng)看下面的常用類型中的說明绽诚。

[[eosio::action]]
void hi( name user ) {
    print( "Hello, ", user);
}

因?yàn)楹霞s無法調(diào)試,所以只能通過print來打印信息杭煎,或者直接通過斷言拋出異常來進(jìn)行調(diào)試恩够。

【CDT老版本】早期cdt版本中直接使用ACTION來定義方法,比如:ACTION hi( name user ){}

  • 常用類型
類型 說明 示例
name 名稱類型羡铲,賬號(hào)名蜂桶、表名、action名都是該類型也切,只能使用26個(gè)小寫字母和1到5的數(shù)字扑媚,特殊可以使用小數(shù)點(diǎn),總長(zhǎng)不超過13雷恃。 name("hi") 或者 "hi"_n
asset 資產(chǎn)類型钦购,Token都是使用該類型,包含了Token符號(hào)和小數(shù)位褂萧,是一個(gè)復(fù)合類型,字符形式為1.0000 EOS asset(10000, symbol("TADO", 4)就是1.0000 TADO)
uint64_t 無符號(hào)64位整型葵萎,主要數(shù)據(jù)類型导犹,表主鍵、name實(shí)質(zhì)都是改類型 uint64_t amount = 10000000;
  • 內(nèi)置常用對(duì)象或方法

在合約中羡忘,contract基類提供了一些方便的內(nèi)置對(duì)象谎痢。

首先是get_self()或者是_self,這個(gè)方法可以獲取到當(dāng)前合約所在的賬號(hào)督勺,比如你把hello合約部署到了helloworld111這個(gè)賬號(hào)纤房,那么get_self()就可以獲取到helloworld111睛竣。

然后是get_code()或者是_code罩抗,這個(gè)方法可以獲取到當(dāng)前交易請(qǐng)求的action方法名滨嘱,這個(gè)在進(jìn)行內(nèi)聯(lián)action調(diào)用時(shí)可以用于判斷入口action峰鄙。

最后是get_datastream()或者_ds,這個(gè)方法獲取的是數(shù)據(jù)流太雨,如果你使用的是復(fù)雜類型吟榴,或者是自定義類型,那么你無法在方法的參數(shù)上直接獲取到反序列化的變量值囊扳,你必須自己通過數(shù)據(jù)流來解析吩翻。

常用的還有獲取當(dāng)前時(shí)間current_time_point(),這個(gè)需要引入#include <eosio/transaction.hpp>锥咸。

數(shù)據(jù)持久化

當(dāng)然狭瞎,合約里面,我們總會(huì)有些功能需要把數(shù)據(jù)存下來搏予,在鏈上持久化存儲(chǔ)熊锭。所以我們就需要定義合約表了。

合約的表存在相應(yīng)的合約賬號(hào)中缔刹,可以劃分表范圍(scope)球涛,每個(gè)表都有一個(gè)主鍵,uint64_t類型的校镐,還可以有多個(gè)其他索引亿扁,表的查詢都是基于索引的。

這里先提一句鸟廓,表數(shù)據(jù)所占用的內(nèi)存从祝,默認(rèn)是合約賬號(hào)的內(nèi)存,也可以使用其他賬號(hào)的引谜,但需要權(quán)限牍陌,這個(gè)以后我們?cè)俳榻B。

我們擴(kuò)展一下hello合約员咽。

#include <eosio/eosio.hpp>
#include <eosio/transaction.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public contract
{
public:
    using contract::contract;

    hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds), friend_table(get_self(), get_self().value)
    {
    }

    [[eosio::action]] void hi(name user)
    {
        print("Hello, ", user);

        uint32_t now = current_time_point().sec_since_epoch();

        auto friend_itr = friend_table.find(user.value);
        if (friend_itr == friend_table.end())
        {
            friend_table.emplace(get_self(), [&](auto &f) {
                f.friend_name = user;
                f.visit_time = now;
            });
        }
        else
        {
            friend_table.modify(friend_itr, get_self(), [&](auto &f) {
                f.visit_time = now;
            });
        }
    }

    [[eosio::action]] void nevermeet(name user)
    {
        print("Never see you again, ", user);

        auto friend_itr = friend_table.find(user.value);
        check(friend_itr != friend_table.end(), "I don't know who you are.");

        friend_table.erase(friend_itr);
    }

private:
    struct [[eosio::table]] my_friend
    {
        name friend_name;
        uint64_t visit_time;

        uint64_t primary_key() const { return friend_name.value; }
    };

    typedef eosio::multi_index<"friends"_n, my_friend> friends;

    friends friend_table;
};

可以看到毒涧,我們已經(jīng)擴(kuò)充了不少東西了,包括構(gòu)造函數(shù)贝室,表定義契讲,多索引表配置,并完善了原先的hi方法滑频,增加了nevermeet方法捡偏。

我們現(xiàn)在模擬的是這樣一個(gè)使用場(chǎng)景,我們遇到一個(gè)朋友的時(shí)候峡迷,就會(huì)和他打招呼(調(diào)用hi)银伟,如果這個(gè)朋友是一個(gè)新朋友,就會(huì)插入一條記錄到我們的朋友表中,如果是一個(gè)老朋友了彤避,我們就會(huì)更新這個(gè)朋友的記錄中的訪問時(shí)間傅物。當(dāng)我們決定不再見這個(gè)朋友了,就是絕交了(調(diào)用nevermeet)忠藤,我們就會(huì)把這個(gè)朋友的記錄刪除挟伙。

  • 表定義

首先我們需要聲明我們的朋友表。定義一個(gè)結(jié)構(gòu)體模孩,然后用[[eosio::table]]標(biāo)注這個(gè)結(jié)構(gòu)體是一個(gè)合約表尖阔。在結(jié)構(gòu)體里定義一個(gè)函數(shù)名primary_key,返回uint64_t類型榨咐,作為主鍵的定義介却。

private:
    struct [[eosio::table]] my_friend
    {
        name friend_name;
        uint64_t visit_time;

        uint64_t primary_key() const { return friend_name.value; }
    };

我們這里聲明了一個(gè)my_friend的表,合約的表名不在這里定義块茁,所以結(jié)構(gòu)體的名稱不必滿足name的規(guī)則齿坷。我們定義了兩個(gè)字段,friend_name(朋友的名稱)和visit_time(拜訪時(shí)間)数焊,主鍵我們直接使用了friend_name永淌,這個(gè)字段是name類型的,而name類型的實(shí)質(zhì)就是一個(gè)uint64_t的類型(所以name的規(guī)則那么苛刻)佩耳。

【CDT老版本】早期cdt版本中直接使用TABLE來定義合約表遂蛀,比如:TABLE my_friend{}

  • 多索引表配置

合約里的表都是通過多索引來定義的,這是合約表的結(jié)構(gòu)基礎(chǔ)干厚。所以這里才是定義表名和查詢索引的地方李滴。

typedef eosio::multi_index<"friends"_n, my_friend> friends;

我們現(xiàn)在只介紹最簡(jiǎn)單的單索引的定義,以后再介紹多索引的定義方式蛮瞄,這里的"friends"_n就是定義表名所坯,所以使用了name類型,之后my_friend是表的結(jié)構(gòu)類型挂捅,typedef實(shí)質(zhì)上就是聲明了一個(gè)類型別名芹助,名字是friends的類型。

  • 構(gòu)造函數(shù)

構(gòu)造函數(shù)這里并不是必須闲先,但是為了我們能在全局直接使用合約表周瞎,所以我們要在構(gòu)造函數(shù)進(jìn)行表對(duì)象的實(shí)例化。

public:
    hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds), friend_table(get_self(), get_self().value)
    {
    }

private:
    friends friend_table;

這一段是標(biāo)準(zhǔn)合約構(gòu)造函數(shù)饵蒂,hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds),合約類型實(shí)例化時(shí)會(huì)傳入receiver也就是我們的合約賬號(hào)(一般情況下)酱讶,code就是我們的action名稱退盯,ds就是數(shù)據(jù)流。

friend_table(get_self(), get_self().value)這一段就是對(duì)我們定義的friend_table變量的實(shí)例化,friend_table變量就是我們定義的多索引表的friends類型的實(shí)例渊迁。在合約里我們就可以直接使用friend_table變量來進(jìn)行表操作了慰照。實(shí)例化時(shí)傳遞的兩個(gè)參數(shù)正是表所在合約的名稱和表范圍(scope),這里都使用的是當(dāng)前合約的名稱琉朽。

  • 查詢記錄

查詢有多種方式毒租,也就是多索引表提供了多種查詢的方式,默認(rèn)的箱叁,使用findget方法是直接使用主鍵進(jìn)行查詢墅垮,下次我們會(huì)介紹使用第二、第三等索引來進(jìn)行查詢耕漱。find返回的是指針算色,數(shù)據(jù)是否存在,需要通過判斷指針是否是指到了表末尾螟够,如果等于表末尾灾梦,就說明數(shù)據(jù)不存在,否則妓笙,指針的值就是數(shù)據(jù)對(duì)象若河。get直接返回的就是數(shù)據(jù)對(duì)象,所以在調(diào)用get時(shí)寞宫,就必須傳遞數(shù)據(jù)不存在時(shí)的錯(cuò)誤信息萧福。

auto friend_itr = friend_table.find(user.value);
if (friend_itr == friend_table.end())
{
    //數(shù)據(jù)不存在
}else
{
    //數(shù)據(jù)存在
}

我們?cè)趆i方法中先查詢了user是否存在。如果不存在淆九,我們就添加數(shù)據(jù)统锤,如果存在了,就修改數(shù)據(jù)中的visit_time字段的值為當(dāng)前時(shí)間炭庙。

  • 添加記錄

多索引的表對(duì)象添加記錄使用emplace方法饲窿,第一個(gè)參數(shù)就是內(nèi)存使用的對(duì)象,第二個(gè)參數(shù)就是添加表對(duì)象時(shí)的委托方法焕蹄。

uint32_t now = current_time_point().sec_since_epoch();

auto friend_itr = friend_table.find(user.value);
if (friend_itr == friend_table.end())
{
    friend_table.emplace(get_self(), [&](auto &f) {
        f.friend_name = user;
        f.visit_time = now;
    });
}
else
{
    //數(shù)據(jù)存在
}

這里先定義了一個(gè)變量now來表示當(dāng)前時(shí)間逾雄,正是使用的內(nèi)置方法current_time_point(),這個(gè)還是用了它的sec_since_epoch()方法腻脏,是為了直接獲取秒單位的值鸦泳。

我們查詢后發(fā)現(xiàn)這個(gè)user的數(shù)據(jù)不存在,所以就進(jìn)行插入操作永品,內(nèi)存直接使用的合約賬號(hào)的做鹰,所以使用get_self(),然后對(duì)表數(shù)據(jù)對(duì)象進(jìn)行賦值鼎姐。

  • 修改記錄

多索引的表對(duì)象修改記錄使用modify方法钾麸,第一個(gè)參數(shù)是傳遞需要修改的數(shù)據(jù)指針更振,第二個(gè)參數(shù)是內(nèi)存使用的對(duì)象,第二個(gè)參數(shù)就是表對(duì)象修改時(shí)的委托方法饭尝。

friend_table.modify(friend_itr, get_self(), [&](auto &f) {
    f.visit_time = now;
});

我們將查詢到的用戶對(duì)象的指針friend_itr傳入肯腕,然后內(nèi)存還是使用合約賬號(hào)的,委托中钥平,我們只修改visit_time的值(主鍵是不能修改的)实撒。

  • 刪除記錄
  • 多索引的表對(duì)象刪除記錄使用erase方法,只有一個(gè)參數(shù)涉瘾,就是要?jiǎng)h除的對(duì)象指針知态,有返回值,是刪除數(shù)據(jù)后的指針偏移睡汹,也就是下一條數(shù)據(jù)的指針肴甸。
auto friend_itr = friend_table.find(user.value);
check(friend_itr != friend_table.end(), "I don't know who you are.");

friend_table.erase(friend_itr);

我們的示例中,將查詢到的這條數(shù)據(jù)直接刪除囚巴,并為使用變量來接收下一條數(shù)據(jù)的指針原在,在連續(xù)刪除數(shù)據(jù)時(shí),你會(huì)需要獲取下一條數(shù)據(jù)的指針彤叉,因?yàn)橐褎h除的數(shù)據(jù)的指針已經(jīng)失效了庶柿。

編譯

編譯我們?cè)僦耙灿羞^介紹,安裝了eosio.cdt后秽浇,我們就有了eosio-cpp命令浮庐,進(jìn)入到合約文件夾中,直接執(zhí)行以下命令就會(huì)在當(dāng)前目錄生成wasm和abi文件柬焕。

eosio-cpp -abigen hello.cpp -o hello.wasm

注意:替換命令中使用的hello.cpp為實(shí)際合約代碼文件名审残,而hello.wasm為實(shí)際合約的wasm文件名。

當(dāng)然斑举,編譯不通過的時(shí)候搅轿,你就要看看錯(cuò)誤是什么了,這可能會(huì)考驗(yàn)一下你的C++功底富玷。

發(fā)布

決定了要發(fā)布的賬號(hào)后璧坟,記得要購(gòu)買足夠的內(nèi)存和抵押足夠的資源。合約的內(nèi)存消耗我們可以大致這樣估算赎懦,看下編譯好了的合約wasm文件有多大雀鹃,然后乘以10,就是你發(fā)布到鏈上大概所需的內(nèi)存大小了励两。

發(fā)布合約我們使用cleos set contract命令黎茎,其后跟合約賬號(hào)名和合約目錄,為了方便当悔,我建議你把合約的目錄名保持和合約文件名一致工三。

cleos set contract helloworld111 ./hello -p helloworld111

這里我們給出的代碼是將hello目錄下的hello合約發(fā)布到helloworld111迁酸。我這里的文件夾是hello,里面的abi和wasm也都是hello俭正,這樣你不用手動(dòng)指定合約文件了。

總結(jié)

至此焙畔,我想大家應(yīng)該對(duì)合約的編寫有了一個(gè)大致的了解了掸读,至少你可以參照著寫個(gè)簡(jiǎn)單的合約出來了,這其中還有很多技巧和高級(jí)用法宏多,我會(huì)在后續(xù)的文章中繼續(xù)和大家分享儿惫。

原文鏈接:https://www.fishopark.com/Article/Info?id=6771aecf-6fda-4246-8dd8-71be86fc709e

更多內(nèi)容請(qǐng)關(guān)注微信公眾號(hào)


公眾號(hào).jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市伸但,隨后出現(xiàn)的幾起案子肾请,更是在濱河造成了極大的恐慌,老刑警劉巖更胖,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铛铁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡却妨,警方通過查閱死者的電腦和手機(jī)饵逐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彪标,“玉大人倍权,你說我怎么就攤上這事±萄蹋” “怎么了薄声?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)题画。 經(jīng)常有香客問我默辨,道長(zhǎng),這世上最難降的妖魔是什么婴程? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任廓奕,我火速辦了婚禮,結(jié)果婚禮上档叔,老公的妹妹穿的比我還像新娘桌粉。我一直安慰自己,他們只是感情好衙四,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布铃肯。 她就那樣靜靜地躺著,像睡著了一般传蹈。 火紅的嫁衣襯著肌膚如雪押逼。 梳的紋絲不亂的頭發(fā)上步藕,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音挑格,去河邊找鬼咙冗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛漂彤,可吹牛的內(nèi)容都是我干的雾消。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼挫望,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼立润!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起媳板,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤桑腮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蛉幸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體破讨,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年巨缘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了添忘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡若锁,死狀恐怖搁骑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情又固,我是刑警寧澤仲器,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站仰冠,受9級(jí)特大地震影響乏冀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜洋只,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一辆沦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧识虚,春花似錦肢扯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肛循,卻和暖如春铭腕,著一層夾襖步出監(jiān)牢的瞬間银择,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工累舷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浩考,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓被盈,卻偏偏與公主長(zhǎng)得像怀挠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子害捕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353