Fabric 插拔式共識框架(協(xié)議規(guī)范)

共識框架定義了每個(gè)共識插件都需要實(shí)現(xiàn)的接口:

  • consensus.Consenter: 允許共識插件從網(wǎng)絡(luò)上接收消息的接口
  • consensus.Stack: 允許共識插件用來與棧交互的凡傅,這個(gè)接口可以分為兩部分:
    • consensus.Communicator: 用來發(fā)送(廣播或單播)消息到其他的驗(yàn)證 peer
    • consensus.LedgerStack: 這個(gè)接口使得執(zhí)行框架像總賬一樣方便

就像下面描述的細(xì)節(jié)一樣氛谜,consensus.LedgerStack封裝了其他接口托慨,consensus.Executor接口是共識框架的核心部分。換句話說望薄,consensus.Executor接口允許一個(gè)(批量)交易啟動(dòng),執(zhí)行喜每,根據(jù)需要回滾间护,預(yù)覽和提交。每一個(gè)共識插件都需要滿足以所有驗(yàn)證 peer 上全序的方式把批量(塊)交易(通過consensus.Executor.CommitTxBatch)被提交到總賬中(參看下面的consensus.Executor接口獲得詳細(xì)細(xì)節(jié))白筹。

當(dāng)前智末,共識框架由consensus, controllerhelper這三個(gè)包組成。使用controllerhelper包的主要原因是防止Go語言的“循環(huán)引入”和當(dāng)插件更新時(shí)的最小化代碼變化徒河。

  • controller 包規(guī)范了驗(yàn)證 peer 所使用的共識插件
  • helper 是圍繞公式插件的墊片系馆,它是用來與剩下的棧交互的,如為其他 peer 維護(hù)消息顽照。

這里有2個(gè)共識插件提供:pbftnoops

  • obcpbft包包含實(shí)現(xiàn) PBFT [1] 和 Sieve 共識協(xié)議的共識插件由蘑。參看上一篇文章介紹。
  • noops 是一個(gè)為開發(fā)和測試提供的''假的''共識插件. 它處理所有共識消息但不提供共識功能代兵,它也是一個(gè)好的學(xué)習(xí)如何開發(fā)一個(gè)共識插件的簡單例子尼酿。

3.4.1 Consenter 接口

定義:

type Consenter interface {
    RecvMsg(msg *pb.Message) error
    ExecutionConsumer
}

Consenter接口是插件對(外部的)客戶端請求的入口,當(dāng)處理共識時(shí)植影,共識消息在內(nèi)部(如從共識模塊)產(chǎn)生裳擎。NewConsenter創(chuàng)建Consenter插件。RecvMsg`以到達(dá)共識的順序來處理進(jìn)來的交易思币。

閱讀下面的helper.HandleMessage來理解 peer 是如何和這個(gè)接口來交互的鹿响。

3.4.2 CPI接口

定義:

type Stack interface {
    NetworkStack
    SecurityUtils
    Executor
    LegacyExecutor
    LedgerManager
    ReadOnlyLedger
    StatePersistor
}
type CPI interface {
    Inquirer
    Communicator
    SecurityUtils
    Executor
    Ledger
    RemoteLedgers
}

CPI 允許插件和棧交互。它是由helper.Helper對象實(shí)現(xiàn)的谷饿』涛遥回想一下這個(gè)對象是:

  1. helper.NewConsensusHandler被調(diào)用時(shí)初始化的
  2. 當(dāng)它們的插件構(gòu)造了consensus.Consenter對象,那么它對插件的作者是可訪問的

3.4.3 Inquirer接口

定義:

type Inquirer interface {
        GetNetworkInfo() (self *pb.PeerEndpoint, network []*pb.PeerEndpoint, err error)
        GetNetworkHandles() (self *pb.PeerID, network []*pb.PeerID, err error)
}

這個(gè)接口是consensus.CPI接口的一部分博投。它是用來獲取網(wǎng)絡(luò)中驗(yàn)證 peer 的(GetNetworkHandles)句柄绸贡,以及那些驗(yàn)證 peer 的明細(xì)(GetNetworkInfo):

注意peers由pb.PeerID對象確定。這是一個(gè)protobuf消息,當(dāng)前定義為(注意這個(gè)定義很可能會被修改):

message PeerID {
    string name = 1;
}

3.4.4 Communicator接口

定義:

type Communicator interface {
    Broadcast(msg *pb.Message) error
    Unicast(msg *pb.Message, receiverHandle *pb.PeerID) error
}

這個(gè)接口是consensus.CPI接口的一部分恃轩。它是用來與網(wǎng)絡(luò)上其它 peer 通信的(helper.Broadcast, helper.Unicast):

3.4.5 SecurityUtils接口

定義:

type SecurityUtils interface {
        Sign(msg []byte) ([]byte, error)
        Verify(peerID *pb.PeerID, signature []byte, message []byte) error
}

這個(gè)接口是consensus.CPI接口的一部分结洼。它用來處理消息簽名(Sign)的加密操作和驗(yàn)證簽名(Verify)

3.4.6 LedgerStack 接口

定義:

type LedgerStack interface {
    Executor
    Ledger
    RemoteLedgers
}

CPI接口的主要成員,LedgerStack 組與fabric的其它部分與共識相互作用叉跛,如執(zhí)行交易松忍,查詢和更新總賬。這個(gè)接口支持對本地區(qū)塊鏈和狀體的查詢筷厘,更新本地區(qū)塊鏈和狀態(tài)鸣峭,查詢共識網(wǎng)絡(luò)上其它節(jié)點(diǎn)的區(qū)塊鏈和狀態(tài)。它是由Executor, LedgerRemoteLedgers這三個(gè)接口組成的酥艳。下面會描述它們摊溶。

3.4.7 Executor 接口

定義:

type Executor interface {
    BeginTxBatch(id interface{}) error
    ExecTXs(id interface{}, txs []*pb.Transaction) ([]byte, []error)
    CommitTxBatch(id interface{}, transactions []*pb.Transaction, transactionsResults []*pb.TransactionResult, metadata []byte) error
    RollbackTxBatch(id interface{}) error
    PreviewCommitTxBatchBlock(id interface{}, transactions []*pb.Transaction, metadata []byte) (*pb.Block, error)
}

executor接口是LedgerStack接口最常使用的部分,且是共識網(wǎng)絡(luò)工作的必要部分充石。接口允許交易啟動(dòng)莫换,執(zhí)行,根據(jù)需要回滾骤铃,預(yù)覽和提交拉岁。這個(gè)接口由下面這些方法組成。

3.4.7.1 開始批量交易

BeginTxBatch(id interface{}) error

這個(gè)調(diào)用接受任意的惰爬,故意含糊的id喊暖,來使得共識插件可以保證與這個(gè)具體的批量相關(guān)的交易才會被執(zhí)行。例如:在pbft實(shí)現(xiàn)中撕瞧,這個(gè)id是被執(zhí)行交易的編碼過的哈希陵叽。

3.4.7.2 執(zhí)行交易

ExecTXs(id interface{}, txs []*pb.Transaction) ([]byte, []error)

這個(gè)調(diào)用根據(jù)總賬當(dāng)前的狀態(tài)接受一組交易,并返回帶有對應(yīng)著交易組的錯(cuò)誤信息組的當(dāng)前狀態(tài)的哈希丛版。注意一個(gè)交易所產(chǎn)生的錯(cuò)誤不影響批量交易的安全提交巩掺。當(dāng)遇到失敗所采用的策略取決與共識插件的實(shí)現(xiàn)。這個(gè)接口調(diào)用多次是安全的页畦。

3.4.7.3 提交與回滾交易

RollbackTxBatch(id interface{}) error

這個(gè)調(diào)用中止了批量執(zhí)行锌半。這會廢棄掉對當(dāng)前狀態(tài)的操作,并把總賬狀態(tài)回歸到之前的狀態(tài)寇漫。批量是從BeginBatchTx開始的,如果需要開始一個(gè)新的就需要在執(zhí)行任意交易之前重新創(chuàng)建一個(gè)殉摔。

PreviewCommitTxBatchBlock(id interface{}, transactions []*pb.Transaction, metadata []byte) (*pb.Block, error)

這個(gè)調(diào)用是共識插件對非確定性交易執(zhí)行的測試時(shí)最有用的方法州胳。區(qū)塊返回的哈希表部分會保證,當(dāng)CommitTxBatch被立即調(diào)用時(shí)的區(qū)塊是同一個(gè)逸月。這個(gè)保證會被任意新的交易的執(zhí)行所打破栓撞。

CommitTxBatch(id interface{}, transactions []*pb.Transaction, transactionsResults []*pb.TransactionResult, metadata []byte) error

這個(gè)調(diào)用提交區(qū)塊到區(qū)塊鏈中。區(qū)塊必須以全序提交到區(qū)塊鏈中,CommitTxBatch結(jié)束批量交易瓤湘,在執(zhí)行或提交任意的交易之前必須先調(diào)用BeginTxBatch瓢颅。

3.4.8 Ledger 接口

定義:

type Ledger interface {
    ReadOnlyLedger
    UtilLedger
    WritableLedger
}

Ledger 接口是為了允許共識插件詢問或可能改變區(qū)塊鏈當(dāng)前狀態(tài)。它是由下面描述的三個(gè)接口組成的

3.4.8.1 ReadOnlyLedger 接口

定義:

type ReadOnlyLedger interface {
    GetBlock(id uint64) (block *pb.Block, err error)
    GetCurrentStateHash() (stateHash []byte, err error)
    GetBlockchainSize() (uint64, error)
}

ReadOnlyLedger 接口是為了查詢總賬的本地備份弛说,而不會修改它挽懦。它是由下面這些函數(shù)組成的。

GetBlockchainSize() (uint64, error)

這個(gè)函數(shù)返回區(qū)塊鏈總賬的長度木人。一般來說信柿,這個(gè)函數(shù)永遠(yuǎn)不會失敗,在這種不太可能發(fā)生情況下醒第,錯(cuò)誤被傳遞給調(diào)用者渔嚷,由它確定是否需要恢復(fù)。具有最大區(qū)塊值的區(qū)塊的值為GetBlockchainSize()-1

注意在區(qū)塊鏈總賬的本地副本是腐壞或不完整的情況下稠曼,這個(gè)調(diào)用會返回鏈中最大的區(qū)塊值+1形病。這允許節(jié)點(diǎn)在舊的塊是腐壞或丟失的情況下能繼續(xù)操作當(dāng)前狀態(tài)/塊。

GetBlock(id uint64) (block *pb.Block, err error)

這個(gè)調(diào)用返回區(qū)塊鏈中塊的數(shù)值id霞幅。一般來說這個(gè)調(diào)用是不會失敗的漠吻,除非請求的區(qū)塊超出當(dāng)前區(qū)塊鏈的長度,或者底層的區(qū)塊鏈被腐壞了蝗岖。GetBlock的失敗可能可以通過狀態(tài)轉(zhuǎn)換機(jī)制來取回它侥猩。

GetCurrentStateHash() (stateHash []byte, err error)

這個(gè)調(diào)用返回總賬的當(dāng)前狀態(tài)的哈希。一般來說抵赢,這個(gè)函數(shù)永遠(yuǎn)不會失敗欺劳,在這種不太可能發(fā)生情況下,錯(cuò)誤被傳遞給調(diào)用者铅鲤,由它確定是否需要恢復(fù)划提。

3.4.8.2 UtilLedger 接口

定義:

type UtilLedger interface {
    HashBlock(block *pb.Block) ([]byte, error)
    VerifyBlockchain(start, finish uint64) (uint64, error)
}

UtilLedger 接口定義了一些由本地總賬提供的有用的功能。使用mock接口來重載這些功能在測試時(shí)非常有用邢享。這個(gè)接口由兩個(gè)函數(shù)構(gòu)成鹏往。
會會

HashBlock(block *pb.Block) ([]byte, error)

盡管*pb.Block定義了GetHash方法,為了mock測試骇塘,重載這個(gè)方法會非常有用伊履。因此,建議GetHash方法不直接調(diào)用款违,而是通過UtilLedger.HashBlock接口來調(diào)用這個(gè)方法唐瀑。一般來說,這個(gè)函數(shù)永遠(yuǎn)不會失敗插爹,但是錯(cuò)誤還是會傳遞給調(diào)用者哄辣,讓它決定是否使用適當(dāng)?shù)幕謴?fù)请梢。

VerifyBlockchain(start, finish uint64) (uint64, error)

這個(gè)方法是用來校驗(yàn)區(qū)塊鏈中的大的區(qū)域。它會從高的塊start到低的塊finish力穗,返回第一個(gè)塊的PreviousBlockHash與塊的前一個(gè)塊的哈希不相符的塊編號以及錯(cuò)誤信息毅弧。注意,它一般會標(biāo)識最后一個(gè)好的塊的編號当窗,而不是第一個(gè)壞的塊的編號够坐。

3.4.8.3 WritableLedger 接口

定義:

type WritableLedger interface {
    PutBlock(blockNumber uint64, block *pb.Block) error
    ApplyStateDelta(id interface{}, delta *statemgmt.StateDelta) error
    CommitStateDelta(id interface{}) error
    RollbackStateDelta(id interface{}) error
    EmptyState() error
}

WritableLedger 接口允許調(diào)用者更新區(qū)塊鏈。注意這NOT 不是共識插件的通常用法超全。當(dāng)前的狀態(tài)需要通過Executor接口執(zhí)行交易來修改咆霜,新的區(qū)塊在交易提交時(shí)生成。相反的嘶朱,這個(gè)接口主要是用來狀態(tài)改變和腐化恢復(fù)蛾坯。特別的,這個(gè)接口下的函數(shù)永遠(yuǎn)不能直接暴露給共識消息疏遏,這樣會導(dǎo)致打破區(qū)塊鏈所承諾的不可修改這一概念脉课。這個(gè)結(jié)構(gòu)包含下面這些函數(shù)。

?   PutBlock(blockNumber uint64, block *pb.Block) error

? 這個(gè)函數(shù)根據(jù)給定的區(qū)塊編號把底層區(qū)塊插入到區(qū)塊鏈中财异。注意這是一個(gè)不安全的接口倘零,所以它不會有錯(cuò)誤返回或返回。插入一個(gè)比當(dāng)前區(qū)塊高度更高的區(qū)塊是被允許的戳寸,同樣呈驶,重寫一個(gè)已經(jīng)提交的區(qū)塊也是被允許的。記住疫鹊,由于哈希技術(shù)使得創(chuàng)建一個(gè)鏈上的更早的塊是不可行的袖瞻,所以這并不影響鏈的可審計(jì)性和不可變性。任何嘗試重寫區(qū)塊鏈的歷史的操作都能很容易的被偵測到拆吆。這個(gè)函數(shù)一般只用于狀態(tài)轉(zhuǎn)移API聋迎。

?   ApplyStateDelta(id interface{}, delta *statemgmt.StateDelta) error

這個(gè)函數(shù)接收狀態(tài)變化,并把它應(yīng)用到當(dāng)前的狀態(tài)枣耀。變化量的應(yīng)用會使得狀態(tài)向前或向后轉(zhuǎn)變霉晕,這取決于狀態(tài)變化量的構(gòu)造,與Executor方法一樣捞奕,ApplyStateDelta接受一個(gè)同樣會被傳遞給CommitStateDelta or RollbackStateDelta不透明的接口id

?   CommitStateDelta(id interface{}) error

這個(gè)方法提交在ApplyStateDelta中應(yīng)用的狀態(tài)變化牺堰。這通常是在調(diào)用者調(diào)用ApplyStateDelta后通過校驗(yàn)由GetCurrentStateHash()獲得的狀態(tài)哈希之后調(diào)用的。這個(gè)函數(shù)接受與傳遞給ApplyStateDelta一樣的id颅围。

?   RollbackStateDelta(id interface{}) error

這個(gè)函數(shù)撤銷在ApplyStateDelta中應(yīng)用的狀態(tài)變化量萌焰。這通常是在調(diào)用者調(diào)用ApplyStateDelta后與由GetCurrentStateHash()獲得的狀態(tài)哈希校驗(yàn)失敗后調(diào)用的。這個(gè)函數(shù)接受與傳遞給ApplyStateDelta一樣的id谷浅。

    EmptyState() error

這個(gè)函數(shù)將會刪除整個(gè)當(dāng)前狀態(tài),得到原始的空狀態(tài)。這通常是通過變化量加載整個(gè)新的狀態(tài)時(shí)調(diào)用的一疯。這一般只對狀態(tài)轉(zhuǎn)移API有用撼玄。

3.4.9 RemoteLedgers 接口

定義:

type RemoteLedgers interface {
    GetRemoteBlocks(peerID uint64, start, finish uint64) (<-chan *pb.SyncBlocks, error)
    GetRemoteStateSnapshot(peerID uint64) (<-chan *pb.SyncStateSnapshot, error)
    GetRemoteStateDeltas(peerID uint64, start, finish uint64) (<-chan *pb.SyncStateDeltas, error)
}

RemoteLedgers 接口的存在主要是為了啟用狀態(tài)轉(zhuǎn)移,和向其它副本詢問區(qū)塊鏈的狀態(tài)墩邀。和WritableLedger接口一樣掌猛,這不是給正常的操作使用,而是為追趕眉睹,錯(cuò)誤恢復(fù)等操作而設(shè)計(jì)的荔茬。這個(gè)接口中的所有函數(shù)調(diào)用這都有責(zé)任來處理超時(shí)。這個(gè)接口包含下面這些函數(shù):

    GetRemoteBlocks(peerID uint64, start, finish uint64) (<-chan *pb.SyncBlocks, error)

這個(gè)函數(shù)嘗試從由peerID指定的 peer 中取出由startfinish標(biāo)識的范圍中的*pb.SyncBlocks流竹海。一般情況下慕蔚,由于區(qū)塊鏈必須是從結(jié)束到開始這樣的順序來驗(yàn)證的,所以start是比finish更高的塊編號斋配。由于慢速的結(jié)構(gòu)孔飒,其它請求的返回可能出現(xiàn)在這個(gè)通道中,所以調(diào)用者必須驗(yàn)證返回的是期望的塊艰争。第二次以同樣的peerID來調(diào)用這個(gè)方法會導(dǎo)致第一次的通道關(guān)閉坏瞄。

    GetRemoteStateSnapshot(peerID uint64) (<-chan *pb.SyncStateSnapshot, error)

這個(gè)函數(shù)嘗試從由peerID指定的 peer 中取出*pb.SyncStateSnapshot流。為了應(yīng)用結(jié)果甩卓,首先需要通過WritableLedgerEmptyState調(diào)用來清空存在在狀態(tài)鸠匀,然后順序應(yīng)用包含在流中的變化量。

    GetRemoteStateDeltas(peerID uint64, start, finish uint64) (<-chan *pb.SyncStateDeltas, error)

這個(gè)函數(shù)嘗試從由peerID指定的 peer 中取出由startfinish標(biāo)識的范圍中的*pb.SyncStateDeltas流逾柿。由于慢速的結(jié)構(gòu)缀棍,其它請求的返回可能出現(xiàn)在這個(gè)通道中,所以調(diào)用者必須驗(yàn)證返回的是期望的塊變化量鹿寻。第二次以同樣的peerID來調(diào)用這個(gè)方法會導(dǎo)致第一次的通道關(guān)閉睦柴。

3.4.10 controller

3.4.10.1 controller.NewConsenter

簽名:

func NewConsenter(cpi consensus.CPI) (consenter consensus.Consenter)

這個(gè)函數(shù)讀取為peer過程指定的core.yaml配置文件中的peer.validator.consensus的值。鍵peer.validator.consensus的有效值指定運(yùn)行noops還是pbft共識插件毡熏。(注意坦敌,它最終被改變?yōu)?code>noops或custom。在custom情況下痢法,驗(yàn)證 peer 將會運(yùn)行由consensus/config.yaml中定義的共識插件)

插件的作者需要編輯函數(shù)體狱窘,來保證路由到它們包中正確的構(gòu)造函數(shù)。例如财搁,對于pbft 我們指向pbft.GetPlugin構(gòu)造器蘸炸。

這個(gè)函數(shù)是當(dāng)設(shè)置返回信息處理器的consenter域時(shí),被helper.NewConsensusHandler調(diào)用的尖奔。輸入?yún)?shù)cpi是由helper.NewHelper構(gòu)造器輸出的搭儒,并實(shí)現(xiàn)了consensus.CPI接口

3.4.11 helper

3.4.11.1 高層次概述

驗(yàn)證 peer 通過helper.NewConsesusHandler函數(shù)(一個(gè)處理器工廠)穷当,為每個(gè)連接的 peer 建立消息處理器(helper.ConsensusHandler)。每個(gè)進(jìn)來的消息都會檢查它的類型(helper.HandleMessage)淹禾;如果這是為了共識必須到達(dá)的消息馁菜,它會傳遞到 peer 的共識對象(consensus.Consenter)。其它的信息會傳遞到棧中的下一個(gè)信息處理器铃岔。

3.4.11.2 helper.ConsensusHandler

定義:

type ConsensusHandler struct {
    chatStream  peer.ChatStream
    consenter   consensus.Consenter
    coordinator peer.MessageHandlerCoordinator
    done        chan struct{}
    peerHandler peer.MessageHandler
}

共識中的上下文汪疮,我們只關(guān)注域coordinatorconsentercoordinator就像名字隱含的那樣毁习,它被用來在 peer 的信息處理器之間做協(xié)調(diào)智嚷。例如,當(dāng) peer 希望Broadcast時(shí)纺且,對象被訪問盏道。共識需要到達(dá)的共識者會接收到消息并處理它們。

注意隆檀,fabric/peer/peer.go定義了peer.MessageHandler (接口)摇天,和peer.MessageHandlerCoordinator(接口)類型。

3.4.11.3 helper.NewConsensusHandler

簽名:

func NewConsensusHandler(coord peer.MessageHandlerCoordinator, stream peer.ChatStream, initiatedStream bool, next peer.MessageHandler) (peer.MessageHandler, error)

創(chuàng)建一個(gè)helper.ConsensusHandler對象恐仑。為每個(gè)coordinator設(shè)置同樣的消息處理器泉坐。同時(shí)把consenter設(shè)置為controller.NewConsenter(NewHelper(coord))

3.4.11.4 helper.Helper

定義:

type Helper struct {
    coordinator peer.MessageHandlerCoordinator
}

包含驗(yàn)證peer的coordinator的引用。對象是否為peer實(shí)現(xiàn)了consensus.CPI接口裳仆。

3.4.11.5 helper.NewHelper

簽名:

func NewHelper(mhc peer.MessageHandlerCoordinator) consensus.CPI

返回coordinator被設(shè)置為輸入?yún)?shù)mhchelper.ConsensusHandler消息處理器的coordinator域)的helper.Helper對象腕让。這個(gè)對象實(shí)現(xiàn)了consensus.CPI接口,從而允許插件與棧進(jìn)行交互歧斟。

3.4.11.6 helper.HandleMessage

回憶一下纯丸,helper.NewConsensusHandler返回的helper.ConsesusHandler對象實(shí)現(xiàn)了 peer.MessageHandler 接口:

type MessageHandler interface {
    RemoteLedger
    HandleMessage(msg *pb.Message) error
    SendMessage(msg *pb.Message) error
    To() (pb.PeerEndpoint, error)
    Stop() error
}

在共識的上下文中,我們只關(guān)心HandleMessage方法静袖。簽名:

func (handler *ConsensusHandler) HandleMessage(msg *pb.Message) error

這個(gè)函數(shù)檢查進(jìn)來的MessageType觉鼻。有四種情況:

  1. 等于pb.Message_CONSENSUS:傳遞給處理器的consenter.RecvMsg函數(shù)。
  2. 等于pb.Message_CHAIN_TRANSACTION (如:一個(gè)外部部署的請求): 一個(gè)響應(yīng)請求首先被發(fā)送給用戶队橙,然后把消息傳遞給consenter.RecvMsg函數(shù)
  3. 等于pb.Message_CHAIN_QUERY (如:查詢): 傳遞給helper.doChainQuery方法來在本地執(zhí)行
  4. 其它: 傳遞給棧中下一個(gè)處理器的HandleMessage方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坠陈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子捐康,更是在濱河造成了極大的恐慌仇矾,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件解总,死亡現(xiàn)場離奇詭異贮匕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)花枫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門刻盐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掏膏,“玉大人,你說我怎么就攤上這事隙疚∪雷罚” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵供屉,是天一觀的道長。 經(jīng)常有香客問我溺蕉,道長伶丐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任疯特,我火速辦了婚禮哗魂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘漓雅。我一直安慰自己录别,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布邻吞。 她就那樣靜靜地躺著组题,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抱冷。 梳的紋絲不亂的頭發(fā)上崔列,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機(jī)與錄音旺遮,去河邊找鬼赵讯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛耿眉,可吹牛的內(nèi)容都是我干的边翼。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼鸣剪,長吁一口氣:“原來是場噩夢啊……” “哼组底!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起西傀,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤斤寇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后拥褂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娘锁,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年饺鹃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莫秆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片间雀。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖镊屎,靈堂內(nèi)的尸體忽然破棺而出惹挟,到底是詐尸還是另有隱情,我是刑警寧澤缝驳,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布连锯,位于F島的核電站,受9級特大地震影響用狱,放射性物質(zhì)發(fā)生泄漏运怖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一夏伊、第九天 我趴在偏房一處隱蔽的房頂上張望摇展。 院中可真熱鬧,春花似錦溺忧、人聲如沸咏连。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祟滴。三九已至,卻和暖如春刀森,著一層夾襖步出監(jiān)牢的瞬間踱启,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工研底, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留埠偿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓榜晦,卻偏偏與公主長得像冠蒋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子乾胶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

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