比特幣探究之隔離見證

隔離見證(segregated witness脆诉,簡稱segwit)宠纯,是比特幣歷史上一次很重要的升級,涉及到共識規(guī)則和網絡協(xié)議逝慧。它正式激活于2017年8月24日,區(qū)塊高度481,824瑟捣。此前馋艺,比特幣的交易驗證,需要依賴兩部分數(shù)據迈套,一部分是交易狀態(tài)捐祠,簡單地說就是誰給誰轉賬多少錢;另一部分是見證數(shù)據桑李,證明這個交易的真實性和合法性踱蛀。我們知道,交易一旦確定贵白,狀態(tài)就是不可更改的了率拒,但是見證數(shù)據由于其算法設計,卻是可以改變的禁荒,或者說證據是可以不只一份的猬膨。那么如果有惡意攻擊者,通過修改見證數(shù)據就可以修改交易ID呛伴,這被稱之為延展性攻擊勃痴,會帶來相當?shù)牟话踩在怂fMt.Gox黑客事件就從這個漏洞而來。

隔離見證的提出沛申,將見證數(shù)據隔離在區(qū)塊基本信息之外劣领,也就意味著交易ID只跟交易狀態(tài)有關,那么交易一旦發(fā)生铁材,任何人都無法再修改交易ID尖淘,這就順利解決了所謂的延展性攻擊。同時它帶來的另外一個好處著觉,就是區(qū)塊容量在不需要硬分叉的前提下增大了村生,并且為下一步閃電網絡鋪平了路子。

隔離見證是比特幣歷史上的重大變革

關于隔離見證的知識固惯,還可以參見隔離見證(CSDN)以及什么是隔離見證(知乎)這兩篇貼子梆造,英文好的可以直接看Github上的說明缴守。本文的重點葬毫,依然是直接切入源碼看實現(xiàn)。

那么隔離見證是如何實現(xiàn)的呢屡穗?

一贴捡、怎么隔離,在哪隔離

首先來看交易輸入CTxIn村砂,下面是它的部分代碼(src/primitives/transaction.h):

class CTxIn
{
public:
    COutPoint prevout;
    CScript scriptSig;
    uint32_t nSequence;
    CScriptWitness scriptWitness;  //僅當交易被序列化時才參與

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(prevout);
        READWRITE(scriptSig);
        READWRITE(nSequence);
    }
    //...
}

簡單地理解烂斋,所謂隔離見證,就是把原來scriptSig里的主要內容础废,轉移到scriptWitness中去汛骂,注意上面的序列化代碼中,scriptWitness是不會被序列化的评腺,它只在整個交易被序列化時才參與帘瞭。同時,相應的scriptSig就變成空腳本了蒿讥,這就是所謂的隔離蝶念,附帶的一個好處就是交易size減小了,相應的交易費用也會降低芋绸。需要注意的是媒殉,scriptWitness里的內容是經過了進一步處理的,已經不再是腳本摔敛,詳情可以參見上面列出的參考文章廷蓉。

那么scriptWitness是什么時候生成的?答案是在CreateTransaction里马昙,最后生成簽名的時候桃犬。以下是src/script/sign.cpp中ProduceSignature函數(shù)的部分代碼售貌,這里只引用隔離見證相關的部分:

bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, 
                      const CScript& fromPubKey, SignatureData& sigdata)
{
    //...
    if (solved && whichType == TX_WITNESS_V0_KEYHASH)
    {
        CScript witnessscript;  //簽名腳本
        witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
        txnouttype subType;
        solved = solved && SignStep(provider, creator, witnessscript, result, subType, 
                                    SigVersion::WITNESS_V0, sigdata);
        sigdata.scriptWitness.stack = result;  //填入scriptWitness
        sigdata.witness = true;
        result.clear();  //注意這里清空了
    }
    else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
    {
        CScript witnessscript(result[0].begin(), result[0].end());
        sigdata.witness_script = witnessscript;  //贖回腳本
        txnouttype subType;
        solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) 
                        && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
        result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
        sigdata.scriptWitness.stack = result;  //填入scriptWitness
        sigdata.witness = true;
        result.clear();  //注意這里清空了
    } else if (solved && whichType == TX_WITNESS_UNKNOWN) {
        sigdata.witness = true;
    }
    sigdata.scriptSig = PushAll(result);  //實際上是scriptSig清空了
    //...
    return sigdata.complete;
}

可以看到,如果使用了隔離見證疫萤,那么交易簽名被存入了scriptWitness颂跨,而不是scriptSig。這就是所謂隔離的由來扯饶。

注意scriptWitness內部使用的stack來存儲數(shù)據恒削,每個witness都由一個var_int打頭,代表接下來的數(shù)據長度尾序。如果某個輸入沒有見證钓丰,那么其witness就是一個0x00。

二每币、Transaction ID

一個交易的txid是以下序列的雙SHA256加密結果:

[nVersion][txins][txouts][nLockTime]

采用隔離見證以后携丁,txid的定義仍然保持不變,但是另外增加了一個wtxid兰怠,它對應的序列是這樣:

[nVersion][marker][flag][txins][txouts][witness][nLockTime]

下面是src/primitives/transaction.h(cpp)中的相關源碼梦鉴,為便于閱讀,稍有整理:

class CTransaction
{
    //...
private:
    //這兩個hash值在交易被構建時計算揭保,并且只在內存中不寫磁盤
    //注意CTransaction數(shù)據值是不會變的肥橙,會變的是CMutableTransaction
    const uint256 hash;
    const uint256 m_witness_hash;

    uint256 ComputeHash() const {  //計算txid,注意設定了無見證參數(shù)
        return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
    }
    uint256 ComputeWitnessHash() const {  //計算wtxid秸侣,第3個參數(shù)為0默認有見證
        if (!HasWitness())  return hash;  //如果沒有見證數(shù)據存筏,直接返回hash
        return SerializeHash(*this, SER_GETHASH, 0);
    }
    //...
}

SerializeHash函數(shù),采用輸入流的方式讀取Transaction數(shù)據味榛,最后調用的是SerializeTransaction函數(shù):

template<typename Stream, typename TxType>
inline void SerializeTransaction(const TxType& tx, Stream& s) {
    //根據Computer時設定的參數(shù)椭坚,確定帶不帶見證
    const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
    s << tx.nVersion;
    unsigned char flags = 0;
    if (fAllowWitness) {
        if (tx.HasWitness()) {  //帶見證,且確實包含見證數(shù)據
            flags |= 1;
        }
    }
    if (flags) {
        std::vector<CTxIn> vinDummy;
        s << vinDummy;  //輸入一個空vector搏色,其實就是輸入一個0善茎,它對應的就是marker
        s << flags;  //對應flag,一定是1
    }
    s << tx.vin;
    s << tx.vout;
    if (flags & 1) {  //如果帶見證继榆,依次輸入見證數(shù)據
        for (size_t i = 0; i < tx.vin.size(); i++) {
            s << tx.vin[i].scriptWitness.stack;
        }
    }
    s << tx.nLockTime;
}

下面是對應的UnsierializeTransaction函數(shù):

template<typename Stream, typename TxType>
inline void UnserializeTransaction(TxType& tx, Stream& s) {
    const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
    s >> tx.nVersion;
    unsigned char flags = 0;
    tx.vin.clear();
    tx.vout.clear();
    s >> tx.vin;  //先讀一個vin巾表,來判斷到底有沒有見證數(shù)據。如果沒有見證略吨,這里就是正常的vin
    if (tx.vin.size() == 0 && fAllowWitness) {  //確實是空的集币,而且?guī)б娮C,那么剛剛讀取的就是marker
        s >> flags;  //再讀入flag翠忠,目前必定為1
        if (flags != 0) {  //然后開始讀輸入鞠苟、輸出
            s >> tx.vin; 
            s >> tx.vout;
        }
    } else {
        s >> tx.vout;  //vin剛剛已經讀了,這里只讀vout就可以了
    }
    if ((flags & 1) && fAllowWitness) {
        flags ^= 1;
        for (size_t i = 0; i < tx.vin.size(); i++) {
            s >> tx.vin[i].scriptWitness.stack;  //依次讀入見證數(shù)據
        }
    }    if (flags) {
        //如果讀入flags不是1(可能是未來版本生成的),拋出異常
        throw std::ios_base::failure("Unknown transaction optional data");
    }
    s >> tx.nLockTime;
}

三当娱、Coinbase Commitment

我們知道吃既,交易信息是被打包進MerkleTreeRoot,然后寫進區(qū)塊頭確保不可篡改的跨细。那么隔離見證之后鹦倚,我們同樣也要確保witness數(shù)據不可篡改。比特幣是怎么來實現(xiàn)的呢冀惭?

首先震叙,所有的wtxid會被打包進見證版的Merkle樹,見src/consensus/merkle.cpp中的BlockWitnessMerkleRoot函數(shù):

uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
{
    std::vector<uint256> leaves;
    leaves.resize(block.vtx.size());
    leaves[0].SetNull();  //幣基交易的見證哈希是0.
    for (size_t s = 1; s < block.vtx.size(); s++) {
        leaves[s] = block.vtx[s]->GetWitnessHash();
    }
    return ComputeMerkleRoot(std::move(leaves), mutated);
}

隨后散休,在生成區(qū)塊的時候媒楼,創(chuàng)建幣基交易時,會生成一個Coinbase Commitment(幣基承諾)戚丸。下面是src/miner.cpp中CreateNewBlock函數(shù)的節(jié)選:

CMutableTransaction coinbaseTx;
coinbaseTx.vin.resize(1);
coinbaseTx.vin[0].prevout.SetNull();
coinbaseTx.vout.resize(1);
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
pblocktemplate->vTxFees[0] = -nFees;

上面倒數(shù)第二行划址,調用了GenerateCoinbaseCommitment函數(shù),它定義在src/validation.cpp中限府,源碼是這樣的:

std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, 
                                                      const Consensus::Params& consensusParams)
{
    std::vector<unsigned char> commitment;
    int commitpos = GetWitnessCommitmentIndex(block);  //從幣基交易的輸出中尋找承諾項夺颤,沒找到就返回-1
    std::vector<unsigned char> ret(32, 0x00);
    if (consensusParams.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
        if (commitpos == -1) {  //沒有找到,就開始創(chuàng)建承諾谣殊,先生成見證版Merkle樹根
            uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
            CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin());
            CTxOut out;  //構建一個幣基交易的輸出
            out.nValue = 0;  //金額是0
            out.scriptPubKey.resize(38);  //公鑰腳本長度38拂共,前6個字節(jié)固定為0x6a24aa21a9ed
            out.scriptPubKey[0] = OP_RETURN;  //0x6a
            out.scriptPubKey[1] = 0x24;  //36,即后面的總長度
            out.scriptPubKey[2] = 0xaa;  //0xaa21a9ed姻几,固定不變的承諾頭
            out.scriptPubKey[3] = 0x21;
            out.scriptPubKey[4] = 0xa9;
            out.scriptPubKey[5] = 0xed;
            memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);  //插入見證版Merkle樹根
            commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end());
            CMutableTransaction tx(*block.vtx[0]);
            tx.vout.push_back(out);  //幣基交易中添加這個輸出
            block.vtx[0] = MakeTransactionRef(std::move(tx));  //寫回區(qū)塊
        }
    }
    UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams);  //更新區(qū)塊其他結構
    return commitment;
}

幣基交易中添加輸出之后,它的輸入也有相應變化势告,也就是上面最后調用的UpdateUncommittedBlockStructures函數(shù):

void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, 
                                      const Consensus::Params& consensusParams)
{
    int commitpos = GetWitnessCommitmentIndex(block);
    static const std::vector<unsigned char> nonce(32, 0x00);
    if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) {
        CMutableTransaction tx(*block.vtx[0]);  //修改幣基交易
        tx.vin[0].scriptWitness.stack.resize(1);  //向空輸入中添加一項
        tx.vin[0].scriptWitness.stack[0] = nonce;
        block.vtx[0] = MakeTransactionRef(std::move(tx));  //寫回區(qū)塊
    }
}

OK蛇捌,既然費那么大勁寫入承諾,那么一定要對它進行檢查咱台,否則就失去意義了络拌。這段代碼在ContextualCheckBlock函數(shù)中,以下是它的部分代碼:

bool fHaveWitness = false;
if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) 
                    == ThresholdState::ACTIVE) {
    int commitpos = GetWitnessCommitmentIndex(block);
    if (commitpos != -1) {
        bool malleated = false;
        uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated);
        if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 
                || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
            return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, 
                             strprintf("%s : invalid witness reserved value size", __func__));
        }
        CHash256().Write(hashWitness.begin(), 32)
                  .Write(&block.vtx[0]->vin[0].scriptWitness.stack[0][0], 32)
                  .Finalize(hashWitness.begin());
        if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
            return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, 
                             strprintf("%s : witness merkle commitment mismatch", __func__));
        }
        fHaveWitness = true;
    }
}

四回溺、交易哈希算法

隔離見證同時還修改了交易簽名所用的哈希算法春贸,此前原有算法存在兩個方面缺陷,一個是當交易中sigOp數(shù)量增加時遗遵,復雜度呈平方增長萍恕;另一個是算法不涉及輸入金額,可能對冷錢包的使用有所影響车要。

關于新的交易哈希算法的詳細解釋允粤,可以參見Github上的原文 。下面直接摘取src/script/interpreter.cpp中的SignatureHash函數(shù)的部分源碼:

template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, 
                      const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
    //...
    if (sigversion == SigVersion::WITNESS_V0) {
        uint256 hashPrevouts, hashSequence, hashOutputs;
        const bool cacheready = cache && cache->ready;
        if (!(nHashType & SIGHASH_ANYONECANPAY)) {
            hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
        }
        if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE 
                && (nHashType & 0x1f) != SIGHASH_NONE) {
            hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo);
        }
        if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
            hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);
        } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
            CHashWriter ss(SER_GETHASH, 0);
            ss << txTo.vout[nIn];
            hashOutputs = ss.GetHash();
        }
        //數(shù)據準備好了,下面是正式處理過程类垫,可以看出其復雜度明顯降低
        CHashWriter ss(SER_GETHASH, 0);
        ss << txTo.nVersion;  //版本號
        ss << hashPrevouts;
        ss << hashSequence;
        ss << txTo.vin[nIn].prevout;
        ss << scriptCode;
        ss << amount;  //金額這里包含了
        ss << txTo.vin[nIn].nSequence;
        ss << hashOutputs;
        ss << txTo.nLockTime;
        ss << nHashType;
        return ss.GetHash();
    }
    //...
}

五司光、腳本驗證

在創(chuàng)建交易的最后,會對簽名腳本進行驗證悉患,涉及到隔離見證的部分残家,先看src/scripts/interpreter.cpp中VerifyScript函數(shù)的部分源碼:

bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, 
                  unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
    //...
    int witnessversion;
    std::vector<unsigned char> witnessprogram;
    if (flags & SCRIPT_VERIFY_WITNESS) {
        if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
            hadWitness = true;
            if (scriptSig.size() != 0) {
                return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED);
            }
            if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
                return false;
            }
            stack.resize(1);
        }
    }
    //...
}

可以看到,它調用了VerifyWitnessProgram來進行驗證售躁。它的源碼是這樣的:

static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, 
                 const std::vector<unsigned char>& program, unsigned int flags, 
                 const BaseSignatureChecker& checker, ScriptError* serror)
{
    std::vector<std::vector<unsigned char> > stack;
    CScript scriptPubKey;

    if (witversion == 0) {
        if (program.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
            //32位的P2WSH跪削,witness為stack + witnessScript,而witnessScript經雙SHA256就是32位program
            if (witness.stack.size() == 0) {
                return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
            }
            scriptPubKey = CScript(witness.stack.back().begin(), witness.stack.back().end());
            stack = std::vector<std::vector<unsigned char> >(witness.stack.begin(), witness.stack.end() - 1);
            uint256 hashScriptPubKey;
            CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin());
            if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) {
                return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
            }
        } else if (program.size() == WITNESS_V0_KEYHASH_SIZE) {
            //20位P2WPKH迂求,witness就是sig + pubkey碾盐,其中pubkey經過HASH160之后就是20位program
            if (witness.stack.size() != 2) {
                return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
            }
            scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
            stack = witness.stack;
        } else {
            return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH);
        }
    } else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
        return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
    } else {  //高版本見證腳本就等將來的軟分叉吧
        return set_success(serror);
    }

    //棧數(shù)據不允許溢出
    for (unsigned int i = 0; i < stack.size(); i++) {
        if (stack.at(i).size() > MAX_SCRIPT_ELEMENT_SIZE)
            return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
    }

    //執(zhí)行一下,看看結果是不是TRUE
    if (!EvalScript(stack, scriptPubKey, flags, checker, SigVersion::WITNESS_V0, serror)) {
        return false;
    }
    //stack最后只能剩1個數(shù)據TRUE
    if (stack.size() != 1)
        return set_error(serror, SCRIPT_ERR_CLEANSTACK);
    if (!CastToBool(stack.back()))
        return set_error(serror, SCRIPT_ERR_EVAL_FALSE);
    return true;
}

謝謝閱讀揩局。如有不妥之處毫玖,請高手不吝指正。


原創(chuàng)不易凌盯,懇請支持付枫!你的贊賞,我的動力驰怎!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末阐滩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子县忌,更是在濱河造成了極大的恐慌掂榔,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件症杏,死亡現(xiàn)場離奇詭異装获,居然都是意外死亡,警方通過查閱死者的電腦和手機厉颤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門穴豫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人逼友,你說我怎么就攤上這事精肃。” “怎么了帜乞?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵司抱,是天一觀的道長。 經常有香客問我挖函,道長状植,這世上最難降的妖魔是什么浊竟? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮津畸,結果婚禮上振定,老公的妹妹穿的比我還像新娘。我一直安慰自己肉拓,他們只是感情好后频,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著暖途,像睡著了一般卑惜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驻售,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天露久,我揣著相機與錄音,去河邊找鬼欺栗。 笑死毫痕,一個胖子當著我的面吹牛,可吹牛的內容都是我干的迟几。 我是一名探鬼主播消请,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼类腮!你這毒婦竟也來了臊泰?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蚜枢,失蹤者是張志新(化名)和其女友劉穎缸逃,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祟偷,經...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡察滑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了修肠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡户盯,死狀恐怖嵌施,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情莽鸭,我是刑警寧澤吗伤,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站硫眨,受9級特大地震影響足淆,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一巧号、第九天 我趴在偏房一處隱蔽的房頂上張望族奢。 院中可真熱鬧,春花似錦丹鸿、人聲如沸越走。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽廊敌。三九已至,卻和暖如春门怪,著一層夾襖步出監(jiān)牢的瞬間骡澈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工掷空, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肋殴,地道東北人。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓拣帽,卻偏偏與公主長得像疼电,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子减拭,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內容