2. ETH SPV

2.1. 什么是SPV

轻钱包(SPV)是“Simplified Payment Verification”(简单支付验证)的缩写。中本聪论文简要地提及了这一概念,指出:不运行完全节点也可验证支付,用户只需要保存所有的block header就可以了。用户虽然不能自己验证交易,但如果能够从区块链的某处找到相符的交易,他就可以知道网络已经认可了这笔交易,而且得到了网络的多少个确认。

2.1.1. ETH Hash 算法

  • DAG 分层级的有向无环图 ,每3万个块生成一次,

    • 每个 epoch 的seed 是存在依赖的,

      Seed Hash[0] = Kec(32 byte 0)
      Seed Hash[i] = Kec(32 byte i-1)
      
    • Seed 生成 cache, 初始值是 16MB,每个 epoch 增长 128K B, 每个 cache 的单位是 64B

    • cache 生成 Data,初始值 是 1GB,每个 epoch 增长 8M。

2.2. ETH 数据结构

2.2.1. Block

以太坊中的一个区块由区块头Header,以及交易列表 ,以及叔块的 header 集合三部分组成。

具体结构如下图

blockchain-structure

2.2.2. Block Header

// Header represents a block header in the Ethereum blockchain.
type Header struct {
	ParentHash  common.Hash    `json:"parentHash"       gencodec:"required"`
	UncleHash   common.Hash    `json:"sha3Uncles"       gencodec:"required"`
	Coinbase    common.Address `json:"miner"            gencodec:"required"`
	Root        common.Hash    `json:"stateRoot"        gencodec:"required"`
	TxHash      common.Hash    `json:"transactionsRoot" gencodec:"required"`
	ReceiptHash common.Hash    `json:"receiptsRoot"     gencodec:"required"`
	Bloom       Bloom          `json:"logsBloom"        gencodec:"required"`
	Difficulty  *big.Int       `json:"difficulty"       gencodec:"required"`
	Number      *big.Int       `json:"number"           gencodec:"required"`
	GasLimit    uint64         `json:"gasLimit"         gencodec:"required"`
	GasUsed     uint64         `json:"gasUsed"          gencodec:"required"`
	Time        uint64         `json:"timestamp"        gencodec:"required"`
	Extra       []byte         `json:"extraData"        gencodec:"required"`
	MixDigest   common.Hash    `json:"mixHash"`
	Nonce       BlockNonce     `json:"nonce"`
}

Header包括以下字段。

  • ParentHashHp 表示。

    • 父节点的hash值, 每个区块有且只有一个父区块, 但允许某个时候存在多个子块(分叉)。

  • UncleHashHo 表示。

    • Block结构体的成员 uncles 进行 RLP 编码后的 Hash 值,这块是跟GHOST相关的,

  • CoinbaseHc 表示。 矿工address

  • stateRootHr 表示。

    • StateDB中的 state Trie 的根节点的RLP哈希值。Block中,每个账户以stateObject对象表示,账户以Address为唯一标示,其信息在相关交易Transaction的执行中被修改。所有账户对象可以逐个插入一个Merkle-PatricaTrie(MPT)结构里,形成 state Trie

  • transactionsRootHt 表示。

    • Blocktx Trie 的根节点的RLP哈希值。Block的成员变量transactions中所有的tx对象,被逐个插入一个MPT结构,形成 tx Trie

  • receiptsRootHe表示。

  • Block中的 Receipt Trie 的根节点的RLP哈希值。Block的所有Transaction执行完后会生成一个Receipt数组,这个数组中的所有Receipt被逐个插入一个MPT结构中,形成 Receipt Trie

  • logsBloomHb 表示。

    • Bloom过滤器(Filter),用来快速判断一个参数Log对象是否存在于一组已知的Log集合中

  • difficultyHd表示。

    • 区块的难度。BlockDifficulty由共识算法基于parentBlockTimeDifficulty计算得出

  • number Hi 表示。 区块号

  • gasLimitHl 表示。区块的gas数量限制,即区块中交易使用掉的gas值不应该超过该值。

  • gasUsedHg 表示。区块使用掉的gas数量,

  • timestampHs 表示。时间戳,

  • extraDataHx 表示。额外的数据,合法的交易对长度有限制,

  • mixHashHm 表示。与nonce一起用作工作量证明,

  • nonceHn 表示。PoW 中使用的随机数 与mixHash一起用作工作量证明,

2.2.3. 区块头校验

  • 校验区块号和区块hash的符号化表示

  • 难度计算的符号化表示

  • 当区块编号为0的时候其难度值是固定好的,在这里用D0表示,其值为131072.

    • 对于其他区块,其难度值需要根据其父区块难度值以及一些其他因素,出块的间隔时间,区块编号等有关进行调节的,若小于D0,则难度值调整为D0。

  • 调节系数 the adjustment factor x 的定义。

    • 难度系数(diculty parameterς2的定义。该系数主要与出块间隔时间有关,当间隔大的时候,系数变大,难度也会相应变大,当间隔小的时候,系数变小,难度也会变小。使得区块链在整体上出块时间是趋于稳定的。其中y值根据父节点的uncle节点是否为空而有所区别,可以看出当父节点的uncle不为空的时候,y值为2,说明当前的分叉程度较大,适当调大难度,一定程度上会减少分叉。

  • gasLimit限制的符号化表示

    gaslimit

    • 区块gasLimit必须大于等于5000,和上个区块的gasLimit差值不超过 上个块 gasLimit 除以 1024

  • 时间戳的符号化表示

    • 当前区块的时间戳必须大于父区块的时间戳

  • mixHash和nonce相关符号化表示

  • 总体而言:区块头验证的符号化表示 如下

    gaslimit

2.3. 为什么用SPV

以太坊区块的存储的信息主要包含两部分内容:Header和Body. Body主要由交易列表和Uncle Block构成;Header的内容更为丰富,主要有父区块的hash,时间戳,挖矿的难度等. 而Header中最重要的是以太坊的是"三棵树",对应于区块头中的StateRoot,TransactionRoot和ReceiptRoot三个哈希值

light client 只会同步 ETH 的 Block header data 。 由于没有具体的交易信息,只有 Block header data 无法验证某一个交易是否被确认,所以只能通过 Merkle 证明来判断一笔交易是否在现在的区块链交易列表中。

用户在验证一笔交易的时候就不需要知道其他交易的数据,只需要其他交易的交易哈希(Txhash),然后计算这笔交易的hash值,再逐步计算出Merkle Root并与接收到的Block Header的Merkel Root对比就可以验证交易是否存在了(需要用户提供交易的信息: 区块块高,交易的index)

lght client 无法识别最长链(规范链)

2.4. 如何维护一个 light client

  • 计算 receiptsRoot 提供 tx 的校验

  • 计算 header data 的校验

2.5. Reference

轻节点SPV

go-ethereum 源码分析 - 区块结构(1)

以太坊黄皮书详解(一)