# 区块

区块是一种包含在公共账簿(区块链)里的聚合了交易信息的容器数据结构。它由一个包含元数据的区块头和紧跟其后的构成区块主体的一长串交易组成。每一个区块都指向前一个区块,所有的区块从后向前链接在一起共同组成了一条区块链。 对每一个区块进行SHA256加密哈希,可生成一个哈希值。可以通过该哈希值在区块链上找到唯一的区块。每一个区块头中都包含一个父区块的哈希,但可以暂时拥有多个子区块,这种情况被称为"区块链分叉"。区块链分叉只是暂时存在,只有当多个不同区块被不同的旷工几乎同时发现时才会发生。最终,只有一个子区块会成为区块链的一部分。因此,在用区块高度来指定某个区块时,同一个高度可能存在不止一个区块。 由于区块头中包含"父区块哈希值"字段,所以当父区块哈希值发生变化时,当前区块的哈希值也会发生变化,当前区块的哈希值发生变化又将导致其后区块的哈希值发生变化,以此类推。这种多米诺骨牌效应将保证该区块不会改变,除非强制重新计算该区块所有后续的区块。

# Blcock

区块数据结构

区块的结构如下表所示,一个区块主要包含区块头以及构成区块主体的所有交易信息。区块主体的大小远远大于区块头的大小。

字段 类型 描述
BlockHeader *BlockHeader 组成区块头的几个字段
ID Hash 区块体中交易hash
Transactions []*Tx 区块体中的所有交易信息

# BlockHeader

区块头数据结构

区块头结构如下表所示:

字段 类型 描述
Version uint64 版本号,用于跟踪软件/协议的更新
Height uint64 区块的高度
PreviousBlockId *Hash 引用区块链中父区块的哈希值
Timestamp uint64 该区块产生的时间
TransactionsRoot *Hash 该区块中交易根节点哈希值
TransactionStatusHash *Hash 该区块中交易状态的Merkle树哈希值
Nonce uint64 用于工作量证明算法的计数器
Bits uint64 该区块工作量证明算法的难度目标

** 区块ID的意义和来历**

区块哈希值是通过SHA256算法对区块头进行二次哈希计算而得到的数字指纹。区块哈希值可以唯一地标识一个区块,并且任何节点通过简单地对区块头进行哈希计算都可以独立地获取该区块的哈希值。区块的哈希值并不包含在当前区块的数据结构里,但它会被下一个区块作为"父区块哈希"引用。

区块的验证

当新区块在网络中传播时,每一个节点在将它转发到其他节点之前,会进行一系列的测试去验证它,这确保了只有有效的区块会在网络中传播。一个节点接收到一个新的区块时会按照一个标准清单会该区块进行验证,若没有通过验证,这个区块将被拒绝。它包括:

  • 区块的版本是否大于等于父区块的版本
  • 区块的高度是否等于父区块的高度加1
  • 区块的"父区块哈希"字段是否等于父区块的哈希
  • 区块的时间戳不得超过当前时间未来一个小时且不得小于最近11个区块的中间时间戳
  • 难度目标是否符合要求
  • 工作量证明是否达到给定的难度目标
  • coinbase的金额是否符合当前高度
  • 计算得到的交易的Merkle树根节点哈希值是否与区块头中的一致
  • 计算得到的交易状态的Merkle树哈希值是否与区块头中的一致
  • 对区块体中每一笔交易进行验证

两个默克尔根的意义

交易树默克尔根TransactionsRoot,防止区块里的交易被篡改,同时可以快速定位交易是否都存在于区块中。 交易状态树默克尔根TransactionStatusHash,防止保存在区块所有交易的验证结果状态被篡改,同时可以快速定位交易状态是否存在于区块中。

难度

矿工找到下一个有效区块的难度。该参数并不存储在区块链上,而是由Bits计算得出。

# 区块链header验证逻辑

区块头验证是保证区块链不产生分叉的重要手段。如果没有区块头验证,则在同步区块的过程中节点间会产生较多的分叉。分叉会对链上资金和财产安全造成极大的威胁。

一般的会在以下四种情况对区块进行验证:

  • 挖矿节点在成功挖到一个区块并向链上提交区块时,节点会验证区块是否合法。
  • 用户通过API接口向节点提交区块到区块链时,节点会验证区块是否合法。
  • 同步区块时,节点收到其他节点同步过来的区块,节点会先验证同步的区块是否合法。如果合法则将其加入到本地链。
  • 矿池中的节点向矿池提交工作时,矿池会验证矿机提交的区块。

bytom的简单快速验证方法如下:

 // ProcessBlock is the entry for handle block insert
 func (c *Chain) processBlock(block *types.Block) (bool, error) {
    blockHash := block.Hash()
    if c.BlockExist(&blockHash) {
       log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Info("block has been processed")
       return c.orphanManage.BlockExist(&blockHash), nil
    }

 if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil {
    c.orphanManage.Add(block)
    return true, nil
 }

 if err := c.saveBlock(block); err != nil {
    return false, err
 }

 bestBlock := c.saveSubBlock(block)
 bestBlockHash := bestBlock.Hash()
 bestNode := c.index.GetNode(&bestBlockHash)

 if bestNode.Parent == c.bestNode {
    log.WithFields(log.Fields{"module": logModule}).Debug("append block to the end of mainchain")
    return false, c.connectBlock(bestBlock)
 }

 if bestNode.Height > c.bestNode.Height && bestNode.WorkSum.Cmp(c.bestNode.WorkSum) >= 0 {
    log.WithFields(log.Fields{"module": logModule}).Debug("start to reorganize chain")
    return false, c.reorganizeChain(bestNode)
 }
 return false, nil
 }

区块头验证主要包扩以下6部分:

  • 当前的区块版本号要与父区块的版本号相同。
  • 当前区块高度只能比父区块高度大1.
  • 当前区块的难度目标要等于根据父区块高度计算出的下一个区块的难度目标。
  • 当前区块的父区块的哈希值要等于父区块的哈希值。
  • 当前区块的时间戳不能比当前时间延后一小时,并且不能早于之前11个区块时间戳的中位数
  • 验证区块的哈希值是否等于给定的难度目标。

# Bytom 创世区块数据

区块链中的第一个区块称为创世区块,它是区块链里面所有区块的共同祖先。一般情况下创世区块被硬编码到代码核心中,每一个节点都始于同一个创世区块,这能确保创世区块不会被修改。每个节点都把创世区块作为区块链的首区块,从而构建了一个安全可信的区块链。

通过命令行工具可以获取创世区块的信息。

生成创世区块

当启动一个节点的时候,节点会先尝试从本地的LevelDB数据库中加载区块信息。如果节点没有从LevelDB中获取到区块,则节点会认为自己是一个全新的节点。因此,节点会根据硬编码的创世区块信息初始化本地链。

硬编码的创世区块信息如下:

 func mainNetGenesisBlock() *types.Block {
   tx := GenesisTx()
   txStatus := bc.NewTransactionStatus()
   if err := txStatus.SetStatus(0, false); err != nil {
     log.Panicf(err.Error())
   }
   txStatusHash, err := types.TxStatusMerkleRoot(txStatus.VerifyStatus)
   if err != nil {
      log.Panicf("fail on calc genesis tx status merkle root")
   }

   merkleRoot, err := types.TxMerkleRoot([]*bc.Tx{tx.Tx})
   if err != nil {
      log.Panicf("fail on calc genesis tx merkel root")
   }

   block := &types.Block{
     BlockHeader: types.BlockHeader{
        Version:   1,
        Height:    0,
        Nonce:     9253507043297,
        Timestamp: 1524549600,
        Bits:      2161727821137910632,
        BlockCommitment: types.BlockCommitment{
            TransactionsMerkleRoot: merkleRoot,
            TransactionStatusHash:  txStatusHash,
        },
     },
     Transactions: []*types.Tx{tx},
  }
  return block
 }
上次更新: 3/30/2020, 11:49:24 AM