以太坊(ETH)技术黄皮书学习笔记2:RLP封装
RLP封装是以太坊中,将任意二进制数据进行序列化的一个算法。在计算机的世界里,数据是以字节码的形式存在,但是在不同的计算机硬件和软件架构下,字节码的大小和存储方式是不一样的,在网络传输的过程中,数据的封装也是有区别的。以太坊的定位是区块链世界的操作系统,他必须屏蔽掉底层操作系统的差异,提供给DAPP开发者和用户无差别的数据存储格式,因此以太坊定义了很多自己的独有的数据封装方式。RLP就是其中一种,先来看RLP的定义:
首先公式1,2,3定义了RLP算法可以操作的对象,公式1说明操作对象是L集合与B结合的并集,B集合即公式3的定义表述为:B是一个集合,集合里面的任何一个元素元素b都是一个序列,序列是有x个元素组成:b[0],b[1]…,则序列b的长度||b||=x,即序列的长度是序列的元素的个数,对于任意一个小于x的整数n,b[n]属于集合O,集合O是所有计算机能表达的字节的集合。字节是计算机的计算和存储单位。简单来说B就是所有计算机能表达的任意字节序列。L集合即公式2是一个循环定义,它与公式3的区别就是其集合的元素t属于集合T而不是O,那意味着L里面的元素有可能是个L集合或者B结合。
因为RLP操作对象的类型不同,将RLP定义为一个分段函数,公式4给出了详细的定义。即针对字节集合定义了公式5。
其中根据3中不同的情况,又将公式5分成了3段函数,分别是公式8,9,10下面以示例的方式讲解:
首先部分ASCII字节码表,这个是基本的计算机知识,不了解的同学可以简单搜索一下相关知识:
在公式5中,我们需要使用每个字节对于的计算机表示才能进行编码
示例1中,对应的公式5中的第一种情况,字符a在ASCII码表中的数值为97,显然字符a的长度是1并且其值小于128,因此对此字符的RLP操作是不做任何修改,直接数据结果为a本身。这个比较好理解,不再详细解析。
示例2中,我对字符串dog进行RLP封装,其结果为"83646f67"。dog字符的长度是3,大于1小于56,属于公式5中第二种情况,按照公式,首先计算128+||x||
= 128 +3
=131=0x83,其中0x83是十进制数131的16进制表示方式,十进制表示是我们日常使用的数字进制方式,16进制表示方式请大家自行搜索。继续查ASCII表,字符d的十进制值为100,16进制表示为0x64,以此类推。我们根据公式8得到了字符串dog的RLP封装。其中的点操作是将转义之后的字符串拼接起来,公式7给出了点操作的准确定义。
公式9用到了公式6,在讲解公式9之前先讲解公式6,BE函数是将一个正数按照以8bit位为单位的,长度最小的字符串表示出来,
上图为第三种情况下的示例,其中公式1表示BE的定义,与图1公式6是一样的,这个规则用数学公式表达有点复杂,但是如果用语言表达会比较简单,一个数字16进制表达可以有多个0存在,然后以8个比特位为单位,找一个消耗单位数最少的表达方式,比如上图中表达式2,x=56的十六进制表达最短为0x38。需要注意的是BE的计算结果为一个序列,其中每个序列元素的长度为8bit,因此表达式3是BE函数对56的处理结果:1个元素0x38。表达4是BE对1024的处理结果:2个元素:0x04,
0x00。
有了对BE的理解,那么Rb(x)的第三种情况就比较容易理解,表达式5是一个长度为56的字符串,将该字符代入到公式6,则BE(56)=(0x38),
||BE(56)|| =
1,表示该序列只有1个元素。其结果是表达式7。"0X"是表明后面的数字的表达方式为16进制方式。我在此给出y和Rb(y)的值:
y="Lorem ipsum dolor sit amet, consectetur adipisicing elit"
Rb(y)= "B8384C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C6974"
有兴趣的同学可以参考ASCII码表检查一下。
上面的例子只表示了RLP封装对象是字节集合的情况,当封装对象不是B集合而是L集合的时候,使用以下公式:
其中公式5最为简单,我们首先来解释这个公式,此公式是对自然数i的单独的RLP定义,我们知道很多计算机上对自然数int的表达需要32位即4个字节(1字节=8bit位),但是如果数字比较小,完全有2个字节或者一个字节表示,那么就可以通过BE算法,先缩短数字的表达长度然后再运用RLP的封装算法去运算,简单的例子,在32位机器中,int类型的变量i=1存取时长度是4个字节:0x00000001,但是通过BE之后是0x01,然后再运用RLP序列化封装。
对于公式3和2分别举例如下:
其中表达是6处理一个{1,2,3}这样的序列,把他分解成对1,2,3的处理,然后再添加响应的开头即可,此示例理解比较方便,无需过多解释。
表达式7处理的是一个字符串序列,一共15个字符串,每个字符串有3个字母,通过公式我们将每个字符串用Rb(x)去处理,然后将连接起来的s(x)用最新的开头数字247+x来表示。整个对L集合的处理就是按照一定的规则再分解到B域去处理,将处理的结果,计算长度,然后在应用L域的特殊开头字符来表示。
代码的位置如下:有兴趣的同学可以根据test脚本去加深理解。下一节将改造后的merkle 树。