Liquid 使用方法

三個節(jié)點鸭栖,測試一下p2p廣播功能帝雇,點到點消息發(fā)送功能和節(jié)點發(fā)現(xiàn)功能

  1. 場景模擬node1 發(fā)送廣播,node2和node3 訂閱冤灾,
  2. node1 點對點 發(fā)送數(shù)據(jù)給node2和node3
  3. node2連接node1 玛臂, node3連接node2烤蜕,node1發(fā)現(xiàn)node3 節(jié)點

代碼分別如下

node1

/*
Copyright (C) BABEC. All rights reserved.

SPDX-License-Identifier: Apache-2.0
*/

package main

import (
    "chainmaker.org/chainmaker/common/v2/crypto/asym"
    cmTls "chainmaker.org/chainmaker/common/v2/crypto/tls"
    cmx509 "chainmaker.org/chainmaker/common/v2/crypto/x509"
    "chainmaker.org/chainmaker/common/v2/helper"
    "chainmaker.org/chainmaker/net-liquid/core/host"
    "chainmaker.org/chainmaker/net-liquid/core/peer"
    "chainmaker.org/chainmaker/net-liquid/core/protocol"
    "chainmaker.org/chainmaker/net-liquid/core/util"
    "chainmaker.org/chainmaker/net-liquid/discovery/protocoldiscovery"
    nethost "chainmaker.org/chainmaker/net-liquid/host"
    "chainmaker.org/chainmaker/net-liquid/logger"
    "chainmaker.org/chainmaker/net-liquid/pubsub"
    "context"
    "fmt"
    ma "github.com/multiformats/go-multiaddr"
    "strconv"
    "time"
)

const msg = "Hello!My first LIQUID program demo."
const testProtocolID = "/test"
var (
    addrsTcp = []ma.Multiaddr{
        ma.StringCast("/ip4/127.0.0.1/tcp/7081"),
        ma.StringCast("/ip4/127.0.0.1/tcp/7084"),
        ma.StringCast("/ip4/127.0.0.1/tcp/7085"),
    }


    keyPEMs = [][]byte{
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDc16StWEUNF0W7iXrT/k0Niy1ZWKbheI2wkjMqJ3WcaoAoGCCqGSM49\nAwEHoUQDQgAEmung19cBCguCaWeyf/nnARHRISTxtaxxf3Zqn+EQxxeYsXqa8oAg\ncalmJaGcmk9kTc0aJwpi7bKvJQdrRg1vFw==\n-----END EC PRIVATE KEY-----\n"),
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIGLbfX/EvXQtyO+GJvj5rFB0gyTqO3g4gO/gHX2A/BgNoAoGCCqGSM49\nAwEHoUQDQgAE0nVVBXfDQp+EaRbXXfXjn3QG+KYKqApi13+aeNMO7hvS4FlS3B5Z\nMRUCS2oBICwDgLf2q6ef1T5by1u9+IKK7w==\n-----END EC PRIVATE KEY-----\n"),
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIEUG3DFeXHVr5WjTRfpuSou30zEy2DMmzmyxuSd7F7K9oAoGCCqGSM49\nAwEHoUQDQgAEF+Tvi0fMzJOT9DLS+SFKPIo8kb3ouM54dJ9ibbohpR7FC8Iiid4g\nJ1QNguDjxEmwMV7Q1YiAuighKEmvtb4R+Q==\n-----END EC PRIVATE KEY-----"),
    }

    certPEMs = [][]byte{
        []byte("-----BEGIN CERTIFICATE-----\nMIICcjCCAhegAwIBAgIIFPWUuqixxuAwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzU2MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNTYwHhcNMjEx\nMDI4MDgyMzU3WhcNNDExMDIzMDgyMzU3WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNTYx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNTYtbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAASa6eDX1wEKC4JpZ7J/+ecBEdEhJPG1rHF/\ndmqf4RDHF5ixeprygCBxqWYloZyaT2RNzRonCmLtsq8lB2tGDW8Xo4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEIOTdkAJ/Fe7PiOnSFe5Fa95mreAwpX2OiK2sB3+Bd86jMCsGA1UdIwQk\nMCKAIKUCNBAgGIS+Af0ucVsMq9ZGS6S2GLlbcDyrtghnqnAnMBkGA1UdEQQSMBCC\nDmhjLW9yZzU2LW5vZGUxMAoGCCqGSM49BAMCA0kAMEYCIQDH+9IoSsBPE33ZRuog\ng9FBCAVj30uuxB08b+eUjbdDGgIhANZ7yABDBk4z9r+gEYtVdFPZdjkEmPa9dYVG\n1u9tUvFy\n-----END CERTIFICATE-----\n"),
        []byte("-----BEGIN CERTIFICATE-----\nMIICcDCCAhegAwIBAgIIJY/rNMZhQFwwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzU3MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNTcwHhcNMjEx\nMDI4MDgzMDI5WhcNNDExMDIzMDgzMDI5WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNTcx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNTctbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAATSdVUFd8NCn4RpFtdd9eOfdAb4pgqoCmLX\nf5p40w7uG9LgWVLcHlkxFQJLagEgLAOAt/arp5/VPlvLW734gorvo4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEII/jHW2D+EGmFFPJupqBoCubnOwYiEF30VGIZjngZIfwMCsGA1UdIwQk\nMCKAIJ6svKRzGIGG9O0Qh0xEizT54wjh7HivJPiATX5h7ZahMBkGA1UdEQQSMBCC\nDmhjLW9yZzU3LW5vZGUxMAoGCCqGSM49BAMCA0cAMEQCIDES1Cz+VE1uiwSDQYDO\noUD9O51G+a3NMnELPp3GZudUAiBe4jJKk1xPU8jIM6yG5B9q3A7CeASpbbISV0tW\nRDLVYw==\n-----END CERTIFICATE-----\n"),
        []byte("-----BEGIN CERTIFICATE-----\nMIICcjCCAhegAwIBAgIIJFu2YffFkwMwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzY1MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNjUwHhcNMjEx\nMTE1MDUzMjQ5WhcNNDExMTEwMDUzMjQ5WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNjUx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNjUtbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQX5O+LR8zMk5P0MtL5IUo8ijyRvei4znh0\nn2JtuiGlHsULwiKJ3iAnVA2C4OPESbAxXtDViIC6KCEoSa+1vhH5o4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEIGMwbIbdCt+HSL/t2+51nJNAhvH705W8/NnKCj7naO53MCsGA1UdIwQk\nMCKAIIMGhjkBj8VD2+2zAscpecisB5b7dlRdLCJwpce55MSIMBkGA1UdEQQSMBCC\nDmhjLW9yZzY1LW5vZGUxMAoGCCqGSM49BAMCA0kAMEYCIQCSA05P68pGYBCrFoTF\nsPrjx7XN7x1EdFzKb7jnqIc6SQIhAOJYgKkwQQwrXV+yqQJ9XmGRM9I3Ghdvs+Ew\nN4dM580M\n-----END CERTIFICATE-----\n"),
    }

    pidList = []peer.ID{
        "QmVGwL6VXMab7HubwkheZQpJkKKxutqGseJnjg4upWhvDo",
        "Qmav9PUgzrdaLTgJ68XzX9gb4eKLzTonMUpAEsyJRFqV63",
        "QmNQTmmZj8STcDhpKK3izd6q9DT42EuXu7oJyH7Tk1czga",
    }
)


func CreateHostTCP(idx int, seeds map[peer.ID]ma.Multiaddr) (host.Host, error) {
    certPool := cmx509.NewCertPool()
    for i := range certPEMs {
        certPool.AppendCertsFromPEM(certPEMs[i])
    }
    sk, err := asym.PrivateKeyFromPEM(keyPEMs[idx], nil)
    if err != nil {
        return nil, err
    }
    tlsCert, err := cmTls.X509KeyPair(certPEMs[idx], keyPEMs[idx])
    if err != nil {
        return nil, err
    }
    hostCfg := &nethost.HostConfig{
        TlsCfg: &cmTls.Config{
            Certificates:       []cmTls.Certificate{tlsCert},
            InsecureSkipVerify: true,
            ClientAuth:         cmTls.RequireAnyClientCert,
            VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*cmx509.Certificate) error {
                tlsCertBytes := rawCerts[0]
                cert, err := cmx509.ParseCertificate(tlsCertBytes)
                if err != nil {
                    return err
                }
                _, err = cert.Verify(cmx509.VerifyOptions{Roots: certPool})
                if err != nil {
                    return err
                }
                return nil
            },
        },
        LoadPidFunc: func(certificates []*cmx509.Certificate) (peer.ID, error) {
            pid, err := helper.GetLibp2pPeerIdFromCertDer(certificates[0].Raw)
            if err != nil {
                return "", err
            }
            return peer.ID(pid), err
        },
        SendStreamPoolInitSize:    10,
        SendStreamPoolCap:         50,
        PeerReceiveStreamMaxCount: 100,
        ListenAddresses:           []ma.Multiaddr{addrsTcp[idx]},
        DirectPeers:               seeds,
        MsgCompress:               false,
        Insecurity:                false,
        PrivateKey:                sk,
    }

    return hostCfg.NewHost(nethost.TcpNetwork, context.Background(), logger.NewLogPrinter("HOST"+strconv.Itoa(idx)))
}

func TestHostTCP() {
    // create host1 pidList[1]: ma.Join(addr2TargetTcp, ma.StringCast("/p2p/"+pidList[1].ToString()))
    host1, err := CreateHostTCP(0, map[peer.ID]ma.Multiaddr{})
    // register notifee
    notifyBundle := &host.NotifieeBundle{
        PeerConnectedFunc: func(id peer.ID) {
            fmt.Printf("節(jié)點已連接,節(jié)點ID:%s\n", id)
        },
        PeerDisconnectedFunc: func(id peer.ID) {
            fmt.Printf("節(jié)點已斷開迹冤,節(jié)點ID:%s\n", id)
        },
        PeerProtocolSupportedFunc: func(protocolID protocol.ID, pid peer.ID) {
            fmt.Printf("節(jié)點%s新支持協(xié)議%s\n", pid, protocolID)
        },
        PeerProtocolUnsupportedFunc: func(protocolID protocol.ID, pid peer.ID) {
            fmt.Printf("節(jié)點%s取消支持協(xié)議%s\n", pid, protocolID)
        },
    }
    host1.Notify(notifyBundle)
    ps := pubsub.NewChainPubSub("c1635410037173", logger.NewLogPrinter("PubSub"))
    err = ps.AttachHost(host1)
    if err != nil {
        // do something
        panic(err)
    }
    // start hosts
    err = host1.Start()

    err = host1.RegisterMsgPayloadHandler(testProtocolID, func(senderPID peer.ID, msgPayload []byte) {
        fmt.Println(string(msgPayload))
    })


    testTopic := "topic"
    go func() {
        i:=0
        for  {
            i++
            // host1 send msg to host2
            time.Sleep(time.Second)
            err = host1.SendMsg(testProtocolID, pidList[1], []byte(msg))
            err = host1.SendMsg(testProtocolID, pidList[2], []byte(msg))
            ps.Publish(testTopic,[]byte("廣播的消息"+strconv.Itoa(i)))
        }
    }()
    // 節(jié)點發(fā)現(xiàn)服務
    // 10. 開啟節(jié)點發(fā)現(xiàn)服務
    log := logger.NewLogPrinter("TEST")
    discoveryService, err := protocoldiscovery.NewProtocolBasedDiscovery(host1, protocoldiscovery.WithLogger(log))
    if err != nil {
        // do something
        panic(err)
    }
    ctx := context.Background()
    // 11. 宣布自己支持的服務
    err = discoveryService.Announce(ctx, testProtocolID)
    if err != nil {
        // do something
        panic(err)
    }

    // 12. 搜尋其他節(jié)點
    findC, err := discoveryService.FindPeers(ctx, testProtocolID)
    if err != nil {
        // do something
        panic(err)
    }
    go listenFindingC(ctx,0,host1,findC)
    select {

    }

}

func listenFindingC(ctx context.Context, idx int, h host.Host, c <-chan ma.Multiaddr) {
    for {
        select {
        case <-ctx.Done():
            return
        case ai := <-c:
            fmt.Printf("發(fā)現(xiàn)新節(jié)點讽营,地址:%s\n", ai.String())
            addr, pid := util.GetNetAddrAndPidFromNormalMultiAddr(ai)
            if pid == "" {
                fmt.Errorf("[Discovery%d] peer id not contains in addr", idx)
                continue
            }
            if h.ConnMgr().PeerCount() >= h.ConnMgr().MaxPeerCountAllowed() || h.ID() == pid || h.ConnMgr().IsConnected(pid) {
                continue
            }
            fmt.Printf("[Discovery%d] find new peer.(pid: %s, addr: %s)", idx, pid, addr.String())
            _, err := h.Dial(ai)
            if err!=nil{
                fmt.Errorf("連接出現(xiàn)錯誤")
                continue
            }

        }
    }
}

node 2

/*
Copyright (C) BABEC. All rights reserved.

SPDX-License-Identifier: Apache-2.0
*/

package main

import (
    "chainmaker.org/chainmaker/common/v2/crypto/asym"
    cmTls "chainmaker.org/chainmaker/common/v2/crypto/tls"
    cmx509 "chainmaker.org/chainmaker/common/v2/crypto/x509"
    "chainmaker.org/chainmaker/common/v2/helper"
    "chainmaker.org/chainmaker/net-liquid/core/handler"
    "chainmaker.org/chainmaker/net-liquid/core/host"
    "chainmaker.org/chainmaker/net-liquid/core/peer"
    "chainmaker.org/chainmaker/net-liquid/core/protocol"
    "chainmaker.org/chainmaker/net-liquid/core/util"
    "chainmaker.org/chainmaker/net-liquid/discovery/protocoldiscovery"
    nethost "chainmaker.org/chainmaker/net-liquid/host"
    "chainmaker.org/chainmaker/net-liquid/logger"
    "chainmaker.org/chainmaker/net-liquid/pubsub"
    "context"
    "fmt"
    ma "github.com/multiformats/go-multiaddr"
    "strconv"
)

const msg = "Hello!My first LIQUID program demo."
const testProtocolID = "/test"
var (
    addrsTcp = []ma.Multiaddr{
        ma.StringCast("/ip4/127.0.0.1/tcp/7081"),
        ma.StringCast("/ip4/127.0.0.1/tcp/7084"),
        ma.StringCast("/ip4/127.0.0.1/tcp/7085"),
    }


    keyPEMs = [][]byte{
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDc16StWEUNF0W7iXrT/k0Niy1ZWKbheI2wkjMqJ3WcaoAoGCCqGSM49\nAwEHoUQDQgAEmung19cBCguCaWeyf/nnARHRISTxtaxxf3Zqn+EQxxeYsXqa8oAg\ncalmJaGcmk9kTc0aJwpi7bKvJQdrRg1vFw==\n-----END EC PRIVATE KEY-----\n"),
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIGLbfX/EvXQtyO+GJvj5rFB0gyTqO3g4gO/gHX2A/BgNoAoGCCqGSM49\nAwEHoUQDQgAE0nVVBXfDQp+EaRbXXfXjn3QG+KYKqApi13+aeNMO7hvS4FlS3B5Z\nMRUCS2oBICwDgLf2q6ef1T5by1u9+IKK7w==\n-----END EC PRIVATE KEY-----\n"),
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIEUG3DFeXHVr5WjTRfpuSou30zEy2DMmzmyxuSd7F7K9oAoGCCqGSM49\nAwEHoUQDQgAEF+Tvi0fMzJOT9DLS+SFKPIo8kb3ouM54dJ9ibbohpR7FC8Iiid4g\nJ1QNguDjxEmwMV7Q1YiAuighKEmvtb4R+Q==\n-----END EC PRIVATE KEY-----"),
    }

    certPEMs = [][]byte{
        []byte("-----BEGIN CERTIFICATE-----\nMIICcjCCAhegAwIBAgIIFPWUuqixxuAwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzU2MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNTYwHhcNMjEx\nMDI4MDgyMzU3WhcNNDExMDIzMDgyMzU3WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNTYx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNTYtbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAASa6eDX1wEKC4JpZ7J/+ecBEdEhJPG1rHF/\ndmqf4RDHF5ixeprygCBxqWYloZyaT2RNzRonCmLtsq8lB2tGDW8Xo4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEIOTdkAJ/Fe7PiOnSFe5Fa95mreAwpX2OiK2sB3+Bd86jMCsGA1UdIwQk\nMCKAIKUCNBAgGIS+Af0ucVsMq9ZGS6S2GLlbcDyrtghnqnAnMBkGA1UdEQQSMBCC\nDmhjLW9yZzU2LW5vZGUxMAoGCCqGSM49BAMCA0kAMEYCIQDH+9IoSsBPE33ZRuog\ng9FBCAVj30uuxB08b+eUjbdDGgIhANZ7yABDBk4z9r+gEYtVdFPZdjkEmPa9dYVG\n1u9tUvFy\n-----END CERTIFICATE-----\n"),
        []byte("-----BEGIN CERTIFICATE-----\nMIICcDCCAhegAwIBAgIIJY/rNMZhQFwwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzU3MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNTcwHhcNMjEx\nMDI4MDgzMDI5WhcNNDExMDIzMDgzMDI5WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNTcx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNTctbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAATSdVUFd8NCn4RpFtdd9eOfdAb4pgqoCmLX\nf5p40w7uG9LgWVLcHlkxFQJLagEgLAOAt/arp5/VPlvLW734gorvo4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEII/jHW2D+EGmFFPJupqBoCubnOwYiEF30VGIZjngZIfwMCsGA1UdIwQk\nMCKAIJ6svKRzGIGG9O0Qh0xEizT54wjh7HivJPiATX5h7ZahMBkGA1UdEQQSMBCC\nDmhjLW9yZzU3LW5vZGUxMAoGCCqGSM49BAMCA0cAMEQCIDES1Cz+VE1uiwSDQYDO\noUD9O51G+a3NMnELPp3GZudUAiBe4jJKk1xPU8jIM6yG5B9q3A7CeASpbbISV0tW\nRDLVYw==\n-----END CERTIFICATE-----\n"),
        []byte("-----BEGIN CERTIFICATE-----\nMIICcjCCAhegAwIBAgIIJFu2YffFkwMwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzY1MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNjUwHhcNMjEx\nMTE1MDUzMjQ5WhcNNDExMTEwMDUzMjQ5WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNjUx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNjUtbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQX5O+LR8zMk5P0MtL5IUo8ijyRvei4znh0\nn2JtuiGlHsULwiKJ3iAnVA2C4OPESbAxXtDViIC6KCEoSa+1vhH5o4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEIGMwbIbdCt+HSL/t2+51nJNAhvH705W8/NnKCj7naO53MCsGA1UdIwQk\nMCKAIIMGhjkBj8VD2+2zAscpecisB5b7dlRdLCJwpce55MSIMBkGA1UdEQQSMBCC\nDmhjLW9yZzY1LW5vZGUxMAoGCCqGSM49BAMCA0kAMEYCIQCSA05P68pGYBCrFoTF\nsPrjx7XN7x1EdFzKb7jnqIc6SQIhAOJYgKkwQQwrXV+yqQJ9XmGRM9I3Ghdvs+Ew\nN4dM580M\n-----END CERTIFICATE-----\n"),
    }

    pidList = []peer.ID{
        "QmVGwL6VXMab7HubwkheZQpJkKKxutqGseJnjg4upWhvDo",
        "Qmav9PUgzrdaLTgJ68XzX9gb4eKLzTonMUpAEsyJRFqV63",
        "QmNQTmmZj8STcDhpKK3izd6q9DT42EuXu7oJyH7Tk1czga",
    }
)

func CreateHostTCP(idx int, seeds map[peer.ID]ma.Multiaddr) (host.Host, error) {
    certPool := cmx509.NewCertPool()
    for i := range certPEMs {
        certPool.AppendCertsFromPEM(certPEMs[i])
    }
    sk, err := asym.PrivateKeyFromPEM(keyPEMs[idx], nil)
    if err != nil {
        return nil, err
    }
    tlsCert, err := cmTls.X509KeyPair(certPEMs[idx], keyPEMs[idx])
    if err != nil {
        return nil, err
    }
    hostCfg := &nethost.HostConfig{
        TlsCfg: &cmTls.Config{
            Certificates:       []cmTls.Certificate{tlsCert},
            InsecureSkipVerify: true,
            ClientAuth:         cmTls.RequireAnyClientCert,
            VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*cmx509.Certificate) error {
                tlsCertBytes := rawCerts[0]
                cert, err := cmx509.ParseCertificate(tlsCertBytes)
                if err != nil {
                    return err
                }
                _, err = cert.Verify(cmx509.VerifyOptions{Roots: certPool})
                if err != nil {
                    return err
                }
                return nil
            },
        },
        LoadPidFunc: func(certificates []*cmx509.Certificate) (peer.ID, error) {
            pid, err := helper.GetLibp2pPeerIdFromCertDer(certificates[0].Raw)
            if err != nil {
                return "", err
            }
            return peer.ID(pid), err
        },
        SendStreamPoolInitSize:    10,
        SendStreamPoolCap:         50,
        PeerReceiveStreamMaxCount: 100,
        ListenAddresses:           []ma.Multiaddr{addrsTcp[idx]},
        DirectPeers:               seeds,
        MsgCompress:               false,
        Insecurity:                false,
        PrivateKey:                sk,
    }
    return hostCfg.NewHost(nethost.TcpNetwork, context.Background(), logger.NewLogPrinter("HOST"+strconv.Itoa(idx)))
}
func TestHostTCP() {
    // create host2 pidList[0]: ma.Join(addrsTcp[0], ma.StringCast("/p2p/"+pidList[0].ToString()))
    host1, _ := CreateHostTCP(1, map[peer.ID]ma.Multiaddr{pidList[0]: ma.Join(addrsTcp[0], ma.StringCast("/p2p/"+pidList[0].ToString()))})

    notifyBundle := &host.NotifieeBundle{
        PeerConnectedFunc: func(id peer.ID) {
            fmt.Printf("節(jié)點已連接,節(jié)點ID:%s\n", id)
        },
        PeerDisconnectedFunc: func(id peer.ID) {
            fmt.Printf("節(jié)點已斷開泡徙,節(jié)點ID:%s\n", id)
        },
        PeerProtocolSupportedFunc: func(protocolID protocol.ID, pid peer.ID) {
            fmt.Printf("節(jié)點%s新支持協(xié)議%s\n", pid, protocolID)
        },
        PeerProtocolUnsupportedFunc: func(protocolID protocol.ID, pid peer.ID) {
            fmt.Printf("節(jié)點%s取消支持協(xié)議%s\n", pid, protocolID)
        },
    }
    host1.Notify(notifyBundle)

    ps := pubsub.NewChainPubSub("c1635410037173", logger.NewLogPrinter("PubSub"))
    err := ps.AttachHost(host1)
    if err != nil {
        // do something
        panic(err)
    }
    testTopic := "topic"
    var subHandler handler.SubMsgHandler = func(publisher peer.ID, topic string, msg []byte) {
        fmt.Printf("收到節(jié)點%s向頻道%s發(fā)布的消息:%s\n", publisher, topic, string(msg))
    }
    ps.Subscribe(testTopic, subHandler)
    // start hosts
    _ = host1.Start()
    // wait for connection established between host1 and host2

    // register msg payload handler
    _ = host1.RegisterMsgPayloadHandler(testProtocolID, func(senderPID peer.ID, msgPayload []byte) {
        fmt.Println("收到Message"+string(msgPayload))
    })

    // 節(jié)點發(fā)現(xiàn)服務
    // 10. 開啟節(jié)點發(fā)現(xiàn)服務
    log := logger.NewLogPrinter("TEST")
    discoveryService, err := protocoldiscovery.NewProtocolBasedDiscovery(host1, protocoldiscovery.WithLogger(log))
    if err != nil {
        // do something
        panic(err)
    }
    ctx := context.Background()
    // 11. 宣布自己支持的服務
    err = discoveryService.Announce(ctx, testProtocolID)
    if err != nil {
        // do something
        panic(err)
    }

    // 12. 搜尋其他節(jié)點
    findC, err := discoveryService.FindPeers(ctx, testProtocolID)
    if err != nil {
        // do something
        panic(err)
    }
    go listenFindingC(ctx,1,host1,findC)
    select {

    }

}

func listenFindingC(ctx context.Context, idx int, h host.Host, c <-chan ma.Multiaddr) {
    for {
        select {
        case <-ctx.Done():
            return
        case ai := <-c:
            fmt.Printf("發(fā)現(xiàn)新節(jié)點橱鹏,地址:%s\n", ai.String())
            addr, pid := util.GetNetAddrAndPidFromNormalMultiAddr(ai)
            if pid == "" {
                fmt.Errorf("[Discovery%d] peer id not contains in addr", idx)
                continue
            }
            if h.ConnMgr().PeerCount() >= h.ConnMgr().MaxPeerCountAllowed() || h.ID() == pid || h.ConnMgr().IsConnected(pid) {
                continue
            }
            fmt.Printf("[Discovery%d] find new peer.(pid: %s, addr: %s)", idx, pid, addr.String())
            _, _ = h.Dial(ai)
        }
    }
}

node3

/*
Copyright (C) BABEC. All rights reserved.

SPDX-License-Identifier: Apache-2.0
*/

package main

import (
    "chainmaker.org/chainmaker/common/v2/crypto/asym"
    cmTls "chainmaker.org/chainmaker/common/v2/crypto/tls"
    cmx509 "chainmaker.org/chainmaker/common/v2/crypto/x509"
    "chainmaker.org/chainmaker/common/v2/helper"
    "chainmaker.org/chainmaker/net-liquid/core/handler"
    "chainmaker.org/chainmaker/net-liquid/core/host"
    "chainmaker.org/chainmaker/net-liquid/core/peer"
    "chainmaker.org/chainmaker/net-liquid/core/protocol"
    "chainmaker.org/chainmaker/net-liquid/core/util"
    "chainmaker.org/chainmaker/net-liquid/discovery/protocoldiscovery"
    nethost "chainmaker.org/chainmaker/net-liquid/host"
    "chainmaker.org/chainmaker/net-liquid/logger"
    "chainmaker.org/chainmaker/net-liquid/pubsub"
    "context"
    "fmt"
    ma "github.com/multiformats/go-multiaddr"
    "strconv"
)

const msg = "Hello!My first LIQUID program demo."
const testProtocolID = "/test"
var (
    addrsTcp = []ma.Multiaddr{
        ma.StringCast("/ip4/127.0.0.1/tcp/7081"),
        ma.StringCast("/ip4/127.0.0.1/tcp/7084"),
        ma.StringCast("/ip4/127.0.0.1/tcp/7085"),
    }


    keyPEMs = [][]byte{
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDc16StWEUNF0W7iXrT/k0Niy1ZWKbheI2wkjMqJ3WcaoAoGCCqGSM49\nAwEHoUQDQgAEmung19cBCguCaWeyf/nnARHRISTxtaxxf3Zqn+EQxxeYsXqa8oAg\ncalmJaGcmk9kTc0aJwpi7bKvJQdrRg1vFw==\n-----END EC PRIVATE KEY-----\n"),
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIGLbfX/EvXQtyO+GJvj5rFB0gyTqO3g4gO/gHX2A/BgNoAoGCCqGSM49\nAwEHoUQDQgAE0nVVBXfDQp+EaRbXXfXjn3QG+KYKqApi13+aeNMO7hvS4FlS3B5Z\nMRUCS2oBICwDgLf2q6ef1T5by1u9+IKK7w==\n-----END EC PRIVATE KEY-----\n"),
        []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIEUG3DFeXHVr5WjTRfpuSou30zEy2DMmzmyxuSd7F7K9oAoGCCqGSM49\nAwEHoUQDQgAEF+Tvi0fMzJOT9DLS+SFKPIo8kb3ouM54dJ9ibbohpR7FC8Iiid4g\nJ1QNguDjxEmwMV7Q1YiAuighKEmvtb4R+Q==\n-----END EC PRIVATE KEY-----"),
    }

    certPEMs = [][]byte{
        []byte("-----BEGIN CERTIFICATE-----\nMIICcjCCAhegAwIBAgIIFPWUuqixxuAwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzU2MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNTYwHhcNMjEx\nMDI4MDgyMzU3WhcNNDExMDIzMDgyMzU3WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNTYx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNTYtbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAASa6eDX1wEKC4JpZ7J/+ecBEdEhJPG1rHF/\ndmqf4RDHF5ixeprygCBxqWYloZyaT2RNzRonCmLtsq8lB2tGDW8Xo4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEIOTdkAJ/Fe7PiOnSFe5Fa95mreAwpX2OiK2sB3+Bd86jMCsGA1UdIwQk\nMCKAIKUCNBAgGIS+Af0ucVsMq9ZGS6S2GLlbcDyrtghnqnAnMBkGA1UdEQQSMBCC\nDmhjLW9yZzU2LW5vZGUxMAoGCCqGSM49BAMCA0kAMEYCIQDH+9IoSsBPE33ZRuog\ng9FBCAVj30uuxB08b+eUjbdDGgIhANZ7yABDBk4z9r+gEYtVdFPZdjkEmPa9dYVG\n1u9tUvFy\n-----END CERTIFICATE-----\n"),
        []byte("-----BEGIN CERTIFICATE-----\nMIICcDCCAhegAwIBAgIIJY/rNMZhQFwwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzU3MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNTcwHhcNMjEx\nMDI4MDgzMDI5WhcNNDExMDIzMDgzMDI5WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNTcx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNTctbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAATSdVUFd8NCn4RpFtdd9eOfdAb4pgqoCmLX\nf5p40w7uG9LgWVLcHlkxFQJLagEgLAOAt/arp5/VPlvLW734gorvo4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEII/jHW2D+EGmFFPJupqBoCubnOwYiEF30VGIZjngZIfwMCsGA1UdIwQk\nMCKAIJ6svKRzGIGG9O0Qh0xEizT54wjh7HivJPiATX5h7ZahMBkGA1UdEQQSMBCC\nDmhjLW9yZzU3LW5vZGUxMAoGCCqGSM49BAMCA0cAMEQCIDES1Cz+VE1uiwSDQYDO\noUD9O51G+a3NMnELPp3GZudUAiBe4jJKk1xPU8jIM6yG5B9q3A7CeASpbbISV0tW\nRDLVYw==\n-----END CERTIFICATE-----\n"),
        []byte("-----BEGIN CERTIFICATE-----\nMIICcjCCAhegAwIBAgIIJFu2YffFkwMwCgYIKoZIzj0EAwIwZDELMAkGA1UEBhMC\nQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAOBgNVBAcTB0JlaWppbmcxETAPBgNVBAoT\nCGhjLW9yZzY1MQswCQYDVQQLEwJjYTERMA8GA1UEAxMIaGMtb3JnNjUwHhcNMjEx\nMTE1MDUzMjQ5WhcNNDExMTEwMDUzMjQ5WjBxMQswCQYDVQQGEwJDTjEQMA4GA1UE\nCBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzERMA8GA1UEChMIaGMtb3JnNjUx\nEjAQBgNVBAsTCWNvbnNlbnN1czEXMBUGA1UEAxMOaGMtb3JnNjUtbm9kZTEwWTAT\nBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQX5O+LR8zMk5P0MtL5IUo8ijyRvei4znh0\nn2JtuiGlHsULwiKJ3iAnVA2C4OPESbAxXtDViIC6KCEoSa+1vhH5o4GlMIGiMA4G\nA1UdDwEB/wQEAwID+DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwKQYD\nVR0OBCIEIGMwbIbdCt+HSL/t2+51nJNAhvH705W8/NnKCj7naO53MCsGA1UdIwQk\nMCKAIIMGhjkBj8VD2+2zAscpecisB5b7dlRdLCJwpce55MSIMBkGA1UdEQQSMBCC\nDmhjLW9yZzY1LW5vZGUxMAoGCCqGSM49BAMCA0kAMEYCIQCSA05P68pGYBCrFoTF\nsPrjx7XN7x1EdFzKb7jnqIc6SQIhAOJYgKkwQQwrXV+yqQJ9XmGRM9I3Ghdvs+Ew\nN4dM580M\n-----END CERTIFICATE-----\n"),
    }

    pidList = []peer.ID{
        "QmVGwL6VXMab7HubwkheZQpJkKKxutqGseJnjg4upWhvDo",
        "Qmav9PUgzrdaLTgJ68XzX9gb4eKLzTonMUpAEsyJRFqV63",
        "QmNQTmmZj8STcDhpKK3izd6q9DT42EuXu7oJyH7Tk1czga",
    }
)

func CreateHostTCP(idx int, seeds map[peer.ID]ma.Multiaddr) (host.Host, error) {
    certPool := cmx509.NewCertPool()
    for i := range certPEMs {
        certPool.AppendCertsFromPEM(certPEMs[i])
    }
    sk, err := asym.PrivateKeyFromPEM(keyPEMs[idx], nil)
    if err != nil {
        return nil, err
    }
    tlsCert, err := cmTls.X509KeyPair(certPEMs[idx], keyPEMs[idx])
    if err != nil {
        return nil, err
    }
    hostCfg := &nethost.HostConfig{
        TlsCfg: &cmTls.Config{
            Certificates:       []cmTls.Certificate{tlsCert},
            InsecureSkipVerify: true,
            ClientAuth:         cmTls.RequireAnyClientCert,
            VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*cmx509.Certificate) error {
                tlsCertBytes := rawCerts[0]
                cert, err := cmx509.ParseCertificate(tlsCertBytes)
                if err != nil {
                    return err
                }
                _, err = cert.Verify(cmx509.VerifyOptions{Roots: certPool})
                if err != nil {
                    return err
                }
                return nil
            },
        },
        LoadPidFunc: func(certificates []*cmx509.Certificate) (peer.ID, error) {
            pid, err := helper.GetLibp2pPeerIdFromCertDer(certificates[0].Raw)
            if err != nil {
                return "", err
            }
            return peer.ID(pid), err
        },
        SendStreamPoolInitSize:    10,
        SendStreamPoolCap:         50,
        PeerReceiveStreamMaxCount: 100,
        ListenAddresses:           []ma.Multiaddr{addrsTcp[idx]},
        DirectPeers:               seeds,
        MsgCompress:               false,
        Insecurity:                false,
        PrivateKey:                sk,
    }
    return hostCfg.NewHost(nethost.TcpNetwork, context.Background(), logger.NewLogPrinter("HOST"+strconv.Itoa(idx)))
}
func TestHostTCP() {
    // create host2 pidList[0]: ma.Join(addrsTcp[0], ma.StringCast("/p2p/"+pidList[0].ToString()))
    host1, _ := CreateHostTCP(2, map[peer.ID]ma.Multiaddr{pidList[1]: ma.Join(addrsTcp[1], ma.StringCast("/p2p/"+pidList[1].ToString()))})

    notifyBundle := &host.NotifieeBundle{
        PeerConnectedFunc: func(id peer.ID) {
            fmt.Printf("節(jié)點已連接,節(jié)點ID:%s\n", id)
        },
        PeerDisconnectedFunc: func(id peer.ID) {
            fmt.Printf("節(jié)點已斷開堪藐,節(jié)點ID:%s\n", id)
        },
        PeerProtocolSupportedFunc: func(protocolID protocol.ID, pid peer.ID) {
            fmt.Printf("節(jié)點%s新支持協(xié)議%s\n", pid, protocolID)
        },
        PeerProtocolUnsupportedFunc: func(protocolID protocol.ID, pid peer.ID) {
            fmt.Printf("節(jié)點%s取消支持協(xié)議%s\n", pid, protocolID)
        },
    }
    host1.Notify(notifyBundle)

    ps := pubsub.NewChainPubSub("c1635410037173", logger.NewLogPrinter("PubSub"))
    err := ps.AttachHost(host1)
    if err != nil {
        // do something
        panic(err)
    }
    testTopic := "topic"
    var subHandler handler.SubMsgHandler = func(publisher peer.ID, topic string, msg []byte) {
        fmt.Printf("收到節(jié)點%s向頻道%s發(fā)布的消息:%s\n", publisher, topic, string(msg))
    }
    ps.Subscribe(testTopic, subHandler)
    // start hosts
    _ = host1.Start()
    // wait for connection established between host1 and host2

    // register msg payload handler
    _ = host1.RegisterMsgPayloadHandler(testProtocolID, func(senderPID peer.ID, msgPayload []byte) {
        fmt.Println("收到Message"+string(msgPayload))
    })

    // 節(jié)點發(fā)現(xiàn)服務
    // 10. 開啟節(jié)點發(fā)現(xiàn)服務
    log := logger.NewLogPrinter("TEST")
    discoveryService, err := protocoldiscovery.NewProtocolBasedDiscovery(host1, protocoldiscovery.WithLogger(log))
    if err != nil {
        // do something
        panic(err)
    }
    ctx := context.Background()
    // 11. 宣布自己支持的服務
    err = discoveryService.Announce(ctx, testProtocolID)
    if err != nil {
        // do something
        panic(err)
    }

    // 12. 搜尋其他節(jié)點
    findC, err := discoveryService.FindPeers(ctx, testProtocolID)
    if err != nil {
        // do something
        panic(err)
    }
    go listenFindingC(ctx,2,host1,findC)
    select {

    }

}

func listenFindingC(ctx context.Context, idx int, h host.Host, c <-chan ma.Multiaddr) {
    for {
        select {
        case <-ctx.Done():
            return
        case ai := <-c:
            fmt.Printf("發(fā)現(xiàn)新節(jié)點莉兰,地址:%s\n", ai.String())
            addr, pid := util.GetNetAddrAndPidFromNormalMultiAddr(ai)
            if pid == "" {
                fmt.Errorf("[Discovery%d] peer id not contains in addr", idx)
                continue
            }
            if h.ConnMgr().PeerCount() >= h.ConnMgr().MaxPeerCountAllowed() || h.ID() == pid || h.ConnMgr().IsConnected(pid) {
                continue
            }
            fmt.Printf("[Discovery%d] find new peer.(pid: %s, addr: %s)", idx, pid, addr.String())
            _, _ = h.Dial(ai)
        }
    }
}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市礁竞,隨后出現(xiàn)的幾起案子糖荒,更是在濱河造成了極大的恐慌,老刑警劉巖模捂,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捶朵,死亡現(xiàn)場離奇詭異,居然都是意外死亡枫绅,警方通過查閱死者的電腦和手機泉孩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來并淋,“玉大人寓搬,你說我怎么就攤上這事∠氐ⅲ” “怎么了句喷?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵镣典,是天一觀的道長。 經(jīng)常有香客問我唾琼,道長兄春,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任锡溯,我火速辦了婚禮赶舆,結果婚禮上,老公的妹妹穿的比我還像新娘祭饭。我一直安慰自己芜茵,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布倡蝙。 她就那樣靜靜地躺著九串,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寺鸥。 梳的紋絲不亂的頭發(fā)上猪钮,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音胆建,去河邊找鬼烤低。 笑死,一個胖子當著我的面吹牛眼坏,可吹牛的內(nèi)容都是我干的拂玻。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼宰译,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了魄懂?” 一聲冷哼從身側響起沿侈,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎市栗,沒想到半個月后缀拭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡填帽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年蛛淋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片篡腌。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡褐荷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嘹悼,到底是詐尸還是另有隱情叛甫,我是刑警寧澤层宫,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站其监,受9級特大地震影響萌腿,放射性物質發(fā)生泄漏。R本人自食惡果不足惜抖苦,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一毁菱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锌历,春花似錦贮庞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怔揩,卻和暖如春捉邢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背商膊。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工伏伐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晕拆。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓藐翎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親实幕。 傳聞我的和親對象是個殘疾皇子吝镣,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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