待字闺中开发了一门区块链方面的课程:《深入浅出ETH原理与智能合约开发》,马良老师讲授。此简书文集记录我的学习笔记。
课程共8节课。其中,前四课讲ETH原理,后四课讲智能合约。
第二课分为三部分:
- 以太坊交易
- MPT与RLP
- MPT与RLP实验
这篇文章是第二课第三部分的学习笔记:MPT与RLP实验。
这节课是上机实验,检验前一小节课程内容。
1、课前准备
1.1 拉取代码
新建一个工作目录,比如 WorkDir ,执行如下命令,下载老师的教学代码。
git clone https://git.coding.net/blockchainsaw/mastering_eth_course.git
进入到第2课的目录下,进入到 trie 目录。
1.2 安装依赖
老师已经将要安装的依赖写成了文件。cat setup.sh
可查看具体有哪些。直接执行即可,source setup.sh
。
执行 source setup.sh 过程中,提示没有 pip ,搜索了一下怎么安装的。
安装方法:sudo apt-get install python-pip
查看pip版本:pip -v
查看pip帮助:pip -help
1.3 安装vim
老师执行 vim rlptest.py
查看代码,我照做,提示没有安装 vim ,又去搜索怎么安装。安装方法sudo apt install vim
,安装成功后,可以查看代码了。
Vim 是一个很厉害的编辑器。简单学习了几个入门级的命令,比如:从光标后插入 a
、 从光标前插入 i
、退出编辑模式 Esc
、保存修改退出vim回到终端 :wq
、不保存修改退出vim回到终端 :q!
、等。
1.4 使用tmux
了解到老师在课程中使用了 tmux 终端,比Ubuntu默认的好用,也安装了一下。sudo apt-get install tmux
学习几个常用的操作:
左右分屏:Ctrl+B
,%
(按Ctrl+B,再按shift+5)
上下分屏:Ctrl+B
,"
退出分屏:Ctrl+B
,X
或者exit
切换分屏:Ctrl+B
,→
或者Ctrl+B
,←
退出tmux:Ctrl+B
,&
接下来就是查看代码,执行代码,检验前面所学内容了。
2、RLP编码演示
进入到 lessson_2/trie 目录,执行python rlptest.py
。其中,rlptest.py 的代码如下:
import sys
sys.path.append('src')
import rlp
test_list = ["helloworld",
chr(0),
chr(126),
"The quick brown fox jumps over the lazy dog The quick brown fox jumps over the lazy dog",
["Peter Piper picked a peck of pickled peppers","Betty Botter bought some butter"],
'']
for test in test_list:
if isinstance(test, list):
for s in test:
print("original string in list :" + s)
print("original string in list :" + hex(len(s)))
else:
print("original string :" + test)
print("original string length: " + hex(len(test)))
print("after encode : 0x" + rlp.encode(test).encode('hex'))
输出结果如下:
以下是6个例子文字版及结果分析,编码规则可参考前一小节《MPT与RLP》的最后一部分——5、RLP的编码标准。
- 字符串,长度为10,即十六进制的0xa。0x80+0xa=0x8a。所以编码的前缀为0x8a,后面的68、65、6c、6c、6f、77、6f、72、6c、64分别为helloworld的10个字母。
original string :helloworld
original string length: 0xa
after encode : 0x8a68656c6c6f776f726c64
- 单一字节,如果其值介于[0x00, 0x7F]之间,则保持不变。
original string :
original string length: 0x1
after encode : 0x00
- 单一字节,如果其值介于[0x00, 0x7F]之间,则保持不变。
original string :~
original string length: 0x1
after encode : 0x7e
- 字符串,长度为0x57,长度的长度为1,0xb7+0x1=0xb8,所以前缀是0xb857。接下来的54是字母T,……,67是字母g 。
original string :The quick brown fox jumps over the lazy dog The quick brown fox jumps over the lazy dog
original string length: 0x57
after encode : 0xb85754686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672054686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67
- 再贴一下规则。
本例中,分别按字符串编码规则对两个元素编码,两个的长度都小于55。
对第一个:0x80+0x2c=0xac
编码为:
ac5065746572205069706572207069636b65642061207065636b206f66207069636b6c65642070657070657273
对第二个:0x80+0x1f=0x9f
编码为:
9f426574747920426f7474657220626f7567687420736f6d6520627574746572
而这两个编码的总长度为0x4d,长度的长度为1,0xf7+0x1=0xf8,所以前缀是0xf84d,后面再接上前面得出的两个编码,即为最终的编码结果。
original string in list :Peter Piper picked a peck of pickled peppers
original string in list :0x2c
original string in list :Betty Botter bought some butter
original string in list :0x1f
after encode : 0xf84dac5065746572205069706572207069636b65642061207065636b206f66207069636b6c656420706570706572739f426574747920426f7474657220626f7567687420736f6d6520627574746572
- 空字符串,规定为0x80
original string :
original string length: 0x0
after encode : 0x80
- 另外,我又照着第5个改写了一个例子。
原始为["PPeter Piper picked a peck of pickled peppers,Betty Botter bought some butter"],总长度为0x4d,列表中只有一个元素。
对这一个元素编码为0xb84d……(后面是字符串)。现在编码的长度不再是0x4d,而是0x4f,因为多2个长度的前缀。所以整个列表的前缀是0xf84f。最终结果为0xf84fb84d……(后面是字符串)。
riginal string in list :PPeter Piper picked a peck of pickled peppers,Betty Botter bought some butter
original string in list :0x4d
after encode : 0xf84fb84d505065746572205069706572207069636b65642061207065636b206f66207069636b6c656420706570706572732c426574747920426f7474657220626f7567687420736f6d6520627574746572
3、MPT编码演示
下面的例子中,只第一个放上完整代码,其他的只写主要部分。编码规则可参考前一小节《MPT与RLP》的第2、3、4部分。
3.1 例子ex1.py
代码如下:
import sys
sys.path.append('src')
import trie, utils, rlp
#initialize trie
state = trie.Trie('triedb', trie.BLANK_ROOT)
state.update('\x01\x01\x02', rlp.encode(['hello']))
print 'root hash', state.root_hash.encode('hex')
k, v = state.root_node
print 'root node:', [k, v]
print 'hp encoded key, in hex:', k.encode('hex')
主要关注部分为,key = 0x010102, value = 'hello' 。
执行python exercises/ex1.py
,得出结果。
编码分析:key之前补一个四元组,(倒数)第0位区分奇偶信息,偶数为0;第1位区分节点类型,叶子节点1。0010=2,高4位为2,key的长度为偶数,再补一个四元组0x0,即前缀为0x20,最终的Hex-Prefix编码为0x20010102,如图最后一行。
3.2 例子ex2.py
(010102: 'hellothere')
这个例子同前一个,只是value值不同。最终的Hex-Prefix编码也是0x20010102。
接下来3个例子可参考下图。ex2b.py这个例子的MPT图可能不对,待请教老师后再做补充。
3.3 例子ex2b.py
(010102: 'hello')
(010103: 'hellothere')
前缀:key之前补一个四元组,(倒数)第0位区分奇偶信息,奇数为1;第1位区分节点类型,扩展节点0。0001=1,高4位为1,key的长度为奇数,不需要再补一个四元组0x0,所以前缀为0x1 。
3.4 例子ex2c.py
(010102: 'hello')
(0101: 'hellothere')
扩展节点的前缀:key之前补一个四元组,(倒数)第0位区分奇偶信息,偶数为0;第1位区分节点类型,扩展节点0。0000=0,高4位为0,key的长度为偶数,再补一个四元组0x0,所以前缀为0x00 。
扩展节点的前缀:key之前补一个四元组,(倒数)第0位区分奇偶信息,奇数数为1;第1位区分节点类型,叶子节点1。0011=3,高4位为3,key的长度为奇数,不需要再补一个四元组0x0,所以前缀为0x3 。
3.5 例子ex2d.py
(010102: 'hello')
(01010255: 'hellothere')
此例子的前缀同前一个例子。另见上图。
后记:写这篇笔记时学会了怎么在简书高亮代码,就是用键盘上1左边那个键把代码包起来,前后各一个,或者一段文字的前面各三个。画风有点不好看~
不足之处请指教,谢谢。