使用Truffle開(kāi)發(fā)Dapp

本文內(nèi)容來(lái)自熊麗兵老師在登鏈學(xué)院上的《以太坊Dapp開(kāi)發(fā)實(shí)戰(zhàn)》課程

簡(jiǎn)介

Dapp VS App

去中心化的應(yīng)用的后端是一個(gè)分布式的網(wǎng)絡(luò)(中心化應(yīng)用后端是一個(gè)中心化的節(jié)點(diǎn))劲阎。前端連接到網(wǎng)絡(luò)中的任意一個(gè)節(jié)點(diǎn)都一樣匠题。

去中心化應(yīng)用給節(jié)點(diǎn)發(fā)送的請(qǐng)求叫交易颜懊,節(jié)點(diǎn)不單自己處理請(qǐng)求采够,還把請(qǐng)求轉(zhuǎn)發(fā)到網(wǎng)絡(luò)的其他節(jié)點(diǎn)黄虱,請(qǐng)求的最終執(zhí)行需要網(wǎng)絡(luò)的共識(shí)旷坦,所以應(yīng)用不能直接拿到結(jié)果袭艺,是異步的搀崭,前端想要知道狀態(tài)的改變需要監(jiān)聽(tīng)事件的方法。


Dapp開(kāi)發(fā)流程.png

第一個(gè)去中心化應(yīng)用

本應(yīng)用使用Truffle來(lái)開(kāi)發(fā)Dapp。功能與 第一個(gè)去中心化應(yīng)用 一樣瘤睹。

應(yīng)用效果圖如下


第一個(gè)去中心化應(yīng)用.png

應(yīng)用的功能很簡(jiǎn)單

淺藍(lán)色部分用來(lái)顯示區(qū)塊鏈上的姓名和年齡信息

淺綠色部分用來(lái)修改區(qū)塊鏈上的姓名和年齡信息

準(zhǔn)備

全局安裝truffle框架

sudo npm install -g truffle //安裝truffle升敲。-g代表全局

Truffle是DApps開(kāi)發(fā)用到的一個(gè)最流行的開(kāi)發(fā)框架。本身基于Javascript轰传。
Truffle也可以理解為是一個(gè)腳手架:集成了合約的編譯驴党、鏈接、測(cè)試获茬、部署

Truffle官網(wǎng):https://truffleframework.com/truffle
Truffle文檔:https://truffleframework.com/docs/truffle
Truffle命令:https://truffleframework.com/docs/truffle/reference/truffle-commands

Truffle里有個(gè)box的概念港庄,相當(dāng)于把一些代碼框架給打包好了,想用哪個(gè)box直接下載就可以使用恕曲,在此基礎(chǔ)上編寫自己的Dapp鹏氧。比如有一個(gè)react的box,整合了react的代碼佩谣,不用專門下載react了把还,非常方便。

Truffle box: https://github.com/truffle-box

本文為了梳理開(kāi)發(fā)流程茸俭,沒(méi)有使用box吊履。

全局安裝ganache節(jié)點(diǎn)

sudo npm install -g ganache-cli //安裝ganache-cli節(jié)點(diǎn)
ganache-cli     //啟動(dòng)ganache-cli節(jié)點(diǎn),后續(xù)會(huì)用到调鬓⊥а祝可單獨(dú)開(kāi)一個(gè)終端

以太坊節(jié)點(diǎn)也叫以太坊客戶端。智能合約必須部署到以太坊節(jié)點(diǎn)上來(lái)運(yùn)行袖迎。

以太坊節(jié)點(diǎn)有Ganache虛擬節(jié)點(diǎn)冕臭、Geth節(jié)點(diǎn)、以太坊測(cè)試節(jié)點(diǎn)(Ropsten燕锥、kovan辜贵、Rinkeby)、以太坊主網(wǎng)(Main Ethereum Network)

開(kāi)發(fā)過(guò)程中需要不斷的修改代碼和測(cè)試归形,需要得到及時(shí)的反饋托慨。Truffle官方推薦使用適合開(kāi)發(fā)的客戶端Ganache(前身為EtherumJS TestRPC)

Ganache是一個(gè)完整的在內(nèi)存中的區(qū)塊鏈(因?yàn)樵趦?nèi)存中暇榴,所以重啟后數(shù)據(jù)會(huì)丟失)厚棵,僅僅存在于你開(kāi)發(fā)的設(shè)備上。它在執(zhí)行交易時(shí)是實(shí)時(shí)返回蔼紧,而不等待默認(rèn)的出塊時(shí)間婆硬,可以快速驗(yàn)證新寫的代碼,當(dāng)出現(xiàn)錯(cuò)誤時(shí)能夠即時(shí)反饋奸例。它同時(shí)還是一個(gè)支持自動(dòng)化測(cè)試的功能強(qiáng)大的客戶端彬犯。Truffle充分利用它的特性向楼,能將測(cè)試運(yùn)行時(shí)間提速近90%。

ganache有命令行版和界面版兩個(gè)版本谐区,使用哪個(gè)都可以湖蜕。本文使用的是命令行版

ganache命令行版:https://github.com/trufflesuite/ganache-cli/tree/master
ganache界面版:https://github.com/trufflesuite/ganache/releases
安裝文檔:https://truffleframework.com/docs/ganache/quickstart

節(jié)點(diǎn)選擇

開(kāi)發(fā) 部署 正式部署前測(cè)試 正式部署
ganache Geth 以太坊測(cè)試節(jié)點(diǎn) 以太坊主網(wǎng)

MetaMask連接ganache節(jié)點(diǎn)

MetaMask是一款瀏覽器插件錢包,不需下載安裝客戶端宋列,只需添加至瀏覽器擴(kuò)展程序即可使用昭抒。它不僅是一個(gè)簡(jiǎn)單的錢包,還可以很方便的調(diào)試和測(cè)試以太坊的智能合約炼杖。

官網(wǎng):https://metamask.io/
Github地址:https://github.com/MetaMask/metamask-extension

文檔:https://metamask.github.io/metamask-docs/
使用教程參考:http://www.reibang.com/p/4a28566c425d

將MetaMask連接ganache節(jié)點(diǎn)并導(dǎo)入一個(gè)ganache自動(dòng)創(chuàng)建的賬戶灭返。用此賬戶部署合約以及發(fā)送交易。

連接節(jié)點(diǎn)

MetaMask的network選擇ganache-cli的節(jié)點(diǎn)url(localhost 8545)嘹叫,連接到ganache-cli節(jié)點(diǎn)

MetaMask導(dǎo)入賬戶

在MetaMask的import Account里導(dǎo)入Private Key婆殿。用此賬戶部署合約以及發(fā)送交易诈乒。

ganache-cli開(kāi)啟時(shí)會(huì)默認(rèn)創(chuàng)建十個(gè)賬戶罩扇,每個(gè)賬戶都有一個(gè)Private Key

創(chuàng)建項(xiàng)目

mkdir Dapp      //創(chuàng)建項(xiàng)目目錄

cd Dapp

truffle init    //truffle創(chuàng)建項(xiàng)目,得到項(xiàng)目框架(當(dāng)然也可以用truffle提供的box來(lái)創(chuàng)建項(xiàng)目怕磨,在box的基礎(chǔ)上修改)

npm init        //將項(xiàng)目轉(zhuǎn)換為npm項(xiàng)目(提示時(shí)都使用默認(rèn)值即可)

本文采用truffle init創(chuàng)建空項(xiàng)目喂饥,當(dāng)然還可以使用truffle的box創(chuàng)建帶有其他框架的項(xiàng)目。

npm init是將項(xiàng)目轉(zhuǎn)換為一個(gè)npm的項(xiàng)目肠鲫,方便管理员帮。

項(xiàng)目根目錄下安裝lite-server服務(wù)器

npm install lite-server //安裝項(xiàng)目服務(wù)器

應(yīng)用需要一個(gè)web服務(wù)器,而像nginx导饲、apache等web服務(wù)器需要單獨(dú)安裝,在項(xiàng)目外。而lite-server服務(wù)器本身就是項(xiàng)目的一部分荣挨,環(huán)境容易統(tǒng)一飞傀,方便開(kāi)發(fā)和維護(hù)。

項(xiàng)目根目錄下安裝truffle-contract

npm install truffle-contract    //安裝truffle框架提供的contract袋毙,對(duì)web3進(jìn)行了封裝型檀,方便與合約進(jìn)行交互

truffle-contract是web3的一個(gè)封裝,在應(yīng)用與智能合約交互時(shí)用听盖。

truffle-contract文檔:https://truffleframework.com/docs/truffle/getting-started/interacting-with-your-contracts
web3中文文檔:https://web3.learnblockchain.cn/

初始框架結(jié)構(gòu)

|-- Dapp
    |-- contracts                    //Solidity合約代碼
        -- Migrations.sol            //此文件為必須
    |-- migrations                    //合約部署腳本
        -- 1_initial_migration.js    //用來(lái)部署Migrations.sol的
    |-- test                        //測(cè)試代碼
    |-- node_modules                //npm模塊胀溺,lite-server和truffle-contract在里面
    -- truffle-config.js            //windows下的truffle配置文件
    -- truffle.js                    //linux、mac下的truffle配置文件
    -- package.json             //npm init后的配置文件

后端(智能合約)

開(kāi)發(fā)合約

Solidity文檔:https://solidity.readthedocs.io/en/v0.5.1/

直接將項(xiàng)目導(dǎo)入自己喜歡的編輯器皆看,在contracts目錄下新建 Simple.sol 文件仓坞,或者終端中使用命令來(lái)創(chuàng)建Simple.sol文件

truffle create contract Simple

結(jié)構(gòu)如下

|-- contracts
    -- Migrations.sol
    -- Simple.sol

內(nèi)容如下

pragma solidity ^0.4.24;

contract Simple{
    string name;
    uint age;

    //定義事件
    event Instructor(string name, uint age);

    function set(string _name, uint _age) public {
        name = _name;
        age = _age;
        //觸發(fā)事件
        emit Instructor(name, age);
    }

    function get() public view returns(string, uint) {
        return (name, age);
    }
}

編譯合約

truffle compile

編譯完成后,根目錄下會(huì)生成一個(gè)build目錄腰吟,其下的contracts目錄下是合約的json文件

|-- build
    |-- contracts
        -- Migrations.json
        -- Simple.json      //包含abi无埃、地址等信息

對(duì)合約部署的時(shí)候,truffle會(huì)更新此文件,寫入truffle.js里的網(wǎng)絡(luò)網(wǎng)絡(luò)信息

測(cè)試合約

測(cè)試合約有solidity和js兩種方式
solidity方式文檔:https://truffleframework.com/docs/truffle/testing/writing-tests-in-solidity
js方式文檔:https://truffleframework.com/docs/truffle/testing/writing-tests-in-javascript

test目錄下創(chuàng)建測(cè)試文件

|-- Dapp
    |-- test
        -- TestSimple.sol
        -- Simple.js

TestSimple.sol

pragma solidity ^0.4.24;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Simple.sol";

contract TestSimple {

    Simple info = Simple(DeployedAddresses.Simple());

    string name;
    uint age;

    function testInfo() public {

        info.set("中本聰",18);
        (name, age) = info.get();

        Assert.equal(name, "中本聰", "設(shè)置姓名出錯(cuò)");
        Assert.equal(age, 18, "設(shè)置年齡出錯(cuò)");
    }
}

Simple.js

var Simple = artifacts.require("Simple");

contract('Simple', function(accounts){
    it("set() should be equal set", function(){
        return Simple.deployed().then(function(instance){
            instance.set("中本聰", 18);

            return instance.get().then(function(data){
                assert.equal(data[0], "中本聰");
                assert.equal(data[1], 18);
            });
        });
    });
});

執(zhí)行測(cè)試命令

truffle test    //執(zhí)行所有測(cè)試文件
truffle test ./test/TestSimple.sol  //指定測(cè)試哪個(gè)文件

測(cè)試需要用到網(wǎng)絡(luò)录语,配置部分參考部署合約時(shí)的truffle.js的網(wǎng)絡(luò)配置

結(jié)果如下倍啥,說(shuō)明測(cè)試通過(guò)

 TestSimple
    ? testInfo (90ms)

  Contract: Simple
    ? set() should be equal set (46ms)

  2 passing (865ms)

部署合約

配置truffle.js文件

可參考truffle官網(wǎng)配置部分:http://truffleframework.com/docs/advanced/configuration

module.exports = {
    networks: {
        development: {
            host: "127.0.0.1",
            port: 8545,
            network_id: "*" //watch any network id
        }
    }
};
創(chuàng)建并配置部署的腳本

直接在contracts目錄下新建 Simple.js 文件,或者終端中使用命令來(lái)創(chuàng)建Simple.js文件

truffle create migration simple

在migrations目錄下就會(huì)創(chuàng)建一個(gè)部署腳本

|-- migrations
    -- 1_initial_migration.js
    -- 1544177454_simple.js //命令創(chuàng)建的澎埠。前面數(shù)字應(yīng)該是隨機(jī)的

配置腳本

參考:https://truffleframework.com/docs/truffle/getting-started/running-migrations

var Simple = artifacts.require("./Simple.sol");

module.exports = function(deployer) {
  // deployment steps
  deployer.deploy(Simple);
};

部署

truffle migrate

truffle migrate --reset //如果修改了合約重新部署虽缕,需要加reset參數(shù)

運(yùn)行上面指令發(fā)生的事情

運(yùn)行了1_initial_migration.js、2_deploy_contracts.js文件蒲稳,ganache客戶端中生成了transaction信息氮趋。

contracts目錄里的Migrations.json文件里增加了networks內(nèi)容,最主要的是一個(gè)address信息江耀。

前端

|-- Dapp
    |-- src             //項(xiàng)目根目錄下創(chuàng)建src目錄剩胁,存放前端文件
        -- index.html
        -- index.css
        |-- js
            -- index.js
            -- truffle-contract.min.js //從node_modules/truffle-contract/dist/truffle-contract.min.js復(fù)制過(guò)來(lái)的

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>第一個(gè)去中心化應(yīng)用</title>
    <link rel="stylesheet" href="./index.css">
</head>
<body>
    <div id="content">
        <h1 id="title">第一個(gè)去中心化應(yīng)用</h1>
        <h2 id="info"></h2>
        <div id="loader"></div>
        <div id="update">
            <div>
                <label for="name">姓名:</label>
                <input type="text" id="name">
            </div>
            <div>
                <label for="age">年齡:</label>
                <input type="text" id="age">
            </div>
            <button id="button">更新</button>
        </div>
    </div>
</body>
<!-- 引入jquery、web3.js祥国、truffle-contract.min.js昵观、index.js -->
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<!-- 這里的web3.js為0.2版本。1.0版本目前還是beta階段舌稀,等穩(wěn)定了可更換為1.0版本 -->
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js"></script>
<script src="./js/truffle-contract.min.js"></script>
<script src="./js/index.js"></script>
</html>

index.css

body{background:#f0f0f0;}
#content{width:350px;margin:0 auto;}
#title{text-align: center;}
#info{padding:0.5em;background: lightblue;margin:1em 0;}
#update{padding:1em;background: lightgreen}
#name{border:none;}#age{border:none;}
button{margin:1em 0;}

index.js

這部分是應(yīng)用與智能合約交互的部分啊犬,跟智能合約一樣,是Dapp的關(guān)鍵部分

第一個(gè)去中心化應(yīng)用中壁查,獲取智能合約對(duì)象是通過(guò)在js代碼里寫入合約abi和合約地址的硬編碼方式觉至,每次變更合約都要重新修改為新的合約abi和合約地址,非常繁瑣睡腿,不容易維護(hù)语御。

truffle提供了一個(gè)truffle-contract的抽象,對(duì)web3進(jìn)行了封裝席怪,并且引入了promise的用法应闯,方便與合約進(jìn)行交互。合約編譯的時(shí)候會(huì)在build/contracts目錄下生成合約對(duì)應(yīng)的json文件(重新編譯部署會(huì)自動(dòng)更新json文件)何恶,truffle-contract會(huì)利用這些信息生成合約對(duì)象孽锥,避免了硬編碼方式。

兩者對(duì)比

web3硬編碼方式

var infoContract = web3.eth.contract(合約ABI);
var  Simple = infoContract.at('合約地址');

truffle-contract方式

TruffleContract('合約的json文件').deployed()
.then(function(instance) {

});

完整的js文件

App = {

    web3Provider: null,
    contracts: {},

    //初始化
    init: function(){
        return App.initweb3();
    },

    //初始化web3
    initweb3: function(){
        //獲取web3對(duì)象
        if (typeof web3 !== 'undefined') {
            App.web3Provider = web3.currentProvider;
            web3 = new Web3(App.web3Provider);
        } else {
            // Set the provider you want from Web3.providers
            App.web3Provider = new Web3.providers.HttpProvider("http://localhost:8545");
            web3 = new Web3(App.web3Provider);
        }
        return App.initContract();
    },

    //初始化合約
    initContract: function(){
        //拿到Simple.json的內(nèi)容细层,回調(diào)函數(shù)的參數(shù)data即為拿到的內(nèi)容
        $.getJSON("Simple.json",function(data){
            //得到TruffleContract對(duì)象惜辑,并賦值給App.contracts.Simple
           App.contracts.Simple = TruffleContract(data);
           //設(shè)置Provider
           App.contracts.Simple.setProvider(App.web3Provider);
           //調(diào)用合約的get方法
           App.get();
           //事件監(jiān)聽(tīng),更新顯示的內(nèi)容
           App.watchChanged();
        });

        //調(diào)用事件
        App.bindEvents();
    },

    //合約的get方法
    get: function(){
        //deployed得到合約的實(shí)例疫赎,通過(guò)then的方式回調(diào)拿到實(shí)例
        App.contracts.Simple.deployed().then(function(instance){
            return instance.get.call();
        }).then(function(result){ //異步執(zhí)行盛撑,get方法執(zhí)行完后回調(diào)執(zhí)行then方法,result為get方法的返回值
            $("#loader").hide();
            $("#info").html(`我叫` + result[0] + `捧搞, 我今年` + result[1] + `歲了抵卫。`);
        }).catch(function(err){ //get方法執(zhí)行失敗打印錯(cuò)誤
            console.log(err);
        })
    },

    //事件狮荔,更新操作
    bindEvents: function(){
        $("#button").click(function(){
            $("#loader").show();
            App.contracts.Simple.deployed().then(function(instance){
                return instance.set.sendTransaction($("#name").val(),$("#age").val());
            }).then(function(result){
                    App.get(); //set方法執(zhí)行完后調(diào)用get方法,獲取最新值(可沒(méi)有介粘,通常使用事件監(jiān)聽(tīng)的方式)
                }).catch(function(err){
                    console.log(err);
                });
            });
    },

    //事件監(jiān)聽(tīng)
    watchChanged: function(){
        App.contracts.Simple.deployed().then(function(instance){
            var infoEvent = instance.Instructor();
            infoEvent.watch(function(err, result){
                $("#loader").hide();
                $("#info").html(`我叫` + result.args.name + `殖氏, 我今年` + result.args.age + `歲了。`);
            })
        });
    }
}

//加載應(yīng)用
$(function(){
    $(window).load(function(){
        App.init();
    });
});

開(kāi)啟服務(wù)

原始方式如下

根目錄下輸入

node_modules/lite-server/bin/lite-server

彈出來(lái)的頁(yè)面中定位到index.html文件姻采,就可以看到應(yīng)用了

localhost:3000/src/index.html

這種方式 index.js里$.getJSON第一個(gè)參數(shù)路徑為../build/contracts/Simple.json雅采。上面index.js文件是配置bs-config.json后的寫法,如果使用此方式需要修改路徑慨亲。

但這種方式顯然是太麻煩了婚瓜。可以通過(guò)配置lite-server的baseDir來(lái)自動(dòng)定位刑棵,通過(guò)配置服務(wù)腳本的方式來(lái)簡(jiǎn)化輸入命令

配置bs-config.json

bs-config.jsonbs-config.js為lite-server的配置文件

在項(xiàng)目根目錄下創(chuàng)建bs-config.json巴刻,然后寫入配置

{
    "server":
    {"baseDir": ["./src", "./build/contracts"] }
}

配置./src,直接localhost:3000 即可打開(kāi)index.html
配置./build/contracts蛉签,index.js里$.getJSON第一個(gè)參數(shù)路徑就可以省略胡陪,直接寫Simple.json即可

修改package.json,加入腳本

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "lite-server"    //新加入的腳本
  },

在項(xiàng)目根目錄下直接輸入

npm run dev

即可打開(kāi)項(xiàng)目(自動(dòng)定位到src下)

交互

與應(yīng)用交互

應(yīng)用中在淡綠色區(qū)域?yàn)榻换^(qū)域正蛙,修改姓名督弓、年齡营曼,點(diǎn)擊更新按鈕乒验,會(huì)彈出MetaMask的對(duì)話框,顯示的是交易的信息蒂阱,點(diǎn)擊確定锻全,應(yīng)用頁(yè)面淡藍(lán)色區(qū)域的姓名、年齡就會(huì)更新了录煤。

與ganache節(jié)點(diǎn)交互

truffle console

通過(guò)上面指令進(jìn)入控制臺(tái)鳄厌,可以運(yùn)行web3.js的指令。web3.js指令具體查看:以太坊 web3.js 中文版文檔

比如

truffle(development)> web3.eth.accounts
truffle(development)> web3.currentProvider //web3所鏈接的ethereum網(wǎng)絡(luò)的相關(guān)信息

附最終的文件目錄結(jié)構(gòu)

|-- Dapp
    |-- build                       //編譯合約后生成的json文件目錄
        |-- contracts
            -- Migrations.json
            -- Simple.json
    |-- contracts                    //Solidity合約代碼
        -- Migrations.sol            //此文件為必須
        -- Simple.sol               //合約文件
    |-- migrations                    //合約部署腳本
        -- 1_initial_migration.js    //用來(lái)部署Migrations.sol的
        -- 1544177454_simple.js     //合約遷移文件
    |-- test                        //測(cè)試文件目錄
        -- TestSimple.sol
        -- Simple.js
    |-- node_modules                //npm模塊妈踊,lite-server和truffle-contract在里面
    |-- src                         //資源文件目錄
        -- index.html
        -- index.css
            |-- js
                -- index.js
                -- truffle-contract.min.js
    -- truffle-config.js            //windows下的truffle配置文件
    -- truffle.js                    //linux了嚎、mac下的truffle配置文件
    -- package.json             //npm init后的配置文件
    -- bs-config.json           //lite-server的配置文件
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市廊营,隨后出現(xiàn)的幾起案子歪泳,更是在濱河造成了極大的恐慌,老刑警劉巖露筒,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呐伞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡慎式,警方通過(guò)查閱死者的電腦和手機(jī)伶氢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門趟径,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人癣防,你說(shuō)我怎么就攤上這事蜗巧。” “怎么了蕾盯?”我有些...
    開(kāi)封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵惧蛹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我刑枝,道長(zhǎng)香嗓,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任装畅,我火速辦了婚禮靠娱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掠兄。我一直安慰自己像云,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蚂夕。 她就那樣靜靜地躺著迅诬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪婿牍。 梳的紋絲不亂的頭發(fā)上侈贷,一...
    開(kāi)封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音等脂,去河邊找鬼俏蛮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛上遥,可吹牛的內(nèi)容都是我干的搏屑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼粉楚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼辣恋!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起模软,我...
    開(kāi)封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤伟骨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后撵摆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體底靠,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年特铝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了暑中。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壹瘟。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鳄逾,靈堂內(nèi)的尸體忽然破棺而出稻轨,到底是詐尸還是另有隱情,我是刑警寧澤雕凹,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布殴俱,位于F島的核電站,受9級(jí)特大地震影響枚抵,放射性物質(zhì)發(fā)生泄漏线欲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一汽摹、第九天 我趴在偏房一處隱蔽的房頂上張望李丰。 院中可真熱鬧,春花似錦逼泣、人聲如沸趴泌。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嗜憔。三九已至,卻和暖如春氏仗,著一層夾襖步出監(jiān)牢的瞬間吉捶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工廓鞠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帚稠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓床佳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親榄审。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砌们,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

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