fabric-sdk-java爬坑歷程

本文章使用的環(huán)境為1.4.0版本(都是一點(diǎn)一點(diǎn)磨出來(lái)的饼暑,有用請(qǐng)點(diǎn)個(gè)贊吧告抄,嗚嗚嗚)

主要實(shí)現(xiàn)的功能:

  1. 客戶(hù)端初始化
  2. 通道初始化
  3. 鏈碼安裝
  4. 鏈碼初始化
  5. 調(diào)用鏈碼
  6. 通道創(chuàng)建
  7. 加入通道

Demo路徑
https://github.com/ccDown/fbsdkdemo
或者
https://github.com/ccDown/MyJavaFabricDemo

1. 客戶(hù)端初始化

HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
client.setUserContext(new SimpleUser());

2. 通道初始化

orderer1Prop中的pemFile對(duì)應(yīng)的是orderer中的tls證書(shū)
peer1Prop中的pemFile對(duì)應(yīng)的是peer節(jié)點(diǎn)中的tls證書(shū)
peer1Prop中的clientKeyFile對(duì)應(yīng)的是peer節(jié)點(diǎn)中的tls文件下的keystore
peer1Prop中的clientCertFile對(duì)應(yīng)的是peer節(jié)點(diǎn)中的tls文件下的signcerts

    // initialize Channel
    private Channel initializeChannel(HFClient client, String channelName) throws InvalidArgumentException, TransactionException {
        Properties orderer1Prop = new Properties();
        orderer1Prop.setProperty("pemFile", Config.ORDERER1_TLS_CACERT_FILE);
        orderer1Prop.setProperty("sslProvider", "openSSL");
        orderer1Prop.setProperty("negotiationType", "TLS");
        orderer1Prop.setProperty("hostnameOverride", "chainorder1.com");
        orderer1Prop.setProperty("trustServerCertificate", "true");
        orderer1Prop.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000);
        Orderer orderer = client.newOrderer("chainorder1.com", Config.ORDERER1_ADDRESS, orderer1Prop);

        Properties peer1Prop = new Properties();
        peer1Prop.setProperty("pemFile", Config.PEER1_TLS_CACERT_FILE);
        peer1Prop.setProperty("sslProvider", "openSSL");
        peer1Prop.setProperty("negotiationType", "TLS");
        peer1Prop.setProperty("hostnameOverride", "peer1.operation.com");
        peer1Prop.setProperty("trustServerCertificate", "true");
        peer1Prop.setProperty("clientKeyFile", Config.PEER1_TLS_CLIENTKEY);
        peer1Prop.setProperty("clientCertFile", Config.PEER1_TLS_CLIENTCERT);
        peer1Prop.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000);
        Peer peer = client.newPeer("peer1.operation.com", Config.PEER1_ADDRESS, peer1Prop);

        EventHub eventHub = client.newEventHub("peer1.operation.com",Config.EVENTHUB1_ADDRESS,peer1Prop);

        Channel channel = client.newChannel(channelName);
        channel.addOrderer(orderer);
        channel.addPeer(peer);
        channel.addEventHub(eventHub);
        channel.initialize();
        System.out.println("channel初始化");
        return channel;
    }

3. 鏈碼安裝

(鏈碼默認(rèn)會(huì)安裝到peer節(jié)點(diǎn)的/var/hyperledger/production/chaincodes目錄下,測(cè)試鏈碼安裝的時(shí)候可以去這個(gè)目錄下查找,一定要注意是peer節(jié)點(diǎn)的目錄下忿偷,我好多次都跑到cli里邊找半天找不到枪萄。伶选。史飞。)
鏈碼安裝分為go和java兩種方式尖昏,每種語(yǔ)言的鏈碼安裝也有兩種方式

3.1

installProposalRequest.setChaincodeSourceLocation(sourceLocation);
go鏈碼:
需要額外指定 installProposalRequest.setChaincodePath(chaincodePath);
使用這種方式要確保 sourceLocation子目錄下為chaincodePath仰税,chaincodePath子目錄包含鏈碼,即整體的目錄為 sourceLocation/chaincodePath/鏈碼.go
java鏈碼:
sourceLocation要指定到項(xiàng)目的src目錄下抽诉,否則初始化會(huì)失敗陨簇。也就是說(shuō)sourceLocation的路徑為:項(xiàng)目路徑/src

3.2

installProposalRequest.setChaincodeInputStream(inputStream);這個(gè)方法需要傳的參數(shù)是經(jīng)過(guò)tar壓縮之后的流(使用官方的Util包下的generateTarGzInputStream工具進(jìn)行壓縮)

java鏈碼、nodejs鏈碼:Util.generateTarGzInputStream(Paths.get(sourcePath, projectName).toFile(), "src")
sourcePath/projectName為項(xiàng)目全路徑迹淌,projectName為項(xiàng)目名稱(chēng)河绽,即壓縮的時(shí)候指定的兩個(gè)路徑為項(xiàng)目全路徑和代碼的"src"路徑

go鏈碼:Util.generateTarGzInputStream(Paths.get(sourcePath,"src", ccPath).toFile(), Paths.get("src",ccPath).toString())
sourcePath/src為gopath,ccPath為chaincode相對(duì)于gopath的路徑

另外go鏈碼需要額外指定 installProposalRequest.setChaincodePath(chaincodePath);

    public static Collection<ProposalResponse> InstallChainCode(HFClient hfClient, Channel channel, String chainCodePath, String projectName, String chainCodeName, TransactionRequest.Type language) throws InvalidArgumentException, ProposalException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, CryptoException, ClassNotFoundException, IOException {

        checkClientInit();

        checkChannelInit();

        InstallProposalRequest installProposalRequest = hfClient.newInstallProposalRequest();
        ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chainCodeName).setVersion("1.0").build();
        installProposalRequest.setChaincodeID(chaincodeID);
        installProposalRequest.setChaincodeVersion("1.0");
        installProposalRequest.setChaincodeLanguage(language);
        if (language.equals(TransactionRequest.Type.GO_LANG)) {
//            installProposalRequest.setChaincodeSourceLocation(new File(chainCodePath));
            installProposalRequest.setChaincodeInputStream(Util.generateTarGzInputStream(Paths.get(chainCodePath, projectName).toFile(), ""));

            installProposalRequest.setChaincodePath(chainCodeName);
        } else {
            installProposalRequest.setChaincodeSourceLocation(new File(chainCodePath+"\\"+ projectName));
//            System.out.println("Paths>>>>>>>>>>>>>>>"+Paths.get(chainCodePath,projectName).toFile());
//            installProposalRequest.setChaincodeInputStream(Util.generateTarGzInputStream(Paths.get(chainCodePath,projectName).toFile(),"src"));
        }
        Collection<ProposalResponse> proposalResponses = hfClient.sendInstallProposal(installProposalRequest, channel.getPeers());
        return proposalResponses;
    }

4. 鏈碼初始化

私有數(shù)據(jù)的鏈碼需要額外調(diào)用instantiateProposalRequest.setChaincodeCollectionConfiguration方法

    public static Collection<ProposalResponse> InstantiateChainCode(HFClient hfClient, Channel channel, TransactionRequest.Type language, String chainCodeName, String[] args, String endorsementPolicy) throws ProposalException, InvalidArgumentException, InterruptedException, ExecutionException, TimeoutException, IOException, ChaincodeEndorsementPolicyParseException {

        checkClientInit();

        checkChannelInit();

        InstantiateProposalRequest instantiateProposalRequest = hfClient.newInstantiationProposalRequest();
        instantiateProposalRequest.setProposalWaitTime(120000);
        ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chainCodeName).setVersion("1.0").setPath(chainCodeName).build();
        instantiateProposalRequest.setChaincodeID(chaincodeID);
        instantiateProposalRequest.setChaincodeLanguage(language);
        instantiateProposalRequest.setFcn("init");
        instantiateProposalRequest.setArgs(args);

        //region 如果需要用到私有數(shù)據(jù)唉窃,需要添加私有數(shù)據(jù)的配置
        //instantiateProposalRequest.setChaincodeCollectionConfiguration(ChaincodeCollectionConfiguration.fromYamlFile(new File("src/test/fixture/collectionProperties/PrivateDataIT.yaml")));
        //endregion

        Map<String, byte[]> tm = new HashMap<>();
        tm.put("HyperLedgerFabric", "InstantiateProposalRequest:JavaSDK".getBytes(UTF_8));
        tm.put("method", "InstantiateProposalRequest".getBytes(UTF_8));
        instantiateProposalRequest.setTransientMap(tm);
        if (!endorsementPolicy.equals("")){
            ChaincodeEndorsementPolicy chaincodeEndorsementPolicy = new ChaincodeEndorsementPolicy();
            chaincodeEndorsementPolicy.fromYamlFile(new File(endorsementPolicy));
            instantiateProposalRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);
        }
        Collection<ProposalResponse> responses = channel.sendInstantiationProposal(instantiateProposalRequest,channel.getPeers());

        List<ProposalResponse> list = responses.stream().filter(ProposalResponse::isVerified).collect(Collectors.toList());
        if (list.size() == 0) {
            return responses;
        }
        BlockEvent.TransactionEvent event = channel.sendTransaction(responses).get(60, TimeUnit.SECONDS);

        if (event.isValid()){
            logger.info("InstantiateChainCode success");
        }

        return responses;
    }

5. 鏈碼調(diào)用

調(diào)用完channel.sendTransactionProposal()方法之后實(shí)際上只是完成了模擬交易的環(huán)節(jié)耙饰,接下來(lái)需要調(diào)用channel.sendTransaction()方法將成功的提案發(fā)送給Orderer節(jié)點(diǎn)進(jìn)行真正的交易,并對(duì)該方法的返回回調(diào)進(jìn)行事件監(jiān)聽(tīng)獲取數(shù)據(jù)落鏈情況纹份,此時(shí)交易才完成苟跪。(私有數(shù)據(jù)鏈碼需要額外調(diào)用transactionProposalRequest.setTransientMap方法)

  public Collection<ProposalResponse> invoke(HFClient client, Channel channel, String chaincodeName, String func, String args) throws InvalidArgumentException, ProposalException {
        TransactionProposalRequest request = client.newTransactionProposalRequest();
        String cc = chaincodeName;
        ChaincodeID ccid = ChaincodeID.newBuilder().setName(cc).build();
        request.setChaincodeID(ccid);
        request.setFcn(func);
        request.setArgs(args);
        request.setProposalWaitTime(3000);
        Collection<ProposalResponse> responses = channel.sendTransactionProposal(request);

        //region  如果使用私有數(shù)據(jù),需要通過(guò)map傳遞臨時(shí)數(shù)據(jù)
//        Map<String, byte[]> tm = new HashMap<>();
//        tm.put("A", "a".getBytes(UTF_8));
//        tm.put("AVal", "500".getBytes(UTF_8));
//        tm.put("B", "b".getBytes(UTF_8));
//        String arg3 = "" + (200 + 1);
//        tm.put("BVal", arg3.getBytes(UTF_8));
//        transactionProposalRequest.setTransientMap(tm);
        //endregion

        Collection<ProposalResponse> respsponse = channel.sendTransactionProposal(transactionProposalRequest);

        List<ProposalResponse> list = respsponse.stream().filter(ProposalResponse::isVerified).collect(Collectors.toList());
        if (list.size() == 0) {
            return respsponse;
        }
        BlockEvent.TransactionEvent event = channel.sendTransaction(respsponse).get(60, TimeUnit.SECONDS);

        if (event.isValid()){
            logger.info("Fabric 鏈碼交互事件成功");
        }

        logger.info("Fabric 鏈碼交互完成");

        return respsponse;
    }

6. 通道創(chuàng)建

在官方SDK里邊只有創(chuàng)建完通道之后馬上加入通道蔓涧,而之后無(wú)法讓其他節(jié)點(diǎn)通過(guò)SDK加入(可能是自己菜件已,沒(méi)看到)。通過(guò)這種方式可以實(shí)現(xiàn)以后任意節(jié)點(diǎn)想加入通道就加入元暴,姑且叫它c(diǎn)hannel. block(跟命令行生成的. block格式是不一樣的篷扩,不能在命令行中使用這個(gè)文件讓節(jié)點(diǎn)加入通道,如果需要通過(guò)cli的方式添加需要通過(guò)fetch命令獲取通道配置創(chuàng)世區(qū)塊:peer channel fetch oldest [outputfile] [flags])

public static Channel createChannel(HFClient hfClient,String channelName, byte[] txByte,Orderer orderer) throws InvalidArgumentException, TransactionException, IOException {
        logger.info("Fabric 創(chuàng)建通道開(kāi)始");

        ChannelConfiguration configuration = new ChannelConfiguration(txByte);
        byte[] signData = hfClient.getChannelConfigurationSignature(configuration,hfClient.getUserContext());
        Channel channel = hfClient.newChannel(channelName,orderer,configuration,signData);
        //保存channel信息供以后加入通道使用茉盏。
        channel.serializeChannel(new File("/ channel.block"));
        channel.initialize();

        logger.info("Fabric 創(chuàng)建通道完成");
        return channel;
    }

7. 加入通道

通過(guò)之前序列化保存的channel對(duì)象文件channel. block創(chuàng)建channel從而使其他節(jié)點(diǎn)加入通道

public static Channel joinChannel(HFClient hfClient, byte[] channelByte, Peer peer, Channel.PeerOptions peerOptions) throws InvalidArgumentException, IOException, ClassNotFoundException, TransactionException, ProposalException {
        logger.info("Fabric 加入通道開(kāi)始");
        //反序列化之前保存的通道信息文件
        Channel channel = hfClient.deSerializeChannel(channelByte);
        channel.initialize();
        channel.joinPeer(peer,peerOptions);

        logger.info("Fabric 加入通道完成");

        return channel;
    }

坑點(diǎn):

1. TLS驗(yàn)證不通過(guò)

大概率是你的證書(shū)指定不對(duì)鉴未,這時(shí)候可以使用wireshark抓包看看SSL握手過(guò)程。在tls握手的時(shí)候使用的證書(shū)包括
· order的tls下的ca證書(shū)
· peer的tls的ca證書(shū)鸠姨、server.key铜秆、server.crt證書(shū)
· admin的msp中的keystore和signcerts證書(shū)
如果實(shí)在繞不過(guò)去tls證書(shū)校驗(yàn),可以先把SDK源碼中的EndPoint類(lèi)的262-266行改成下面的享怀,等別的調(diào)通了再搞這個(gè)羽峰,萬(wàn)一哪天運(yùn)氣來(lái)了不是。

//                        try (InputStream myInputStream = new ByteArrayInputStream(pemBytes)) {
//                            sslContext = clientContextBuilder
//                                    .trustManager(myInputStream)
//                                    .build();
//                        }
//                        //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                        sslContext = clientContextBuilder
                                .trustManager(InsecureTrustManagerFactory.INSTANCE)
                                .build();
//                        //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

2. 初始化go鏈碼的時(shí)候出現(xiàn)的問(wèn)題

· 2.1可能會(huì)報(bào)找不到類(lèi)之類(lèi)的錯(cuò)誤,官網(wǎng)的解決辦法是使用vendor來(lái)加載依賴(lài)包梅屉。


https://github.com/hyperledger/fabric-sdk-java/tree/v1.4.4#go-lang-chaincode

3. 鏈碼實(shí)例化可能會(huì)炒雞慢哦

4. 需要發(fā)送給orderer的都需要調(diào)用channel.sendTranscation方法將模擬交易的結(jié)果提案到排序節(jié)點(diǎn)

5. grpc通訊4M限制

因?yàn)镾DK使用的grpc進(jìn)行通訊值纱,所以會(huì)有通訊報(bào)文大小的限制,默認(rèn)是4M坯汤,那么怎么突破這種限制呢虐唠,官方是這么縮滴


https://github.com/hyperledger/fabric-sdk-java#grpc-message-frame-size-exceeds-maximum

然后我親自去翻開(kāi)End2endIT類(lèi)


image.png

所以縮,只要在通道初始化的時(shí)候給peer和order添加這一行配置惰聂,我們就解決了這個(gè)4M限制疆偿。偶吼吼。

6. Android設(shè)備如何使用fabric-sdk-java

本人閑來(lái)無(wú)事在GitHub上閑逛搓幌,就跟女孩子內(nèi)種只逛不買(mǎi)的一樣杆故。一不小心就看到了一個(gè)項(xiàng)目 android-fabric-sdk
人家是把grpc-netty換成了grpc-okhttp來(lái)支持Android設(shè)備,看起來(lái)很吊的樣子

7.nodejs鏈碼實(shí)例化失敗

Error: could not assemble transaction, err proposal response was not successful, error code 500, msg error starting container: error starting container: Failed to generate platform-specific docker build: Error returned from build: 1 "npm ERR! code EAI_AGAIN
npm ERR! errno EAI_AGAIN
npm ERR! request to https://registry.npmjs.org/fabric-shim failed, reason: getaddrinfo EAI_AGAIN registry.npmjs.org:443

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2019-09-16T06_57_58_381Z-debug.log
"

出現(xiàn)這個(gè)問(wèn)題是因?yàn)閐ocker內(nèi)部DNS無(wú)法解析溉愁。解決方案:

$ sudo apt-get install bridge-utils -y
$ sudo service docker stop
$ sudo ip link set dev docker0 down
$ sudo brctl delbr docker0
$ sudo service docker start
$ docker network inspect bridge

之后我的node鏈碼可以進(jìn)行實(shí)例化了处铛,不過(guò)依然很慢。后來(lái)詢(xún)問(wèn)過(guò)某些大佬拐揭,可以修改源加快一點(diǎn)構(gòu)建容器的速度撤蟆。嘗試從fabric源碼里邊查找關(guān)于創(chuàng)建Dockerfile的方法自己重新構(gòu)建一個(gè)fabric-ccenv,發(fā)現(xiàn)挺復(fù)雜堂污。家肯。。后來(lái) 在這里卡了好幾天盟猖。
最后嘗試重新構(gòu)建一個(gè)淘寶npm源的hyperledger/fabric-ccenv讨衣,速度快了很多。

FROM hyperledger/fabric-ccenv:1.4.1

ENV NPM_CONFIG_BUILD_FROM_SOURCE=true

ENV NPM_CONFIG_REGISTRY=https://registry.npm.taobao.org

然后執(zhí)行docker build -t hyperledger/fabric-ccenv:latest .
也可以執(zhí)行以下命令拉取我已經(jīng)build好的鏡像

docker push soullistener/fabric-ccenv:tagname

然后修改tag為hyperledger/fabric-ccenv:latest

8.指紋不匹配

response: status:500 message:"failed to execute transaction 51e5aae96105281fb96e26700ed9ed4fcb25c42fd1c702bbc5b3f2ffd0a58338: [channel mychannel] failed to get chaincode container info for chaincode:1.0.0: could not get chaincode code: chaincode fingerprint mismatch: data mismatch"

指紋不同是因?yàn)?鏈碼名稱(chēng)扒披、版本值依、讀寫(xiě)權(quán)限,owner/group碟案,時(shí)間戳愿险、不同的fabric版本,不同的go語(yǔ)言版本(tar庫(kù)版本)价说、不同的安裝方式(SDK辆亏,peer命令行安裝)等信息,任何不一致都會(huì)導(dǎo)致這個(gè)問(wèn)題鳖目。
使用程序打包安裝的時(shí)候要把generateTarGzInputStream生成的byte數(shù)組保存下來(lái)供以后其他的節(jié)點(diǎn)安裝使用扮叨,否則會(huì)因?yàn)榇虬鼤r(shí)間不同等問(wèn)題安裝之后的鏈碼hash不一樣導(dǎo)致鏈碼實(shí)例化失敗。

詳情:http://www.reibang.com/p/dca0546d85f6

9.java鏈碼實(shí)例化慢或超時(shí)

當(dāng)出現(xiàn)這種情況很大的可能是因?yàn)樵诎裫ava鏈碼打包為jar的時(shí)候會(huì)下載依賴(lài)的三方庫(kù)领迈,而下載三方庫(kù)是個(gè)很漫長(zhǎng)的過(guò)程彻磁,大概率會(huì)因?yàn)橄螺d到一半就超時(shí)了碍沐。因此需要修改為國(guó)內(nèi)的maven庫(kù)加速下載。

首先需要修改settings文件衷蜓,添加自己的倉(cāng)庫(kù)或者阿里云等庫(kù)

  <mirror>
    <id>maven-public</id>
    <mirrorOf>*</mirrorOf>
    <url>http://192.168.7.2:8081/repository/maven-public/</url>
  </mirror>

其次重新構(gòu)建javaenv(替換settings累提,從其他倉(cāng)庫(kù)下載三方庫(kù))

FROM hyperledger/fabric-javaenv:amd64-1.4.4
ADD ./settings.xml   /root/.sdkman/candidates/maven/current/conf
WORKDIR /root/chaincode-java

然后build后替換掉之前的javaenv,之后下載三方庫(kù)會(huì)快很多磁浇。

10 fabric-SDK-go實(shí)例化鏈碼必須指定背書(shū)策略問(wèn)題

有的小伙伴在實(shí)例化鏈碼的時(shí)候不想指定背書(shū)斋陪,想使用默認(rèn)的背書(shū)策略(任意節(jié)點(diǎn)背書(shū)即可),卻發(fā)現(xiàn)通過(guò)go的SDK指定空的背書(shū)策略會(huì)報(bào)錯(cuò)置吓,這可咋整

我們要修改SDK的代碼lscc的createChaincodeDeployProposal

  1. 去除policy為空校驗(yàn)
  2. 添加一個(gè)policy占位
image.png

可供參考的地方:

  1. 官方SDK地址:https://github.com/hyperledger/fabric-sdk-java/tree/v1.4.0(一定要多看test類(lèi)无虚,你要用的東西都能在這里找到)
  2. API :http://central.maven.org/maven2/org/hyperledger/fabric-sdk-java/fabric-sdk-java/
    (在整個(gè)SDK中最常用的類(lèi)就是 HFClient、Channel衍锚、EventHub友题,還有請(qǐng)求對(duì)應(yīng)的request類(lèi):InstallProposalRequest, InstantiateProposalRequest, QueryByChaincodeRequest, QuerySCCRequest, TransactionProposalRequest, UpgradeProposalRequest)
  3. IBM教程:https://developer.ibm.com/tutorials/hyperledger-fabric-java-sdk-for-tls-enabled-fabric-network/
  4. hyperledger中文文檔 https://hyperledgercn.github.io/hyperledgerDocs/sdk_java_zh/
  5. IBM的官方例子 https://github.com/IBM/blockchain-application-using-fabric-java-sdk
  6. GitHub上好多可以借鑒的項(xiàng)目,搜索關(guān)鍵字 fabric-java就出來(lái)好多
  7. 可以加入fabric國(guó)際大家庭聊天群來(lái)問(wèn)問(wèn)題 https://chat.hyperledger.org/channel/fabric-sdk-java

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末构拳,一起剝皮案震驚了整個(gè)濱河市咆爽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌置森,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件符糊,死亡現(xiàn)場(chǎng)離奇詭異凫海,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)男娄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)行贪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人模闲,你說(shuō)我怎么就攤上這事建瘫。” “怎么了尸折?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵啰脚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我实夹,道長(zhǎng)橄浓,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任亮航,我火速辦了婚禮荸实,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缴淋。我一直安慰自己准给,他們只是感情好泄朴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著露氮,像睡著了一般叼旋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沦辙,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天夫植,我揣著相機(jī)與錄音,去河邊找鬼油讯。 笑死详民,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陌兑。 我是一名探鬼主播沈跨,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼兔综!你這毒婦竟也來(lái)了软驰?” 一聲冷哼從身側(cè)響起涧窒,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锭亏,沒(méi)想到半個(gè)月后纠吴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡慧瘤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年戴已,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锅减。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡糖儡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出怔匣,到底是詐尸還是另有隱情握联,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布劫狠,位于F島的核電站拴疤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏独泞。R本人自食惡果不足惜呐矾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望懦砂。 院中可真熱鬧蜒犯,春花似錦组橄、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至淘菩,卻和暖如春遵班,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背潮改。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工狭郑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汇在。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓翰萨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親糕殉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亩鬼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354