汪晓明对区块链、以太坊的思考

记录创业、生活的所思所感,探讨去中心化思想,推动区块链的发展。

HPB39:RLP机制分析

目录

1 RLP 定义

2 RLP 编码规则

3 RLP 编码实例

4 RLP 分析

1 RLP 定义

RLP,即 Recursive Length Prefix, 递归长度前缀编码,是以太坊数据序列化的主要方法, 具有较好的数据处理效率,尤其是将长度和类型统一作为前缀,实际上 RLP 是基于 ASCII 编码的一种结构化扩充,既能表示长度还能表示类型,是一种非常紧凑的结构化编码方案。 该编码方案用于编码任意的嵌套结构的二进制数据,区块、交易等数据结构在持久化时会先 经过 RLP 编码后再存储到数据库中。RLP 的唯一目标就是解决结构体的编码问题;对原子 数据类型(比如,字符串,整数型,浮点型)的编码则交给更高层的协议;以太坊中要求数 字必须是一个大端字节序的、没有零占位的存储的格式(也就是说,一个整数 0 和一个空数 组是等同的)。如果要编码一个字典,推荐使用两种规范的编码格式——一是通过 key 的字 典序来组织字典[[k1,v1],[k2,v2]……],另一种是以太坊中使用的高层的 Patricia Tree。

RLP 编码的定义只处理两类数据:

一类是字符串(例如字节数组),即一串二进制数据(strings);

一类是列表。即一个嵌套递归的结构,里面可以包含字符串和列表。

例如:[“cat”,[“puppy”,“cow”],“horse”,[[]],“pig”,[“”],“sheep”]

其他类型的数据需要转成以上的两类,转换的规则不是 RLP 编码定义的,可以根据自己的 规则转换,例如 struct 可以转成列表,int 可以转成二进制(属于字符串一类),以太坊中整 数都以大端形式存储。

从 RLP 编码的名字可以看出它的特点:

一是递归,被编码的数据是递归的结构,编码算法也是递归进行处理的;

二是长度前缀,即 RLP 编码都带有一个前缀,这个前缀是跟被编码数据的长度相关的。

2 RLP 编码规则

规则一:单字节值在[0x00,0x7f]之间的,编码就是自身。需要注意的是 0x7f 这个边界,因为 ASCII 编码最大值就是 0x7f(即二进制 1111,1111),也就是说在 0x7f 以内完全当做 ASCII 编码使用。

规则二:如果一个 string 长度在 0-55 之间,编码结果的为在 string 开头加一个字节,这个字 节的值是 0x80 加上 string 的长度。由于被编码的字符串最大长度是 55=0x37,因此单字节前 缀的最大值是 0x80+0x37=0xb7,即编码的第一个字节的取值范围是[0x80, 0xb7]。 (128—183)

(0x80+[string 的长度]) || string

规则三:如果一个 string 长度超过了 55 个字节,编码结果的第 1 个字节为 0xb7+string 的长度 值(字节表示)的长度,后跟着string的长度,后跟着string。所以第一个字节的范围为[0xb8,0xbf]。

(0xb7+string 长度值的长度) || (string 的长度) || string

比如某个 string 长度为 1024(0x0400),0x0400 的长度为 2,因此第 1 个字节为前缀应该是 0xb7+2=0xb9,后面跟着0x0400,再后面跟着string,即整个RLP编码应该是\xb9\x0400\string。 编码的第一个字节即前缀的取值范围是[0xb8, 0xbf],因为字符串长度二进制形式最少是 1个字节,因此最小值是 0xb7+1=0xb8,字符串长度二进制最大是 8 个字节,因此最大值是 0xb7+8=0xbf。(184—191)

规则四:如果一个数组中所有元素的长度之和在 0-55 之间,编码结果的第 1 个字节为 0xc0+ 所有元素的长度,后面跟着列表中各元素的编码串,因此第 1 个字节的范围在[0xc0,0xf7]。 [192—247]

(0xc0+列表元素总长度) || 列表各元素的编码串

规则五:如果数组中所有元素的长度超过 55 个字节,编码结果的第 1 个字节为 0xf7 加上所 有元素长度值(字节表示)的长度,后跟所有元素长度,后面跟着数组。第 1 个字节的范围是 [0xf8,0xff]。 [248—255]

(0xf7+所有元素长度值的长度) || 列表元素总长度 || 列表各元素的编码串

3 RLP 编码实例

字符串 “dog” = [0x83, ’d’, ‘o’, ‘g’ ] (规则二)

列表 [“cat”,“dog”] = [0xc8, 0x83, ‘c’, ‘a’, ’t’, 0x83, ’d’, ‘o’, ‘g’ ] (规则四)

空字符串 “” = 0x80 (规则二)

空列表 [] = [0xc0] (规则四)

整数 15(‘\x0f’) = 0x0f (规则一)

整数 1024(‘\x04\00’) = [0x82, 0x04, 0x00] (规则二)

列表 [ [], [[]], [ [], [[]] ] ] = [0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0] (规则四)

字符串 “Lorem ipsum dolor sit amet, consectetur adipisicing elit” = [0xb8, 0x38, ‘L’, ‘o’, ‘r’, ‘e’, ’m’, ‘ ’, … , ‘e’, ‘l’, ‘i’, ’t’](规则三)

4 RLP 分析

RLP 编码的设计思想,就是通过首字节快速判断一串编码的类型,充分利用了一个字 节的存储空间,将 0x7f 以后的值赋予了新的含义。相比于 Unicode 的对指定长度字节进行 编码,处理这些编码时一般按照指定长度进行拆分解码,最大的弊端是传统编码无法表现一 个结构,即列表。RLP 最大的优点是在充分利用字节的情况下,同时支持列表结构,也就 是说可以很轻易的利用 RLP 存储一个树状结构。

程序处理 RLP 编码时也非常容易,根据首字节就可以判断出这段编码的类型,同时调用不 同的方法进行解码,类似于 json 结构, RLP 支持嵌套的结构,通过递归调用可以将整个 RLP 快速还原成一颗树,或者转译成一个 json 结构,便于其他程序使用。

RLP 使用首字节存储长度的位数,再用后续的字节表明整体字符串的长度,根据规则二计 算,RLP 可以支持的单个最大字符串长度为 2 的 64 次方,再加上嵌套规则,理论上 RLP 可 以编码任何数据。

本文由HPB(芯链)团队整理。

HPB38:网络服务分析

目录

1 网络分层 ………………………………………………………………………………..4

2 会话层…………………………………………………………………………………….4

2.1 Peer 介绍……………………………………………………………………………..5

2.2 Peer 管理 …………………………………………………………………………….5

2.2.1 Peer 动态添加删除流程 ………………………………………………………5

2.2.2 Peer 握手机制…………………………………………………………………….6

3 表示层:RLP 编码 ……………………………………………………………………..6

4 应用层:Eth 协议……………………………………………………………………..6

1 网络分层

以太坊所有网络功能如下图所示: 所有网络功能建立在以太网的传输层之上,TCP 及 UDP 均有应用。

2 会话层

会话层主要包括 Peer 管理,NodeTable 管理和 RPC 协议,本文着重介绍 Peer 管理, NodeTable 请参考《P2P 网络及邻居节点发现机制》。 涉及到会话层的关键代码:

2.1 Peer 介绍

Peer 指通过了通信握手的邻居节点,只有邻居节点才能变为 Peer,只有 Peer 列表中的 节点,才能进行正常的通信。

2.2 Peer 管理

Peers 在代码中以 map 的结构存在,由 server 运行方法 run创建,并在 run 方法中进行 添加和删除维护。Pees 最大默认数量为 25(node/defaults.go 定义)

2.2.1 Peer 动态添加删除流程

Peer 添加分为两种:被动添加和主动添加。 1) 被动添加指其他节点发起握手,流程如下:

2) 每当当前 peers 有变动时,如添加,删除,或者一次 dial任务完成,则会执行一次主动 握手流程如下,其中要进行 Dial(拨号,即握手通信)的节点有以下几部分组成:

  • 静态节点,系统启动时配置文件写入
  • nodeTable 中随机选取(当前 needDynDials 的二分之一,needDynDials 的值为 (s.MaxPeer+1)/2=13)
  • loobbuf 中的节点(discovery task 中的邻居节点)
  • lookbuf 中的节点 Peer 数量不足时,会强制进行一次 nodetable 刷新,刷新到的node 写入 lookbuf。

3) Peer 删除有三种方式: RPC 命令删除,一次应用层通信完成自动删除,通信过程读写错误。

2.2.2 Peer 握手机制

参考《以太坊底层技术研究:Peer 握手机制》

3 表示层:RLP 编码

以太坊所相关有网络上 x 发送的数据均遵循 RLP 编码,参考《RLP 机制分析》

4 应用层:Eth 协议

Peer 握手成功后,即可进行应用层通信,Eth 协议数据包如下表所示:

Eth 协议应用层包括如下命令:

本篇文章由芯链团队整理。

HPB37:POA区块生成机制

目录

1 名词介绍

2 POA区块数据结构

3 新区块生成周期

4 新区块生成优先级

1 名词介绍

节点:普通的以太坊节点,没有区块生成的权利。

矿工:具有区块生成权利的以太坊节点

委员会:所有矿工的集合

2 POA区块数据结构

POA共识中,区块数据与POW有些区别,主要体现在header结构:

序号 字段 POW POA
1 Coinbase 挖矿奖励地址 被提名为矿工的节点地址 |
2 Nonce 随机数 提名分类,添加或者删除 |
3 Extra 其他数据 在Epoch时间点,存储当前委员会集合Singners |
4 Difficulty 挖矿难度 优先级,1或者2,同一个Number的区块,只有一个矿工是2 |

3 新区块生成周期

矿工在三中情况下开始生成区块:

● 程序启动时,执行newWorker方法初始化worker对象时,调用commitNewWork方法,开始生成新的区块。(miner/worker.go)

● 网络接收到其他矿工广播过来的新区块,该区块验证有效插入到区块链后,会产生ChainHeadEvent日志,worker对象的update协程检测到到该日志后,会调用commitNewWork方法,开始生成新的区块。(miner/worker.go)

● 矿工自己生成新的区块并入链后,会调用commitNewWork方法,开始生成新的区块。

(wait协程,miner/worker.go)

● 生成新区块时,矿工会进行一定的延时,延时算法:

高优先级矿工:

header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))

delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now())

(consensus/clique/clique.go中的prepare和seal**两个方法定义)

其他矿工:

header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))

delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now())

wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime

delay += time.Duration(rand.Int63n(int64(wiggle)))

(consensus/clique/clique.go中的prepare和seal两个方法定义)

4 新区块生成优先级

POA共识算法中,委员会中的每一个矿工都会持续的生成新的区块,对于同一个Number的区块,不通的矿工生成该块时优先级不同。

优先级计算方法:

● Number:要生成的区块的块号

● Signers:snapshot中记录的委员会集合,并根据矿工的地址进行了升序排列

● Offset:矿工在Signers集合中的位置

● 若:(number % uint64(len(signers))) == uint64(offset),则优先级最高,header. Difficulty =2;否则,header.Difficulty = 1

本文由HPB(芯链)团队整理。

HPB36:POA委员会选举机制

目录

1 名词介绍

2 矿工投票方法

3 委员会确定投票流程

​ 3.1 关键概念描述

​ 3.1.1 Epoch & checkpointInterval

​ 3.1.2 Snapshot

​ 3.2 投票方法

1.名词介绍

节点:普通的以太坊节点,没有区块生成的权利。

矿工:具有区块生成权利的以太坊节点

委员会:所有矿工的集合

2.矿工投票方法

  • 用户通过RPC接口,调用Propose(address common.Address, auth bool)方法(consensus/clique/api.go),进行投票,address表示要投票的节点的地址,auth表示要从将该地址加入委员会,还是从委员会中删除。
  • Propose方法将address和auth两个输入参数写入到clique.proposals集合中。
  • 任何一个委员会的委员,可以在任意时刻进行投票,投票包括两种,即加入委员会和从委员会中删除。

3.委员会确定投票流程

3.1 关键概念描述

3.1.1 Epoch & checkpointInterval

  • CheckpointInterval:为常量1024(consensus/clique/clique.go中定义),即每当区块链的高度为1024的整数倍时,到达checkpointInterval时间点。
  • Epoch:默认为30000(cmd/puppet/wizard_genesis.go中makeGenesis方法中定义),即每当区块链的高度为30000的整数倍时,到达Epoch时间点。

3.1.2 Snapshot

Snapshot是一个快照,矿工程序在区块链高度为CheckpointInterval的整数倍时,会对当前相关数据和状态形成快照,并存储到数据库中。

snapshot结构体(consensus/clique/snapshot.go)关键成员:

  • Number:生成快照时的区块链高度
  • Signers:生成快照时的委员会地址
  • Votes:生成快照时所有的投票集合
  • Tally:被投票的节点集合,其中的Tally是该节点被投票的次数

3.2投票方法

所有投票都是在委员生成新区块的过程中完成,具体流程如下:

1)委员生成新区块时,先为该区块初始化一个header。

prepare方法,consensus/clique/clique.go)

2)从proposals中随机获取一个投票,将被投票的节点地址写入header.coinbase,将提名是添加还是删除写入header.Nonce(添加:0xffffffffffffffff 删除:0),若该委员生成的这个区块最终被写入区块链,则header中的投票也被写入区块链。

prepare方法,consensus/clique/clique.go)

3)委员在生成新区块时,会创建新的snapshot,新的snapshot是由上一checkponitinterval时间点存储到数据库中的快照加入当前时间点和checkpointinterval时间点之间所有的headers数据组成。添加header过程中,若该header的number是Epoch时间点,则会将snap中的Votes和Tally两个集合清零。

apply方法,consensus/clique/snapshot.go)

4)新的snapshot添加header过程中,会检查每一个header中存储的投票,若该投票snap.Votes中已经存在,则将snap.Votes和snap.Tally两个集合的该投票删除。

apply方法,consensus/clique/snapshot.go)

将每一个header中有效的提名写入新snapshot的snap.Votes和snap.Tally集合。

apply方法,consensus/clique/snapshot.go

5)判断snap.Tally集合中某个被提名的节点,提名的次数是否大于snap.Signers的1/2,即是否有超过一半的委员对该节点进行投票,若超过,则投票成功,该节点会被添加到委员会或者从委员会中删除。

apply方法,consensus/clique/snapshot.go)

注释:snapshot快照中的记录的委员会,即Signers集合,初始化时来源于创世块header中的Extra。

本文由HPB(芯链)团队整理。

HPB35:交易收发机制

目录

1、交易的主要数据结构

2、交易收发相关协程

3、关键流程描述

​ 3.1 交易数据验证流程

​ 3.2 交易入池流程

1、交易的主要数据结构

2、交易收发相关协程

3、关键流程描述

​ 3.1 交易数据验证流程

​ 3.2 交易入池流程

本文由HPB(芯链)团队整理。

HPB34:P2P网络及节点发现机制

P2P网络及节点发现机制

1 分布式网络介绍

1.1 Kad网介绍

1.2 Kad网络节点距离

1.3 K桶

1.4 Kad通信协议

2 邻居节点

2.1 NodeTable类主要成员

2.2 邻居节点发现方法

2.3 邻居节点网络拓扑及刷新机制。

1 分布式网络介绍

以太坊底层分布式网络即P2P网络,使用了经典的Kademlia网络,简称kad。

1.1 Kad网介绍

Kademlia在2002年由美国纽约大学的PetarP.Manmounkov和DavidMazieres提出,是一种分布式散列表(DHT)技术,以异或运算为距离度量基础,已经在BitTorrent、BitComet、Emule等软件中得到应用。

1.2 Kad网络节点距离

以太坊网络节点距离计算方法:

  • Node1:节点1 NodeId
  • Node2:节点2 NodeId

1.3 K桶

Kad的路由表是通过称为K桶的数据构造而成,K桶记录了节点NodeId,distance,endpoint,ip等信息。以太坊K桶按照与target节点距离进行排序,共256个K桶,每个K桶包含16个节点。

图1.1

1.4 Kad通信协议

​ 以太坊Kad网络中节点间通信基于UDP,主要由以下几个命令构成,若两个节点间PING-PONG握手通过,则认为相应节点在线。

2 邻居节点

2.1 NodeTable类主要成员

C++版本以太坊源码中,NodeTable是以太坊 P2P网络的关键类,所有与邻居节点相关的数据和方法均由NodeTable类实现。

2.2 邻居节点发现方法

​ 邻居节点是指加入到K桶,并通过PING-PONG握手的节点。

图2.1

邻居节点发现流程说明:

  • 系统第一次启动随机生成本机节点NodeId,记为LocalId,生成后将固定不变,本地节点记为local-eth。
  • 系统读取公共节点信息,ping-pong握手完成后,将其写入K桶。
  • 系统每隔7200ms刷新一次K桶。
  • 刷新K桶流程如下:
  • 随机生成目标节点Id,记为TargetId,从1开始记录发现次数和刷新时间。

  • 计算TargetId与LocalId的距离,记为Dlt

  • K桶中节点的NodeId记为KadId,计算KadId与TargetId的距离,记为Dkt

  • 找出K桶中Dlt大于Dkt的节点,记为k桶节点,向k桶节点发送FindNODE命令,FindNODE命令包含TargetId

  • K桶节点收到FindNODE命令后,同样执行b-d的过程,将从K桶中找到的节点使用Neighbours命令发回给本机节点。

  • 本机节点收到Neighbours后,将收到的节点写入到K桶中。

  • 若搜索次数不超过8次,刷新时间不超过600ms,则返回到b步骤循环执行。

2.3 邻居节点网络拓扑及刷新机制。

图2.2

1 TargetId为随机生成的虚拟节点ID。

2 以太坊Kad网络与传统Kad网络的区别:

1) 以太坊节点在发现邻居节点的8次循环中,所查找的节点均在距离上向随机生成的TargetId收敛。

2) 传统Kad网络发现节点时,在距离上向节点本身收敛。

本文由HPB(芯链)团队整理。

我携HPB(芯链)团队参加区块链峰会

大家好,我是汪晓明,很久没有更新博客,诚挚的向大家说声抱歉,今天向大家汇报一下我的近况。

我目前正在主导HPB(芯链)项目。HPB(High-performance Blockchain)是一种全新的区块链软硬件体系架构,其中包含芯片加速引擎和区块链底层平台,旨在实现分布式应用的性能扩展。定位为易用的高性能区块链平台,跟产业深度结合,满足现实世界的真实商业需求。这是通过创建一个可以构建应用程序的类似操作系统的架构来实现的。该软件架构提供帐户,身份与授权管理、策略管理、数据库、异步通信以及在数以千计的CPU、FPGA或群集上的程序调度,实现一个全新的体系架构,该区块链每秒可以支持数百万个交易,且达到秒级确认。

2017年7月30日纷智·全球区块链峰会于上海召开,我携HPB(芯链)团队参加了此次大会,并在会上对HPB(芯链)做了介绍,本次视频就是大会的现场演讲的录像,供大家了解HPB(芯链)项目,谢谢。