Geth搭建以太坊多节点私有链并开启挖矿部署智能合约

Geth是用Go语言编写的与以太坊网络交互的客户端。

安装参考:https://geth.ethereum.org/downloads/

Geth命令参考文章:

以下部署的前提条件是已安装Geth客户端。

1、初始化创世区块

搭建以太坊网络需要从一个创世区块开始,创建一个名为genesis.json的文件,文件内容如下:

{
  "config": {
        "chainId": 666,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x1",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00",
  "alloc"      : {}
}

参数解析:

参数名称 解释
config 对私有链的配置信息,详见:https://github.com/ethereum/go-ethereum/blob/feeccdf4ec1084b38dac112ff4f86809efd7c0e5/params/config.go#L71
config.chainId 链的id,不同id的链不能互相通信,在 geth 命令中的 --networkid 参数需要与 chainId 的值一致
config.homesteadBlock Homestead 硬分叉区块高度,不需要关注填写0即可
config.eip155Block EIP 155硬分叉高度,不需要关注
config.eip158Block EIP 158硬分叉高度,不需要关注
coinbase 矿工账号
difficulty 设置挖矿难度,这里设置较小难度
extraData 附件信息,可以填写个性信息
gasLimit 对gas消耗总量限制,用来限制区块能包含的交易信息的总和
nonce 一个64位的随机数,用于挖矿
mixhash 与nonce配合挖矿,由上一个区块的一部分生成的hash。注意他和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。
parentHash 上一个区块的hash,因为是创世区块,所以为0
timestamp 创世区块的时间戳
alloc 用来预置账号以及账号的以太币数量,因为是私有链挖矿比较容易,可以不用预设值有币的账户,需要时自己创建即可

2、初始化创世区块

用带init命令

geth --datadir /Users/****/ehtdata/ init 

参数解释:

--datadir 指定区块数据存放目录

运行成功后如下图所示:

image.png

3、启动创世区块

使用Geth的console命令,进入命令行交互模式,并且将日志输出到eth.log文件中,退出交互模式使用exit命令。

geth --datadir /Users/****/ehtdata/ \
--networkid 666 \
--identity "bootnode" \
--port 30303 \
--rpc \
--rpcport 8545 \
--rpccorsdomain "*" \
--nodiscover \
--verbosity 4 \
console 2>> eth.log

参数说明:

参数名称 解释
--networkid 区块网络id,和genesis.json中的chainId一致
--identity 节点id
--port geth节点端口号,默认就是30303
--rpc 启用rpc服务,默认端口号8545
--rpcport rpc服务端口号
--rpccorsdomain rpc域名,这里配置的是通配符*,允许任意域名访问,也可以指定具体的域名,如“http://sixdays.top
--nodiscover 关闭节点自动发现
--verbosity 日志级别 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 3)

启动成功如下图所示:
[图片上传中...(image.png-854f76-1557751444373-0)]

我们可以重新打开一个shell,使用命令

tail -f eth.log

实时查看区块的日志,如下图所示:


image.png

创建成功后,可以使用命令:

eth.getBlock(0)

查看我们刚才配置的创世区块内容,如下图所示:


image.png

至此,我们的私有链已经创建成功了,接下来,我们就在这个私有链上做一些挖矿、部署合约等操作。

Geth的console实际上是一个JavaScript的运行环境,我们可以在这个环境下运行以太坊的RPC和web3的方法。

JSON-RPC文档:https://github.com/ethereum/wiki/wiki/JSON-RPC

JavaScript-API文档:https://github.com/ethereum/wiki/wiki/JavaScript-API

除此之外,Geth提供了一些内置方法:https://github.com/ethereum/go-ethereum/wiki/Management-APIs

image.png

4、创建账号

使用Geth的内置命令,输入账号密码,创建一个新账号

>personal.newAccount() //创建账号
Passphrase: 
Repeat passphrase: 
"0xfa8d4ded7fe1fec96c1b10443bea261195f233bb"

> personal.listAccounts //查看已有账号,也可使用eth.accounts
["0xfa8d4ded7fe1fec96c1b10443bea261195f233bb"]

> eth.getBalance("0xfa8d4ded7fe1fec96c1b10443bea261195f233bb") //查看账号里的余额
0

5、挖矿,赚取eth

> miner.start() //开启挖矿
null

> eth.getBalance("0xfa8d4ded7fe1fec96c1b10443bea261195f233bb") //过一段时间后查看余额
65000000000000000000

> eth.blockNumber //查看区块高度
118

转账测试

> personal.newAccount("linjing1") //创建一个新账号
"0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53"

> eth.accounts
["0xfa8d4ded7fe1fec96c1b10443bea261195f233bb", "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53"]

> personal.unlockAccount("0xfa8d4ded7fe1fec96c1b10443bea261195f233bb","linjing1") //解锁账号
true

> eth.getBalance(eth.accounts[1]) //账号1(0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53)的余额目前为0
0
> eth.sendTransaction({from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb", to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53" gas: "0x76c0", value: "0x9184e72a"}) //转账
"0xed1a157b759e00d9a767b0849f81f5e6a6cf0187dcfb43a5307e51eb436b3e92" //返回的是交易hash

> eth.getTransaction("0xed1a157b759e00d9a767b0849f81f5e6a6cf0187dcfb43a5307e51eb436b3e92") //查看交易信息
{
  blockHash: "0x93d566d17a4b9d252a18cfd2a4c10fdca164c87f883799052ed229d9c6397c7a",
  blockNumber: 661,
  from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb",
  gas: 30400,
  gasPrice: 1000000000,
  hash: "0xed1a157b759e00d9a767b0849f81f5e6a6cf0187dcfb43a5307e51eb436b3e92",
  input: "0x",
  nonce: 0,
  r: "0xade3453541928fe6e67b5137f98f81ddb975100a79e0639b954b66420bd711e5",
  s: "0x4f6e9b2aa27114bbe6f4e609426d188315d18a090bdfe3901694eda3664c201d",
  to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53",
  transactionIndex: 0,
  v: "0x557",
  value: 2441406250
}

> web3.fromWei(eth.getBalance(eth.accounts[1]),"ether") //将单位转化为以太坊
2.44140625e-9

6、创建新节点并加入网络

区块链是一种p2p的网络模型,到目前为止,我们搭建的是一个单机版的私有链。现在我们新建一个节点,并接入到我们的网络中。新节点创建需要注意:

  • 新节点的networkid要保持一致,这里我用的是666
  • 新节点使用同一个创世区块
  • 如果是在同一个机器上创建多个节点,注意端口号区分
$geth --datadir /Users/linjingjing/Documents/project/node2 init genesis.json 
INFO [05-13|17:53:53.368] Maximum peer count                       ETH=25 LES=0 total=25
INFO [05-13|17:53:53.387] Allocated cache and file handles         database=/Users/linjingjing/Documents/project/node2/geth/chaindata cache=16 handles=16
INFO [05-13|17:53:53.389] Writing custom genesis block 
INFO [05-13|17:53:53.390] Persisted trie from memory database      nodes=0 size=0.00B time=12.925µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [05-13|17:53:53.390] Successfully wrote genesis state         database=chaindata                                                 hash=c626d8…b99ce5
INFO [05-13|17:53:53.391] Allocated cache and file handles         database=/Users/linjingjing/Documents/project/node2/geth/lightchaindata cache=16 handles=16
INFO [05-13|17:53:53.394] Writing custom genesis block 
INFO [05-13|17:53:53.394] Persisted trie from memory database      nodes=0 size=0.00B time=2.492µs  gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [05-13|17:53:53.394] Successfully wrote genesis state         database=lightchaindata    

$geth --datadir /Users/linjingjing/Documents/project/node2 \
--port 30304 \
--networkid 666 \
--identity "node2" \
--rpc \
--rpcport 9545 \
--rpccorsdomain "*" \
--nodiscover \
--verbosity 4 \
console 2>> node2.log

接下来,让两个节点建立联系,有三种方法

方法一,使用admin.addPeer()

1、在第一个节点中使用admin.nodeInfo.enode命令查看enode信息,如下所示:

> admin.nodeInfo.enode
"enode://2c4eb7a267b1c00a8550e0237277234ca4bf8076b48268693e689f0657d6eb40eb88ee3f4d619b2361471c72881918a300a0de2943f6a29bd2a7c78e7a820d85@127.0.0.1:30303?discport=0"

2、在新建节点中使用admin.addPeer命令添加联系,如下所示:

admin.addPeer("enode://2c4eb7a267b1c00a8550e0237277234ca4bf8076b48268693e689f0657d6eb40eb88ee3f4d619b2361471c72881918a300a0de2943f6a29bd2a7c78e7a820d85@127.0.0.1:30303?discport=0")
true

3、在两个节点中分别使用admin.peers验证
节点一

> admin.peers
[{
    caps: ["eth/63"],
    enode: "enode://bc44cdf9ebc14a15a052d6c62717a2503f493f673303d193da7e2951d466b9758dbac840bbc08f823a765bb5ea8324e5c3706d3a6473b3c392eda56e55ab7555@127.0.0.1:61413",
    id: "c833b932f0c7329be2d3b15fffced1b0fa7c5f2913d1b2879285021a0f176e3d",
    name: "Geth/node2/v1.8.27-stable-4bcc0a37/darwin-amd64/go1.11.9",
    network: {
      inbound: true,
      localAddress: "127.0.0.1:30303",
      remoteAddress: "127.0.0.1:61413",
      static: false,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 1,
        head: "0xc626d8be5d39286154d44074a2f0a31192792385e89d6f1f60dea77be6b99ce5",
        version: 63
      }
    }
}]

节点二

> admin.peers
[{
    caps: ["eth/63"],
    enode: "enode://2c4eb7a267b1c00a8550e0237277234ca4bf8076b48268693e689f0657d6eb40eb88ee3f4d619b2361471c72881918a300a0de2943f6a29bd2a7c78e7a820d85@127.0.0.1:30303?discport=0",
    id: "4e299133607ac04682880cd06663a82d72c34c1271df06878df739597b759658",
    name: "Geth/bootnode/v1.8.27-stable-4bcc0a37/darwin-amd64/go1.11.9",
    network: {
      inbound: false,
      localAddress: "127.0.0.1:61413",
      remoteAddress: "127.0.0.1:30303",
      static: true,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 155767989,
        head: "0xc758c0e5e2e07c43d71b6513412a6e1bfcb8e3faae3ecdd08e7b53e3ac3a0884",
        version: 63
      }
    }
}]

方法二,使用 static-nodes.json 文件

在--datadir目录中添加static-nodes.json文件,让节点取得联系,文件内容如下所示:

[
    "enode://2c4eb7a267b1c00a8550e0237277234ca4bf8076b48268693e689f0657d6eb40eb88ee3f4d619b2361471c72881918a300a0de2943f6a29bd2a7c78e7a820d85@127.0.0.1:30303?discport=0"
]

方法三,使用--bootnodes参数指定enode

$geth --datadir /Users/linjingjing/Documents/project/node2 \
--port 30304 \
--networkid 666 \
--identity "node2" \
--rpc \
--rpcport 9545 \
--rpccorsdomain "*" \
--nodiscover \
--verbosity 4 \
--bootnodes ["enode://2c4eb7a267b1c00a8550e0237277234ca4bf8076b48268693e689f0657d6eb40eb88ee3f4d619b2361471c72881918a300a0de2943f6a29bd2a7c78e7a820d85@127.0.0.1:30303?discport=0"] \
console 2>> node2.log

7、部署智能合约

写了一个简单的合约

pragma solidity >=0.5.1;

contract Test {
    
    uint256 public A = 0;
    
    function set (uint256 a) public {
        A = a;
    }
    
    function get() view public returns(uint256) {
        return A;
    }
    
    function sum(uint256 b) view public returns(uint256) {
        return A + b;
    }
}

将合约使用remix(http://remix.ethereum.org/)进行编译,然后拷贝ABI和Bytecode

image.png

ABI

[
    {
        "constant": true,
        "inputs": [
            {
                "name": "b",
                "type": "uint256"
            }
        ],
        "name": "sum",
        "outputs": [
            {
                "name": "",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": false,
        "inputs": [
            {
                "name": "a",
                "type": "uint256"
            }
        ],
        "name": "set",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "get",
        "outputs": [
            {
                "name": "",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "A",
        "outputs": [
            {
                "name": "",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    }
]

在bejson中转义成字符串

http://www.bejson.com/jsonviewernew/

[{\"constant\":true,\"inputs\":[{\"name\":\"b\",\"type\":\"uint256\"}],\"name\":\"sum\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"a\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"A\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]

字节码Bytecode

60806040526000805534801561001457600080fd5b50610194806100246000396000f3fe60806040526004361061005c576000357c010000000000000000000000000000000000000000000000000000000090048063188b85b41461006157806360fe47b1146100b05780636d4ce63c146100eb578063f446c1d014610116575b600080fd5b34801561006d57600080fd5b5061009a6004803603602081101561008457600080fd5b8101908080359060200190929190505050610141565b6040518082815260200191505060405180910390f35b3480156100bc57600080fd5b506100e9600480360360208110156100d357600080fd5b810190808035906020019092919050505061014f565b005b3480156100f757600080fd5b50610100610159565b6040518082815260200191505060405180910390f35b34801561012257600080fd5b5061012b610162565b6040518082815260200191505060405180910390f35b600081600054019050919050565b8060008190555050565b60008054905090565b6000548156fea165627a7a723058203033498bf3661ba9be9645b58fc484ca960635e7cabc7879bda415d4e13094170029

准备好了合约,接下来就是部署了

1、定义bytecode和abi


> var bytecode = "0x60806040526000805534801561001457600080fd5b50610194806100246000396000f3fe60806040526004361061005c576000357c010000000000000000000000000000000000000000000000000000000090048063188b85b41461006157806360fe47b1146100b05780636d4ce63c146100eb578063f446c1d014610116575b600080fd5b34801561006d57600080fd5b5061009a6004803603602081101561008457600080fd5b8101908080359060200190929190505050610141565b6040518082815260200191505060405180910390f35b3480156100bc57600080fd5b506100e9600480360360208110156100d357600080fd5b810190808035906020019092919050505061014f565b005b3480156100f757600080fd5b50610100610159565b6040518082815260200191505060405180910390f35b34801561012257600080fd5b5061012b610162565b6040518082815260200191505060405180910390f35b600081600054019050919050565b8060008190555050565b60008054905090565b6000548156fea165627a7a723058203033498bf3661ba9be9645b58fc484ca960635e7cabc7879bda415d4e13094170029"

> var abi = JSON.parse('[{\"constant\":true,\"inputs\":[{\"name\":\"b\",\"type\":\"uint256\"}],\"name\":\"sum\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"a\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"A\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]')
undefined

2、创建合约对象


> test = web3.eth.contract(abi)
{
  abi: [{
      constant: true,
      inputs: [{...}],
      name: "sum",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "set",
      outputs: [],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "get",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "A",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }],
  eth: {
    accounts: ["0xd6b8bbf4232cfb5d70e6c5a1a37d3250a2a190a9"],
    blockNumber: 1049,
    coinbase: "0xd6b8bbf4232cfb5d70e6c5a1a37d3250a2a190a9",
    compile: {
      lll: function(),
      serpent: function(),
      solidity: function()
    },
    defaultAccount: undefined,
    defaultBlock: "latest",
    gasPrice: 1000000000,
    hashrate: 0,
    mining: false,
    pendingTransactions: [],
    protocolVersion: "0x3f",
    syncing: false,
    call: function(),
    chainId: function(),
    contract: function(abi),
    estimateGas: function(),
    filter: function(options, callback, filterCreationErrorCallback),
    getAccounts: function(callback),
    getBalance: function(),
    getBlock: function(),
    getBlockNumber: function(callback),
    getBlockTransactionCount: function(),
    getBlockUncleCount: function(),
    getCode: function(),
    getCoinbase: function(callback),
    getCompilers: function(),
    getGasPrice: function(callback),
    getHashrate: function(callback),
    getMining: function(callback),
    getPendingTransactions: function(callback),
    getProof: function(),
    getProtocolVersion: function(callback),
    getRawTransaction: function(),
    getRawTransactionFromBlock: function(),
    getStorageAt: function(),
    getSyncing: function(callback),
    getTransaction: function(),
    getTransactionCount: function(),
    getTransactionFromBlock: function(),
    getTransactionReceipt: function(),
    getUncle: function(),
    getWork: function(),
    iban: function(iban),
    icapNamereg: function(),
    isSyncing: function(callback),
    namereg: function(),
    resend: function(),
    sendIBANTransaction: function(),
    sendRawTransaction: function(),
    sendTransaction: function(),
    sign: function(),
    signTransaction: function(),
    submitTransaction: function(),
    submitWork: function()
  },
  at: function(address, callback),
  getData: function(),
  new: function()
}

3、预估gas费用

> web3.eth.estimateGas({data: bytecode}) //预估gas
164951

4、解锁账号


> eth.accounts
["0xd6b8bbf4232cfb5d70e6c5a1a37d3250a2a190a9"]

> personal.unlockAccount("0xd6b8bbf4232cfb5d70e6c5a1a37d3250a2a190a9") //解锁账号
Unlock account 0xd6b8bbf4232cfb5d70e6c5a1a37d3250a2a190a9
Passphrase: //输入密码
true

5、部署合约

> contractInstance = test.new({data: bytecode, gas: 1000000, from: "0xd6b8bbf4232cfb5d70e6c5a1a37d3250a2a190a9"}, function(e, contract){ 
      if(!e){ 
           if(!contract.address){ 
                console.log("Contract transaction send: Transaction Hash: "+contract.transactionHash+" waiting to be mined..."); 
     }else{ 
       console.log("Contract mined! Address: "+contract.address); 
       console.log(contract); 
     } 
   }else{ 
     console.log(e) 
   } 
 })
 
Contract transaction send: Transaction Hash: 0x2e5850086b9ebc12336ade65943c6685613db6c885fa4a06ff00f168ad8a2206 waiting to be mined...
{
  abi: [{
      constant: true,
      inputs: [{...}],
      name: "sum",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "set",
      outputs: [],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "get",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "A",
      outputs: [{...}],
      payable: false,
      stateMutability: "view",
      type: "function"
  }],
  address: undefined,
  transactionHash: "0x2e5850086b9ebc12336ade65943c6685613db6c885fa4a06ff00f168ad8a2206"
}

6、开启挖矿并验证部署

> miner.start() //开启挖矿
null

> Contract mined! Address: 0x3d21428a20aa5c51e7ab1dfc52c89c2c337924e6
[object Object]

> eth.getCode('0x3d21428a20aa5c51e7ab1dfc52c89c2c337924e6') //查看是否部署成功
"0x60806040526004361061005c576000357c010000000000000000000000000000000000000000000000000000000090048063188b85b41461006157806360fe47b1146100b05780636d4ce63c146100eb578063f446c1d014610116575b600080fd5b34801561006d57600080fd5b5061009a6004803603602081101561008457600080fd5b8101908080359060200190929190505050610141565b6040518082815260200191505060405180910390f35b3480156100bc57600080fd5b506100e9600480360360208110156100d357600080fd5b810190808035906020019092919050505061014f565b005b3480156100f757600080fd5b50610100610159565b6040518082815260200191505060405180910390f35b34801561012257600080fd5b5061012b610162565b6040518082815260200191505060405180910390f35b600081600054019050919050565b8060008190555050565b60008054905090565b6000548156fea165627a7a723058203033498bf3661ba9be9645b58fc484ca960635e7cabc7879bda415d4e13094170029"

7、调用合约方法

> contractInstance.get()
0

> contractInstance.set.sendTransaction(9, {from:"0xd6b8bbf4232cfb5d70e6c5a1a37d3250a2a190a9", gas:1000000})
"0x6a2b0d2a5d4175519610e681853e823aa401a0295c10b4be7fb7e2358f8f59d0"

> contractInstance.get()
9

> contractInstance.sum(666)
675

当然,除了使用Geth客户端部署和执行合约外,也可以自己在remix上直接进行部署。

在remix上Environment选择“web3 Provider”,然后填写本地的开启的RPC服务。如下图所示:

image.png

此时remix已与我们本地搭建的私有链联通,直接在remix上开发调试以及部署即可。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容