本篇文章主要给网友们分享以太坊底层存储结构的知识,其中更加会对以太坊底层有哪些数据库进行更多的解释,如果能碰巧解决你现在面临的问题,记得关注本站!
以太坊是一个可编程的区块链。与比特币不同,以太坊并没有给用户提供一组预定义毁皮的操作(比如比特币交易),而是允许用户创建他们自己的操作,这些操作可以任意复杂。这样,以太坊成为了多种不同类型去中心化区块链的平台,包括但是不限于密码学货币。
EVM为以太坊虚拟机。以太坊底层通过EVM模块支持智能合约的执行和调用,调用时根据合约的地址获取到代码,生成具体的执行环境,然后将代码载入到EVM虚拟机中运行。通常目前开发智能合约的高级语言为Solidity,在利用solidity实现智能合约逻辑后,通过编译器编译成元数据(字节码)最后发布到以坊上。
EVM架构概述
EVM本质上是一个堆栈机器,它最直接的的功能是执行智能合约,根据官方给出的设计原理,EVM的主要的设计目标为如下几点:
简单性
确定性
空间节省
为区块链服务
安全性保证
便于优化
针对以上几点通过对EVM源代码的阅读来了解其具体的设计思想和工程实用性。
EVM存储系统机器位宽
EVM机器位宽为256位,即32个字节,256位机器字宽态余乱不同于我们经常见到主流的64位的机器字宽,这就标明EVM设计上将考虑一套自己的关于操作,数据,逻辑控制的指令编码。目前主流的处理器原生的支持的计算数据类型有:8bits整数,16bits整数,32bits整数,64bits整数。一般情况下宽字节的计算将更加的快一些,因为它可能包含更多的指令被一次性加载到pc寄存器中,同时伴有内存访问次数的减少。目前在X86的架构中8bits的计算并不是完全的支持(除法和乘法),但基本的数学运算大概在几个时钟周期内就能完成,也就是说主流的字节宽度基本上处理器能够原生的支持,那为什么EVM要采用256位的字宽。主要从以下两个方面考虑:
时间,智能合约是否能执行得更快
空间,这样是否整体字节码的大小会有所减少
gas成本
时间上主要体现在执行的效率上,我们以两个整型数相加来对比具体的操作时间消耗。32bits相加的X86
的汇编代码
mov eax, dword [9876ABCD] //将地址9876ABCD中的32位数据放入eax数据寄存器
add eax, dword [1234DCBA] //将1234DCBA地址指向32位数和eax相加,结果保存在eax中
64bits相加的X86汇编代码
mov rax, qword [123456789ABCDEF1] //将地址指向的64位数据放入64位寄存器
add rax, qword [1020304050607080] //计算相加的结果并将结果放入到64位寄存器中
链乔教育在线旗下学硕创新区块链技术工作站是中国教育部学校规划建设发展中心开展的“智慧学习工场2020-学硕创新工作站 ”唯一获准的“区块帆档链技术专业”试点工作站。专业站立足为学生提供多样化成长路径,推进专业学位研究生产学研结合培养模式改革,构建应用型、复合型人才培养体系。

RLP(Recursive Length Prefix),中文翻译过来叫递归长度前缀编码,它是以太坊序列化所采用以太坊底层存储结构的编码方式。RLP主要用于以太坊中数据的网络传输和持久化存储。
对象序列化方法有很多种,常见的像JSON编码,但是JSON有个明显的缺点:编码结果比较大。例如有如下的结构:
变量s序列化的结果是{"name":"icattlecoder","sex":"male"},字符串长度35,实际有效数据是icattlecoder 和male,共计16个字节,我们可以看到JSON的序列化时引入了太多的冗余信息。假设以太坊采用JSON来序列化,那么本来50GB的区块链可能现在就要100GB,当然实际没这么简单。
所以,以太坊需要设计一种结果更小的编码方法。
RLP编码的定义只处理两类数据:一类是字符串(例如字节数组),一类是列表。字符串指的是一串二进制数据,列表是一个嵌套递归的结构,里面可以包含字符串和列表,例如["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]就是一个复杂的列表。其以太坊底层存储结构他类型的数据需要转成以上的两类,转换的规则不是RLP编码定义的睁和,可以根据自己的规则转换,例如struct可以转成列表,int可以转成二进制(属于字符串一类),以太坊中整数都以大端形式存储。
从RLP编码的名字可以看出它的特点:一个是递归,被编码的数据是递归的结构,编码算法也是递归高早亩进行处理的;二是长度前缀,也就是RLP编码都带有一个前缀,这个前缀是跟被编码数据的长度相关的,从下面的编码规则中可以看出这一点。
对于值在[0, 127]之间的单个字节,其编码是其本身。
例1:a的编码是97。
如果byte数组长度l = 55,编码的结果是数组本身,再加上128+l作为前缀。
例2:空字符串编码是128,即128 = 128 + 0。
例3:abc编码结果是131 97 98 99,其中131=128+len("abc"),97 98 99依次是a b c。
如果数组长度大于55, 编码结果第一个是183加数组长度的编码的长度,然后是数组长度的本身的编码,最后是byte数组的编码。
请把上面的规则多读几篇,特别是数组长度的编码的长度。
例4:编码下面这段字符串:
The length of this sentence is more than 55 bytes, I know it because I pre-designed it
这段字符串共86个字节,而86的编码只需要一个字节,那就是它自己,因此,编码的结果如下:
184 86 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
其中前三个字节的计算方式如下:
184 = 183 + 1,因为数组长度86编码后仅占用一个字节。
86即数组长度86
84是T的编码
例5:编码一个重复1024次"a"的字符串,其结果为:185 4 0 97 97 97 97 97 97 ...。
1024按 big endian编码为0 0 4 0,省略掉前面的零,长度为2,因此185 = 183 + 2。
规则1~3定义了byte数组的编码方案,下面介绍列表的编码规则。在此之前,我们先定义列表长度是指子列表编码后的长度之和。
如果列表戚森长度小于55,编码结果第一位是192加列表长度的编码的长度,然后依次连接各子列表的编码。
注意规则4本身是递归定义的。
例6:["abc", "def"]的编码结果是200 131 97 98 99 131 100 101 102。
其中abc的编码为131 97 98 99,def的编码为131 100 101 102。两个子字符串的编码后总长度是8,因此编码结果第一位计算得出:192 + 8 = 200。
如果列表长度超过55,编码结果第一位是247加列表长度的编码长度,然后是列表长度本身的编码,最后依次连接各子列表的编码。
规则5本身也是递归定义的,和规则3相似。
例7:
["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]
的编码结果是:
248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
其中前两个字节的计算方式如下:
248 = 247 +1
88 = 86 + 2,在规则3的示例中,长度为86,而在此例中,由于有两个子字符串,每个子字符串本身的长度的编码各占1字节,因此总共占2字节。
第3个字节179依据规则2得出179 = 128 + 51
第55个字节163同样依据规则2得出163 = 128 + 35
例8:最后我们再来看个稍复杂点的例子以加深理解递归长度前缀,
["abc",["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]]
编码结果是:
248 94 131 97 98 99 248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
列表第一项字符串abc根据规则2,编码结果为131 97 98 99,长度为4。
列表第二项也是一个列表项:
["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]
根据规则5,结果为
248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
长度为90,因此,整个列表的编码结果第二位是90 + 4 = 94, 占用1个字节,第一位247 + 1 = 248
以上5条就是RPL的全部编码规则。
各语言在具体实现RLP编码时,首先需要将对像映射成byte数组或列表两种形式。以go语言编码struct为例,会将其映射为列表,例如Student这个对象处理成列表["icattlecoder","male"]
如果编码map类型,可以采用以下列表形式:
[["",""],["",""],["",""]]
解码时,首先根据编码结果第一个字节f的大小,执行以下的规则判断:
1. 如果f∈ [0,128), 那么它是一个字节本身。
2. 如果f∈[128,184),那么它是一个长度不超过55的byte数组,数组的长度为 l=f-128
3. 如果f∈[184,192),那么它是一个长度超过55的数组,长度本身的编码长度ll=f-183,然后从第二个字节开始读取长度为ll的bytes,按照BigEndian编码成整数l,l即为数组的长度。
4. 如果f∈(192,247],那么它是一个编码后总长度不超过55的列表,列表长度为l=f-192。递归使用规则1~4进行解码。
5. 如果f∈(247,256],那么它是编码后长度大于55的列表,其长度本身的编码长度ll=f-247,然后从第二个字节读取长度为ll的bytes,按BigEndian编码成整数l,l即为子列表长度。然后递归根据解码规则进行解码。
以上解释了什么叫递归长度前缀编码,这个名字本身很好的解释了编码规则。
(1) 以太坊源码学习—RLP编码( )
(2)简单分析RLP编码原理
( )
在以太坊中,采用了一种名为Recursive Length Prefix(RLP)的方法对交易、账号、合约等基础的数据结构进行序列化处理,从而实现段卖对链上数据的网络传输和持久化存储。RLP作为最为底层的编码方法,其重要性是不言而喻。因此,网上介绍RLP的文章也不少,但是由于RLP是二进制编码,又涉及到嵌套结构,造成编码过程的可读性较差,在学习中过程中,也一直没有找到完整的、易于理解的说明,总是绕在各种规则之中,且不能"自拔",着实有点无奈。所以,在本文中,采用图形化的解释和举例的方法,帮助大家理解RLP嵌套等特点、编解码过程等。
和其他的序列化协议不同,RLP只支持两种数据类型:
1)byte数组,可以是二进制数组,当然也可以是字符串;
2)byte数组的数组,也就是列表。并支持列表内的嵌套。
对于其他的数据类型,RLP都不支持,需要用户自己先转化为数组和列表的类型。
从RLP的命名中就可以看出两个关键字:一个是递归Recursive和前缀Prefix。首先,关于递归,也就是嵌套结构,结构上非常接近“树”,在Ethereum WiKi中,更是直接地采用树的items来进行命名,叶子节点(leaf tress)来存储“byte数组”,嵌套的节点就是一个树的分叉(branching trees)。
比如,需要是对如下对象进行RLP的编码,该对象中包含一个字符数组的列表、一个单个字符的字符数组、一个空字符数组。
[cat],[dog], [0xbf], []
将该对象展开为树的结构,就如下图。其中[0xbf]和[]属于字符数组。[cat], [dog]属于列表,可以嵌套展开,再根据各个节点,进行编码。然后,对于不同长度的数组和列表,编码的方法略有不同,这个也就是Length Prefix相关的内容,和“编码过程”相关的内容,在第二节进行详细地说明。
关于为什么以太坊需要单独设计一种序列化协议,目前还没有找到官方的描述。但与其他序列化方法相比,RLP协议具有一些直接的优点,比如:
1)在以太坊中,最小货币单位为1 Wei,并且1 ETH = 10^18 Wei,所以在编码中,需要考虑对很大的整数类型的序列化,在RLP中采用去除前导零(leading zero)的大端big-endian方式,可以有效处理大整数;
2)使用了灵活的长度前缀来表示数据的实际长度,并且使用递归的方式能编码相当大的数据;
3)为了实现在链上节点的“共识Consensus”,防止出现数据的不一致,以太坊中并不支持浮点数类型,所以一般的序列化协议也不适用。
编码的过程就是将嵌套结构(nested sequence)的树形结构,添加长度前缀(Length Prefix)后,转化为顺序结构(flat sequence)的过程。添加长度前缀的目的,就是在反序列化时,可以根据长度前缀(Length Prefix),将(flat sequence)重构出树的结构(nested sequence)。
关于前缀的生成规则,《Ethereum Yellow Paper》[2]给出了非常形式化的数学符号描述,漂亮是非常漂亮,可惜不是人类的语言,非常难于理解和表达。网上大部分文章的写法也是引用了Yellow Paper中的5个文字形式握租逗上的描述,把原文和翻译一并给出如下:
将上面这型扒个“长度”Length Prefix的编码规则,通过“决策树”可以图形化的表达如下图。
首先,根据编码的类型,进行分类,分为“字节数组”和“列表”两类;第二,根据不同的长度,编码的长度前缀不同。若待编码对象的长度小于56,就是把长度和“前缀字符”进行求和,占用一个字节。反之,待编码对象的长度大于56,其前缀需要多个字节,第一个字节,求出“长度”所占的字节数,再加上“前缀字符”,比如:长度为56,占用1字节。然后对“长度”进行编码,其实也是一个嵌套的过程。
还是以上文中的例子,该编码对象,已经完成了“树的构建”,然后根据“长度前缀”的原则,对树的各个项目进行长度前缀的计算。
[cat],[dog], [0xbf], []
-对于[cat],[dog]属于嵌套数组,需要对内部各项非常进行长度编码的计算
`对于[cat],属于字符数组,且长度为3,其对应的长度为0x80+3 = 0x83
`对于[dog],属于字符数组,且长度为3,其对应的长度为0x80+3 = 0x83
`[cat],[dog]整体上,其长度前缀为0xc0 + 2(新增的两个子项的长度所占用的字节)+6(待编码字符的长度)=0xC8
- 对于[0xbf], 属于字符数组,且长度为1,其对应的长度为0x80+1 = 0x81
- 对于[dog],属于字符数组,且长度为3,其对应的长度为0x80+3 = 0x83
- 对于[],属于字符数组,且长度为0,其对应的长度为0x80+0=0x80
总体上,增加的“长度编码”的字节数为6,加上原来的长度为10,所以整个对象的长度前缀为0xC0+16d=0xD0。所以最后的编码结果为:
D0 C8 83636174 83646F67 81B7 83646F67 80
解码过程将在 《以太坊解读——Recursive Length Prefix协议图解(下)》 一文中,给出图形化的解读说明。
以太坊是什么丨以太坊开发入门指南
很多同学已经跃跃欲试投入到区块链开发队伍当中来,可是又感觉无从下手,本文将基于以太坊平台,以通俗的方式介绍以太坊开发中涉及的各晦涩的概念,轻松带大家入门。
以太坊是什么
以太坊(Ethereum)是一个建立在区块链技术之上, 去中心化应用平台。它允许任何人在平台中建立和使用通过区块链技术运行的去中心化应用。
对这句话不理解的同学,姑且可以理解为以太坊是区块链里的Android,它是一个开发平台,让我们就可以像基于Android Framework一样基于区块链技术写应用。
在没有以太坊之前,写区块链应用是这样的:拷贝一份比特币代码,然后去改底层代码如加密禅罩纤算法,共识机制,网络协议等等(很多山寨币就是这样,改改就出来一个新币)。
以太坊平台对底层区块链技术进行了封装,让区块链应用开发者可以直接基于以太坊平台进行开发,开发者只要专注于应用本身的开发,从而大大降低了难度。
目前围绕以太坊已经形成了一个较为完善的开发生态圈:有社区的支持,有很多开发框架、工具可以选择。
智能合约
什么是智能合约
以太坊上的程序称之为智能合约, 它是代码和数据(状态)的集合。
智能合约可以理解为在区块链上可以自动执行的(由事件驱动的)、以代码形式编写的合同(特殊的交易)。
在比特币脚本中,我们讲到过比特币的交易是可以编程的,但是比特币脚本有很多的限制,能够编写的程序也有限,闷厅而以太坊则更加完备(在计算机科学术语中,称它为是“图灵完备的”),让我们就像使用任何高级语言一样来编写几乎可以做任何事情的程序(智能合约)。
智能合约非常适合对信任、安全和持久性要求较高的应用场景,比如:数字货币、数字资产、投票、保险、金融应用、预测市场、产权所有权管理、物联网、点对点交易等等。
目前除数字货币之外,真正落地的应用还不多(就像移动平台刚开始出来一样),相信1到3年内,各种杀手级会慢慢出现。
编程语言:Solidity
智能合约的默认的编程语言是Solidity,文件扩展名以.sol结尾。
Solidity是和JavaScript相似的语言,用它来开发合约并编译成以太坊虚拟机字节代码。
还有长像Python的智能合约开发语言:Serpent,不过建议大家还是使用Solidity。
Browser-Solidity是一个浏览器的Solidity IDE, 大家可以点进去看看,以后我们更多文章介绍Solidity这个语言。
运行环境:EVM
EVM(Ethereum Virtual Machine)以太坊虚拟机是以太坊中智能合约的运行环境。
Solidity之于EVM,就像之于跟JVM的关系一样,这样大家就容易理解了。
以太坊虚拟机是一个隔离的环境,在EVM内部运行的代码不能跟外部有联系。
而EVM运行在以太坊节点上,当我们把合约部署到以太坊网络上之后,合约就可以在贺仿以太坊网络中运行了。
合约的编译
以太坊虚拟机上运行的是合约的字节码形式,需要我们在部署之前先对合约进行编译,可以选择Browser-Solidity Web IDE或solc编译器。
合约的部署
在以太坊上开发应用时,常常要使用到以太坊客户端(钱包)。平时我们在开发中,一般不接触到客户端或钱包的概念,它是什么呢?
以太坊客户端(钱包)
以太坊客户端,其实我们可以把它理解为一个开发者工具,它提供账户管理、挖矿、转账、智能合约的部署和执行等等功能。
EVM是由以太坊客户端提供的。
Geth是典型的开发以太坊时使用的客户端,基于Go语言开发。 Geth提供了一个交互式命令控制台,通过命令控制台中包含了以太坊的各种功能(API)。Geth的使用我们之后会有文章介绍,这里大家先有个概念。
Geth控制台和Chrome浏览器开发者工具里的面的控制台是类似,不过是跑在终端里。
相对于Geth,Mist则是图形化操作界面的以太坊客户端。
如何部署
智能合约的部署是指把合约字节码发布到区块链上,并使用一个特定的地址来标示这个合约,这个地址称为合约账户。
以太坊中有两类账户:
· 外部账户
该类账户被私钥控制(由人控制),没有关联任何代码。
· 合约账户
该类账户被它们的合约代码控制且有代码与之关联。
和比特币使用UTXO的设计不一样,以太坊使用更为简单的账户概念。
两类账户对于EVM来说是一样的。
外部账户与合约账户的区别和关系是这样的:一个外部账户可以通过创建和用自己的私钥来对交易进行签名,来发送消息给另一个外部账户或合约账户。
在两个外部账户之间传送消息是价值转移的过程。但从外部账户到合约账户的消息会激活合约账户的代码,允许它执行各种动作(比如转移代币,写入内部存储,挖出一个新代币,执行一些运算,创建一个新的合约等等)。
只有当外部账户发出指令时,合同账户才会执行相应的操作。
合约部署就是将编译好的合约字节码通过外部账号发送交易的形式部署到以太坊区块链上(由实际矿工出块之后,才真正部署成功)。
运行
合约部署之后,当需要调用这个智能合约的方法时只需要向这个合约账户发送消息(交易)即可,通过消息触发后智能合约的代码就会在EVM中执行了。
Gas
和云计算相似,占用区块链的资源(不管是简单的转账交易,还是合约的部署和执行)同样需要付出相应的费用(天下没有免费的午餐对不对!)。
以太坊上用Gas机制来计费,Gas也可以认为是一个工作量单位,智能合约越复杂(计算步骤的数量和类型,占用的内存等),用来完成运行就需要越多Gas。
任何特定的合约所需的运行合约的Gas数量是固定的,由合约的复杂度决定。
而Gas价格由运行合约的人在提交运行合约请求的时候规定,以确定他愿意为这次交易愿意付出的费用:Gas价格(用以太币计价) * Gas数量。
Gas的目的是限制执行交易所需的工作量,同时为执行支付费用。当EVM执行交易时,Gas将按照特定规则被逐渐消耗,无论执行到什么位置,一旦Gas被耗尽,将会触发异常。当前调用帧所做的所有状态修改都将被回滚, 如果执行结束还有Gas剩余,这些Gas将被返还给发送账户。
如果没有这个限制,就会有人写出无法停止(如:死循环)的合约来阻塞网络。
因此实际上(把前面的内容串起来),我们需要一个有以太币余额的外部账户,来发起一个交易(普通交易或部署、运行一个合约),运行时,矿工收取相应的工作量费用。
以太坊网络
有些着急的同学要问了,没有以太币,要怎么进行智能合约的开发?可以选择以下方式:
选择以太坊官网测试网络Testnet
测试网络中,我们可以很容易获得免费的以太币,缺点是需要发很长时间初始化节点。
使用私有链
创建自己的以太币私有测试网络,通常也称为私有链,我们可以用它来作为一个测试环境来开发、调试和测试智能合约。
通过上面提到的Geth很容易就可以创建一个属于自己的测试网络,以太币想挖多少挖多少,也免去了同步正式网络的整个区块链数据。
使用开发者网络(模式)
相比私有链,开发者网络(模式)下,会自动分配一个有大量余额的开发者账户给我们使用。
使用模拟环境
另一个创建测试网络的方法是使用testrpc,testrpc是在本地使用内存模拟的一个以太坊环境,对于开发调试来说,更方便快捷。而且testrpc可以在启动时帮我们创建10个存有资金的测试账户。
进行合约开发时,可以在testrpc中测试通过后,再部署到Geth节点中去。
更新:testrpc 现在已经并入到Truffle 开发框架中,现在名字是Ganache CLI。
Dapp:去中心化的应用程序
以太坊社区把基于智能合约的应用称为去中心化的应用程序(DecentralizedApp)。如果我们把区块链理解为一个不可篡改的数据库,智能合约理解为和数据库打交道的程序,那就很容易理解Dapp了,一个Dapp不单单有智能合约,比如还需要有一个友好的用户界面和其他的东西。
Truffle
Truffle是Dapp开发框架,他可以帮我们处理掉大量无关紧要的小事情,让我们可以迅速开始写代码-编译-部署-测试-打包DApp这个流程。
总结
我们现在来总结一下,以太坊是平台,它让我们方便的使用区块链技术开发去中心化的应用,在这个应用中,使用Solidity来编写和区块链交互的智能合约,合约编写好后之后,我们需要用以太坊客户端用一个有余额的账户去部署及运行合约(使用Truffle框架可以更好的帮助我们做这些事情了)。为了开发方便,我们可以用Geth或testrpc来搭建一个测试网络。
注:本文中为了方便大家理解,对一些概念做了类比,有些严格来不是准确,不过我也认为对于初学者,也没有必要把每一个概念掌握的很细致和准确,学习是一个逐步深入的过程,很多时候我们会发现,过一段后,我们会对同一个东西有不一样的理解。
为什么很多人觉得以太坊是金融机构下一个布局以太坊底层存储结构的项目?加密资产那么多以太坊底层存储结构,为何偏偏青睐以太坊?
首先需要我们了解以太坊以太坊底层存储结构的底层逻辑。熟悉的人都知道,它像一个生态系统,提供各种各样的原材料,我们只需要根据这些材料组装成我们需要的功能通过编程橘返语言搭建就算完成了。简单理解,以太坊更像是一个安卓系统,程序的开发者们只需要在基于以太坊链上开发满足自己需求的APP功能即可,也正是基于此,以太坊被很多人称为“数字石油”。
加密生态系统的三大举措——支付、DeFi 和 NFTs——大多是建立在以太坊之上,因此以太坊的定价具有网络效应。使用它的人越多,基于它的产品就越多,最终价格也会越高。而由于在过去的五年里,世界上很多人都把加密货币和比特币联系在一起,既有认知忽略了以太坊的实际价值层面。
实际上比特币的功能相对来说就简单多了,最主要就是一个分布式记账。可以说以太坊底层存储结构:以太坊的出现就是为了完善比特币的许多缺陷地方,以太坊的功能应用更加的丰富,只要有更多得开发者在以太坊上面开发智能合约,那么以太坊生态就会越来越庞大,价值也就凸显。
价值总是源于使用。黄金和房产等过去的价值存储的关键在于,过去有人需要这些资产 (效用性),并通过交换一些有价值的东西 (通常是货币) 来赋予它们价值。事实上,在成为投资资产之前,所有重要的非收益性价值储存手段都有了实际用途。
那么,以太坊的价值储藏手段体现在哪里?
以太坊生态系统支持智能合约,并为开发者提供了在其平台上创建新应用的方式。 大多数去中心化金融 (DeFi) 应用都建立在以太坊网络上,目前发行的 NFTs (非同质化代币) 都是使用 ETH 购买的。与比特币相比,ETH 的交易量更大,这反映了这种主导地位。随着加密货币在 DeFi 和 NFTs 中的使用越来越广泛,ETH 将在应用加密技术方面建立自己的先发优势。
以太坊还可以用于在分布式账本上安全、私密地存储几乎任何信息。 这些信息可以被代币化并进行交易。这意味着以太坊平台有潜力成为可信信息的巨大市场。我们可以通过当前人们使用 NFTs 在线销售数字艺术和收藏品来看到这一点。但这只是以太坊实际用途的一小部分。例如,个人可以通过以太坊存储并将其医疗数据出售给制药研究公司。以太坊上的数字档案可能包含个人数据,包括资产所有权、医疗 历史 ,甚至知稿轿识产权。以太坊还具有作为去中键伍肆心化的全球基础服务器的好处,而不是像亚马逊或微软这样的中心化服务器,使其可能为共享个人数据提供了一个解决方案。
以太坊正在经历更快的协议升级。 即以太坊目前正在从 PoW 向 PoS 的验证方法过渡。PoS 系统的优势在于大大提高了系统的能源效率,因为它根据矿工 (验证者) 质押的 ETH 数量 (而不是他们的处理能力) 来奖励他们,这将结束为了获取矿工奖励而展开的耗能竞赛。比特币的能源消耗已经相当于荷兰的规模,能源消耗太大,一直也是相关部门诟病需关闭违规挖矿的重要考量。
总而言之,任何一项能够长久生存下去的技术,一定是可以解决现实生活中的实际问题,创造价值。如果是泡沫,终究有破灭的一天,而我们需要的就是不断学习,看清楚事物背后的底层逻辑。
注:本文不代表任何投资建议,只是巴哥个人对于以太坊价值的一些浅显看法。
节点发伍宏现功能主要涉及 Server \ Table \ udp 这几个数据结构,它们有独自的事件响应循环,节点发现功能便是它们互相协作完成的。其中,每个以太坊客户端启动后都会在本地运行一个 Server ,并将网络拓扑中相邻的节点视为 Node ,而 Table 是 Node 的容器, udp 则是负责维持底层的连接。下面重点描述它们中重要的字段和事件循环处理的关键部分。
PrivateKey - 本节点的私钥,用于与其他节点建立时的握手协商
Protocols - 支持的所有上层协议
StaticNodes - 预设的静态 Peer ,节点启动时会首先去向它们发起连接,建立邻居关系
newTransport - 下层传输层实现,定义握手过程中的数据加密解密方式,默认的传输层实现是用 newRLPX() 创建的 rlpx ,这不是本文的重点
ntab - 典型实现是 Table ,所有 peer 以 Node 的形式存放在 Table
ourHandshake - 与其他节点建立连接时的握手信息,包含本地节点的版本号以及支持的上层协议
addpeer - 连接握手完成后,连接过程通过这个通道通知 Server
Server 的监听循环,启动底层监听socket,当收到连接请求时,Accept后调用 setupConn() 开始连接建立过程
Server的主要事件处理和功能实现循环
Node 唯一表示网络上的一个节和咐点
IP - IP地址
UDP/TCP - 连接使用的UDP/TCP端口号
ID - 以太坊网络中唯一标识一个节点,本质上是一个椭圆曲线公钥(PublicKey),与 Server 的 PrivateKey 对应。一个节点的IP地址不一定是固定的,但ID是唯一的。
sha - 用于节点间的距离计算
Table 主要用来管理与本节点与其他节点的连接的建立\更新\删除
bucket - 所有 peer 按与本节点的距离远近放在不同的桶(bucket)中,详见之后的 节点维护
refreshReq - 更新 Table 请求通道
Table 的主要事件循环,主要负责控制 refresh 和 revalidate 过程。
refresh.C - 定时(30s)启动Peer刷新过程的定时器
refreshReq - 接收其他线程投递到 Table 的 刷新Peer连接 的通知,当收到该通知时启动更新,详见之后的 更新邻居关系
revalidate.C - 定时重新检查以连接节点的有效性的定时器,详见之后的 探活检测
udp 负责节点间通信的底层消息控制,是 Table 运行的 Kademlia 协议的底层组件
conn - 底层监听端口的连接
addpending - udp 用来接收 pending 的channel。使用场景为:当我们向其他节点发送数据包后(packet)后可能会期待收到它的回复,pending用来记录一次这种还没有到来的回复。举个例子,当我们发送ping包时,总是期待对方回复pong包。这时就可以将构造一个pending结构,其中包含期待接收的pong包的信息以及对应的callback函数,将这个pengding投递到udp的这个channel。 udp 在收到匹配的pong后,执行预设的callback。
gotreply - udp 用来接收其他节点回复的通道,配合上面的addpending,收到回复后,遍历已有的pending链表,看是否有匹配的pending。
Table - 和 Server 中的ntab是同一个 Table
udp 的处理循环,负责控制消息的向上递交和收发控制
udp 的底层接受数据包循环,负责接收其他节点的 packet
以太坊使用 Kademlia 分布式路由存储协议来进行网络拓扑维护,了解该协议建议先阅读 易懂分布式 。唤橘纯更权威的资料可以查看 wiki 。总的来说该协议:
源码中由 Table 结构保存所有 bucket , bucket 结构如下
节点可以在 entries 和 replacements 互相转化,一个 entries 节点如果 Validate 失败,那么它会被原本将一个原本在 replacements 数组的节点替换。
有效性检测就是利用 ping 消息进行探活操作。 Table.loop() 启动了一个定时器(0~10s),定期随机选择一个bucket,向其 entries 中末尾的节点发送 ping 消息,如果对方回应了 pong ,则探活成功。
Table.loop() 会定期(定时器超时)或不定期(收到refreshReq)地进行更新邻居关系(发现新邻居),两者都调用 doRefresh() 方法,该方法对在网络上查找离自身和三个随机节点最近的若干个节点。
Table 的 lookup() 方法用来实现节点查找目标节点,它的实现就是 Kademlia 协议,通过节点间的接力,一步一步接近目标。
当一个节点启动后,它会首先向配置的静态节点发起连接,发起连接的过程称为 Dial ,源码中通过创建 dialTask 跟踪这个过程
dialTask表示一次向其他节点主动发起连接的任务
在 Server 启动时,会调用 newDialState() 根据预配置的 StaticNodes 初始化一批 dialTask , 并在 Server.run() 方法中,启动这些这些任务。
Dial 过程需要知道目标节点( dest )的IP地址,如果不知道的话,就要先使用 recolve() 解析出目标的IP地址,怎么解析?就是先要用借助 Kademlia 协议在网络中查找目标节点。
当得到目标节点的IP后,下一步便是建立连接,这是通过 dialTask.dial() 建立连接
连接建立的握手过程分为两个阶段,在在 SetupConn() 中实现
第一阶段为 ECDH密钥建立 :
第二阶段为协议握手,互相交换支持的上层协议
如果两次握手都通过,dialTask将向 Server 的 addpeer 通道发送 peer 的信息
关于以太坊底层存储结构和以太坊底层有哪些数据库的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
评论