2018-01-28整理

ETH数据结构

// block块数据
    type Block struct {
        header       *Header
        uncles       []*Header
        transactions Transactions

        // caches
        hash atomic.Value
        size atomic.Value

        // Td is used by package core to store the total difficulty
        // of the chain up to and including the block.
        td *big.Int

        // These fields are used by package eth to track
        // inter-peer block relay.
        ReceivedAt   time.Time
        ReceivedFrom interface{}
    }
    
// header数据
    type Header struct {
        ParentHash  common.Hash    `json:"parentHash"       gencodec:"required"` // 指向父区块的指针。
        UncleHash   common.Hash    `json:"sha3Uncles"       gencodec:"required"` // Block结构体的成员uncles的RLP哈希值。
        Coinbase    common.Address `json:"miner"            gencodec:"required"` // 挖掘出这个区块的作者地址。
        Root        common.Hash    `json:"stateRoot"        gencodec:"required"` // StateDB中的“state Trie”的根节点的RLP哈希值。
        TxHash      common.Hash    `json:"transactionsRoot" gencodec:"required"` // Block中 “tx Trie”的根节点的RLP哈希值。
        ReceiptHash common.Hash    `json:"receiptsRoot"     gencodec:"required"` // Block中的 "Receipt Trie”的根节点的RLP哈希值。
        Bloom       Bloom          `json:"logsBloom"        gencodec:"required"` // Bloom过滤器(Filter),用来快速判断一个参数Log对象是否存在于一组已知的Log集合中。
        Difficulty  *big.Int       `json:"difficulty"       gencodec:"required"` // 区块的难度。
        Number      *big.Int       `json:"number"           gencodec:"required"` // 区块的序号。
        GasLimit    uint64         `json:"gasLimit"         gencodec:"required"` // 区块内所有Gas消耗的理论上限。
        GasUsed     uint64         `json:"gasUsed"          gencodec:"required"` // 区块内所有Transaction执行时所实际消耗的Gas总和。
        Time        *big.Int       `json:"timestamp"        gencodec:"required"` // 区块时间戳,要么等于parentBlock.Time + 10s,要么等于当前系统时间。
        Extra       []byte         `json:"extraData"        gencodec:"required"` // ?
        MixDigest   common.Hash    `json:"mixHash"          gencodec:"required"` // ?
        Nonce       BlockNonce     `json:"nonce"            gencodec:"required"` // 挖矿随机数,一个64bit的哈希数。
    }
    
// Transaction数据
    type Transaction struct {
        data txdata  //交易明细
        // caches
        hash atomic.Value // 交易hash
        size atomic.Value // ?
        from atomic.Value // from地址
    }
    type txdata struct {
        AccountNonce uint64          `json:"nonce"    gencodec:"required"`
        Price        *big.Int        `json:"gasPrice" gencodec:"required"`
        GasLimit     uint64          `json:"gas"      gencodec:"required"`
        Recipient    *common.Address `json:"to"       rlp:"nil"` // nil means contract creation
        Amount       *big.Int        `json:"value"    gencodec:"required"`
        Payload      []byte          `json:"input"    gencodec:"required"`

        // Signature values
        V *big.Int `json:"v" gencodec:"required"`
        R *big.Int `json:"r" gencodec:"required"`
        S *big.Int `json:"s" gencodec:"required"`

        // This is only used when marshaling to JSON.
        Hash *common.Hash `json:"hash" rlp:"-"`
    }

// 块数据示例
{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "difficulty": "0x7b56814e8e0",
        "extraData": "0xd783010203844765746887676f312e342e32856c696e7578",
        "gasLimit": "0x2fefd8",
        "gasUsed": "0x37c5e",
        "hash": "0x3d3ebb11286a91fdc00cfb6ffa4f4a73e3e528ce13467bb2203532dc9cb42a8f",
        "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000400000000000000000000040000000000000",
        "miner": "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5",
        "mixHash": "0x40384589c970bff24a4d5d87692cfd948c89cb7e5381ad28d31071e85c7f22b6",
        "nonce": "0xda9cbaa29db514a5",
        "number": "0xaae61",
        "parentHash": "0x6c621dc6d89cecd48004bd6bd6f3098a18f61aa140b912f0db583198cb69558e",
        "receiptsRoot": "0x852ede9b7d114119ecf6f6221f9f6ed059bb25e098ffc6ae89f451186d44c056",
        "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
        "size": "0x572",
        "stateRoot": "0x3b65505100e6fdfa2649764e63a7a980aebfd93726690fe0239e384ede4e3980",
        "timestamp": "0x567137a7",
        "totalDifficulty": "0x3b49b0afd6d0597c",
        "transactionsRoot": "0xb42c31f189406cc77674b5e6f7a7b7d63dd895f875befb3351fb117f37580d38",
        "uncles": [],
        "transactions": [
            {
                "blockHash": "0x3d3ebb11286a91fdc00cfb6ffa4f4a73e3e528ce13467bb2203532dc9cb42a8f",
                "blockNumber": "0xaae61",
                "from": "0xcba4c3cc041b09d3cbf416b73633cb6bd21c88f8",
                "gas": "0x3d090",
                "gasPrice": "0xc1b710800",
                "hash": "0x8c1dd51958fc97a95a24acce4cafba517337138545b57a84cdef3e970bbdf919",
                "input": "0x651e723c000000000000000000000000f0b019bb52f59680845b670453ffd875ae3a59e400000000000000000000000000000000000000000000000000000000567137b900000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002961caac0fcae0a7f51e4131ef6a243df537d11696bb1a27bac34e37a7c198b9b7ca5c71e62ed55fbca1516ad12305849bfb7421cc199d40dd08f8bb8bd0b57dc0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000f0b019bb52f59680845b670453ffd875ae3a59e400000000000000000000000059cc8539f5515335f1340662ce569fa988b40938000000000000000000000000761566074a8e0f7063044abfb8c80ca4cd8c5fd3000000000000000000000000d4fcbd984baebcc12f70f9a151c5c5708d825a7f",
                "nonce": "0x35",
                "to": "0x7de5aba7de728950c92c57d08e20d4077161f12f",
                "transactionIndex": "0x0",
                "value": "0x1",
                "v": "0x1b",
                "r": "0x378066c58af812dc57dc3d090cd6717338d1e51bc1d6fbc308e293ebb69089b",
                "s": "0x6208bfb9f67145ce1ac7fadb65682f3a2ea43380e80d07f6d0eab8e5fc5df926"
            },
            {
                "blockHash": "0x3d3ebb11286a91fdc00cfb6ffa4f4a73e3e528ce13467bb2203532dc9cb42a8f",
                "blockNumber": "0xaae61",
                "from": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069",
                "gas": "0x15f90",
                "gasPrice": "0xba43b7400",
                "hash": "0x5408d12bde03366c4a4b9ad484341bd1c69f86c58ef27a792d1fff1ceda6a6be",
                "input": "0x",
                "nonce": "0x7e55",
                "to": "0xec33b995e319527958760affd24f61fee029e4f9",
                "transactionIndex": "0x1",
                "value": "0x15c98d913a1bc00",
                "v": "0x1c",
                "r": "0xd308fb2c73d6f72545d0f7f5b9ac393f28834bb1f4c64c625cfb71f6ff0fd9f4",
                "s": "0x20ca14e9481c8c8a97ed2d761f3692e05fb3d7dc262176203670be6d6f934573"
            },
            {
                "blockHash": "0x3d3ebb11286a91fdc00cfb6ffa4f4a73e3e528ce13467bb2203532dc9cb42a8f",
                "blockNumber": "0xaae61",
                "from": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
                "gas": "0x15f90",
                "gasPrice": "0xba43b7400",
                "hash": "0x5d8cb5dfd613171daf733f65baefe727060bef89e705a4a7ac46cda1ffb53744",
                "input": "0x",
                "nonce": "0xf4ae",
                "to": "0x7fdf07a0f977e4032c1f082df831423abf58d13d",
                "transactionIndex": "0x2",
                "value": "0xf6ceba93384a000",
                "v": "0x1b",
                "r": "0xa24aefa55e9e47915a0b67bbac1b4cc0a823574077cb60a0b2f75f05eec758e8",
                "s": "0x1b2b89d5a963003283b81c5da14e58b52a611827db1066f0992d36b2fd88a4d"
            }
        ]
    }
} 

database_util.go 说明

    // 初始化前缀信息
    headHeaderKey = []byte("LastHeader")  // 最新区块头 Headchain中使用
    headBlockKey  = []byte("LastBlock")   // 最新区块   BlockChain中使用
    headFastKey   = []byte("LastFast")   //  最新快速同步的区块头 这里“LastFast”所存储的是在一种特别的同步方式FastSync下,最新Block的canonical hash。FastSync相比于FullSync,可以仅仅同步Header而不考虑Body,故此得名Fast。

    // Data item prefixes (use single byte to avoid mixing data types, avoid `i`). ETH定义好的区块头前缀标识,key的前缀可以区分leveldb中存储的是什么类型的数据。
    headerPrefix        = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header   区块头key前缀
    tdSuffix            = []byte("t") // headerPrefix + num (uint64 big endian) + hash + tdSuffix -> td  区块难度
    numSuffix           = []byte("n") // headerPrefix + num (uint64 big endian) + numSuffix -> hash  区块高度
    blockHashPrefix     = []byte("H") // blockHashPrefix + hash -> num (uint64 big endian) 区块hash前缀
    bodyPrefix          = []byte("b") // bodyPrefix + num (uint64 big endian) + hash -> block body 区块体前缀
    blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts 区块交易收据
    lookupPrefix        = []byte("l") // lookupPrefix + hash -> transaction/receipt lookup metadata
    bloomBitsPrefix     = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits

    preimagePrefix = "secure-key-"              // preimagePrefix + hash -> preimage
    configPrefix   = []byte("ethereum-config-") // config prefix for the db

    // Chain index prefixes (use `i` + single byte to avoid mixing data types).
    BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress

    // used by old db, now only used for conversion
    oldReceiptsPrefix = []byte("receipts-")
    oldTxMetaSuffix   = []byte{0x01}

    ErrChainConfigNotFound = errors.New("ChainConfig not found") // general config not found error

    preimageCounter    = metrics.NewCounter("db/preimage/total")
    preimageHitCounter = metrics.NewCounter("db/preimage/hits")
  • 方法归类整理
 数据库操作封装
1.块hash -> 块头
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header

a.WriteHeader()
b.GetHeaderRLP() 
c.GetHeader() 
d.DeleteHeader()


2.块hash -> 总难度
tdSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + tdSuffix -> td

a.WriteTd()
b.GetTd()
c.DeleteTd()


3.块高 -> 块hash
numSuffix  = []byte("n") // headerPrefix + num (uint64 big endian) + numSuffix -> hash  

canonical hash:区块hash,Block(或Header)对象的RLP哈希值。
num(Number)和hash是Block最为重要的两个属性:num用来确定Block在整个区块链中所处的位置,hash用来辨识惟一的Block/Header对象。

a.encodeBlockNumber(number uint64) //将区块高度转化为ig endian uint64类型,大端格式
b.GetCanonicalHash()  
c.WriteCanonicalHash() 
d.DeleteCanonicalHash()


4.块hash -> 块高
blockHashPrefix = []byte("H") // blockHashPrefix + hash -> num (uint64 big endian)


5.块hash -> body数据
bodyPrefix = []byte("b") // bodyPrefix + num (uint64 big endian) + hash -> block body

a.WriteBodyRLP()
b.GetBodyRLP()
c.WriteBody()
d.GetBody() 
e.DeleteBody()


6.块hash -> 交易收据
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts

a.WriteBlockReceipts()
b.GetBlockReceipts()
c.DeleteBlockReceipts()


7.块hash -> 交易收据lookup metadata
lookupPrefix = []byte("l") // lookupPrefix + hash -> transaction/receipt lookup metadata

a.WriteTxLookupEntries()
b.GetTxLookupEntry()
c.DeleteTxLookupEntry()


8.块高 -> bloom bits
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit(uint16 big endian) + section(uint64 big endian) + hash -> bloom bits

a.WriteBloomBits() 
b.GetBloomBits()


9.没看懂
preimagePrefix = "secure-key-"  // preimagePrefix + hash -> preimage(原像)

WritePreimages()


10.eth配置数据库信息
configPrefix = []byte("ethereum-config-") // config prefix for the db

a.WriteChainConfig()
b.GetChainConfig()


11.没看懂
BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress 索引进度跟踪


12.//used by old db, now only used for conversion 转换
oldReceiptsPrefix = []byte("receipts-")
oldTxMetaSuffix   = []byte{0x01}


对外封装方法:
1.初始化数据
  missingNumber  // 如果数据库中没有获取到块高数据,则使用该缺省块高。
  TxLookupEntry  //Lookup实体

2.块高
  GetBlockNumber()  //返回missingNumber/块高
  
3.块hash
  WriteHeadHeaderHash() 
  GetHeadHeaderHash() //返回当前块头的hash

  WriteHeadBlockHash()
  GetHeadBlockHash() //返回当前块头的hash

  WriteHeadFastBlockHash()
  GetHeadFastBlockHash() //返回当前块头的hash

4.区块操作
  WriteBlock()
  GetBlock()
  DeleteBlock()

5.区块头操作
  FindCommonAncestor() // 获取两个区块头的共同父类区块头信息?
  
6.交易信息
  GetTransaction()
  
7.交易收据信息
  GetReceipt()
  
8.区块链操作
  WriteBlockChainVersion()
  GetBlockChainVersion()

9.公用方法
  headerKey()  //公用headerKey生成
  blockBodyKey() //公用bodyKey生成

数据库体系

image

LDBDatabase

属于Ethereum代码范围内的最底层数据库是ethdb.LDBDatabase,它通过持有一个levelDB的对象,最终为Ethereum世界里所有需要存储/读取[k,b]的需求提供服务。

ethDB.png
  • interface.go
    统一接口封装,包含数据库的单/批操作方法(putter,get,has,delete,close,batch)
  • database.go
    a.NewLDBDatabase()--初始化LevelDB实例,初始化数据库数据,文件缓存容量+块缓存容量+写缓存等
    b.Put()--保存数据,将数据放入levelDB存储队列
    c.Has()--校验key值在LevelDB中是否存在
    d.Get()--查询key对应的value值
    e.Delete()--删除key对应的数据
    f.NewIterator()--创建LevelDB数据遍历器
    g.Close()--数据库关闭操作
    h.Meter()--初始化特殊请求前缀,并启动监听程序
    i.NewBatch()--初始化批处理实例,后续提供了相关Put/ValueSize/Write等方法
    j.table/tableBatch--按照前缀操作单/批量数据的实例,后续提供了相关Put/Has/Get/Delete/Close等方法
  • database_test.go
    基于database.go对外提供的测试接口
  • memory_database.go
    使用内存变量模拟数据库单/批的并行操作

Trie

图中多次出现一种类似的设计模式,比如trie.Trie持有一个本地接口trie.<<Database>>,而后者的具体实现是ethdb.LDBDatabase。这种设计模式其实是golang的语法带来的。在golang中,一个结构体(类)要实现另一个接口的所有方法,不必在结构体声明时显式继承那个接口,只要完全实现那些方法。这样,当一个结构体想调用另一个包路径下结构体的多个方法时,可以声明一个本地接口,带有几个同想要调用方法完全一样的方法,就可以了,这种方式的优点是不同包之间的代码更充分的解耦合。所以在上图中,这些辅助性的本地接口全都被标为灰色,只需要关注实际调用的实现类就好了。

  • 源码整理
    a.Trie-LevelDB
      实例:
           Database:DatabaseReader/DatabaseWriter
      方法: 
           DatabaseReader:Get/Has
           DatabaseWriter:Put
           
    b.Trie     
       实例:
          Trie
            -- root node  始终作为整个MPT的根节点
            -- db Database  Trie-LevelDB中数据库操作实例
            -- originalRoot common.Hash 创建Trie对象时承接入参hashNode
            -- cachegen, cachelimit uint16    cacheGen是cache次数的计数器,每次Trie的变动提交后(写入的对象可由外部参数传入),cacheGen自增1
       方法: 
            New  根据数据库数据创建根节点数据,
            NodeIterator  创建节点迭代器
            Get  根据key值返回Trie中的value,不报错
            TryGet   根据key值返回Trie中的value,报错(MissingNodeError)
            Update/TryUpdate   根据key更新(insert)value,如果value长度为0,则从Trie中删除并返回nil。
            Delete/TryDelete
            Commit/CommitTo    将trie记录提交至数据库

State

系统设计中,在底层数据库模块和业务模型之间,往往需要设置本地存储模块,它面向业务模型,可以根据业务需求灵活的设计各种存储格式和单元,同时又连接底层数据库,如果底层数据库(或者第三方API)有变动,可以大大减少对业务模块的影响。在Ethereum世界里,StateDB就担任这个角色,它通过大量的stateObject对象集合,管理所有“账户”信息。

面向业务的存储模块 - StateDB

StateDB有一个trie.Trie类型成员trie,它又被称为storage trie或stte trie,这个MPT结构中存储的都是stateObject对象,每个stateObject对象以其地址(20 bytes)作为插入节点的Key;每次在一个区块的交易开始执行前,trie由一个哈希值(hashNode)恢复出来。另外还有一个map结构,也是存放stateObject,每个stateObject的地址作为map的key。那么问题来了,这些数据结构之间是怎样的关系呢?

image

如上图所示,每当一个stateObject有改动,亦即“账户”信息有变动时,这个stateObject对象会更新,并且这个stateObject会标为dirty,此时所有的数据改动还仅仅存储在map里。当IntermediateRoot()调用时,所有标为dirty的stateObject才会被一起写入trie。而整个trie中的内容只有在CommitTo()调用时被一起提交到底层数据库。可见,这个map被用作本地的一级缓存,trie是二级缓存,底层数据库是第三级,各级数据结构的界限非常清晰,这样逐级缓存数据,每一级数据向上一级提交的时机也根据业务需求做了合理的选择。

StateDB中账户状态的版本管理

StateDB还可以管理账户状态的版本。这个功能用到了几个结构体:journal,revision,先来看看UML关系图:

image

其中journal对象是journalEntry的散列,长度不固定,可任意添加元素。接口journalEntry存在若干种实现体,描述了从单个账户操作(账户余额,发起合约次数等),到account trie变化(创建新账户对象,账户消亡)等各种最小事件。revision结构体,用来描述一个‘版本’,它的两个整型成员jd和journalIndex,都是基于journal散列进行操作的。

image

上图简述了StateDB中账户状态的版本是如何管理的。首先journal散列会随着系统运行不断的增长,记录所有发生过的单位事件;当某个时刻需要产生一个账户状态版本时,代码中相应的是Snapshop()调用,会产生一个新revision对象,记录下当前journal散列的长度,和一个自增1的版本号。

基于以上的设计,当发生回退要求时,只要根据相应的revision中的journalIndex,在journal散列上,根据所记录的所有journalEntry,即可使所有账户回退到那个状态。

Ethereum里的账户 - stateObject

每个stateObject对象管理着Ethereum世界里的一个“账户”。stateObject有一个成员变量data,类型是Accunt结构体,里面存有账户Ether余额,合约发起次数,最新发起合约指令集的哈希值,以及一个MPT结构的顶点哈希值。

stateObject内部也有一个Trie类型的成员trie,被称为storage trie,它里面存放的是一种被称为State的数据。State跟每个账户相关,格式是[Hash, Hash]键值对。有意思的是,stateObject内部也有类似StateDB一样的二级数据缓存机制,用来缓存和更新这些State。

image

stateObject定义了一种类型名为storage的map结构,用来存放[]Hash,Hash]类型的数据对,也就是State数据。当SetState()调用发生时,storage内部State数据被更新,相应标示为"dirty"。之后,待有需要时(比如updateRoot()调用),那些标为"dirty"的State数据被一起写入storage trie,而storage trie中的所有内容在CommitTo()调用时再一起提交到底层数据库。

cachingDB

看代码封装了一些对合约进行操作的方法,如ContractCode/ContractCodeSize,具体需要后续研究。

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

推荐阅读更多精彩内容

  • 简介 不管你们知不知道以太坊(Ethereum blockchain)是什么,但是你们大概都听说过以太坊。最近在新...
    Lilymoana阅读 3,891评论 1 22
  • 译者序 本文最初是我应以太坊中文社区(Ethfans.org)之邀做的翻译稿,原文取自以太坊社区的 shardin...
    风静縠纹平阅读 6,543评论 0 6
  • 前言 《最小可行性区块链设计系列》的第六讲(http://www.jianshu.com/p/a5d0cde549...
    大鱼Whale阅读 952评论 0 1
  • 文/长今 敲不出的字 是梗在心里的话 停不下的雨 是落在回忆的泪 我们不过普通朋友 不过树上两片叶子 要随风飘 怎...
    我是长今阅读 186评论 0 0
  • 曹操再奸,都有知心朋友,刘备再好,都有死对头,孙权再温柔,两边都是仇。不要太在乎别人对你的评价,做好自己的事,走好...
    天使思雅阅读 244评论 0 0