00:00
啊,今天这一部分主要就是以太坊虚拟机,前面我们已经介绍过整个以太坊。被认为是一个世界计算机,那这个世界计算机是无数个,我们称为以太坊节点,也就是说就像我们的盖客户端,它起来之后就可以作为一个一台仿节点参入到这个世界计算机里边去运转。那这台计算机。它是这么多个节点构成的一个网络,那它本身底层在在运行一段程序,运行一段智能合约的时候,它到底是什么样的一个东西在跑呢?这就是我们所说的以太坊虚拟机EVM,呃,简单来说,以太坊虚拟机是什么?它就是智能合约的一个运行环境。因为我们知道我们用编程语言写出来的程序的,其实它不是直接是机器可以读的,它至少都需要编译成,呃,就像我们的编译成自写码也好,或者说像其他的一些编译语言,像像C这样的编译语言,编编译成其他形式,然后再做连接,生成可执行程序,最后才能让机器所识别,那机器识别的时候,它也是需要一个运行式环境的,呃,这就就像大家熟悉,呃,有些同学可能比较熟悉啊,像Java Python,其实都是有虚拟机这样的一个结构的,对吧?但有一些可能没有虚拟机,但是它会涉及到更多的系统组件。
01:38
呃,像以太坊虚拟机呢,简单来说就是运行智能合约的环境。这是专门为以太坊这样一个世界计算机构建起来的一个底层结构,那作为区块验证协议的一部分,每个网络的节点都会运行EVM,所以说,呃,有些同学一开始了解这个EVM以太坊的时候,可能会问,那EVM它到底跑在哪呢?跑在哪台机器上呢?
02:08
我们如果要是起了一个全节点,起了一个真正的以太网节点,那em就会跑在我们的电脑上,就像我们用desk客户端去连接主网,去同步区块,这个时候我们就是运行EVM的。因为它是合约的运行环境嘛,如果我们上面不跑EPM的话,我们的合约没有办法发布上去,也没有办法调用执行,也没有办法验证别人的交易,所以大家可以认为就是说,呃,它的作用主要是什么?主要就是检查正在验证的块里边列出的交易,做交易验证。而且它可以运行由EVM当中的交易触发的代码,这就是我们说的一方面验证交易,另外一方面跑我们的智能合约,这就是EVM的一个作用。所以大家看,呃,我们平常认为的以太坊,其实某种意义上,狭义上可能就是EVM,对吧?它是执行着所有一切东西的一个核心。
03:14
呃,Em它是沙盒封装起来的,就是我们之所以之前对它都很陌生,都它对它根本都不了解,就是因为它完全封装起来了,在一个沙盒里面,我们从外部是没有办法去直接访问它的,呃,而且就是它不仅封装起来,完全隔离EVM里面运行的代码,是没有办法访问网络文件、系统和其他进程的。甚至智能合约之间的访问,就是在EVM里面,它也是受限的。所以这样的一个隔离的环境,其实就保证了我们整个系统的一个安全性,如果说em它本身在运行的过程当中还可以就是像我们真正的计算机一样,还可以连接到网络,接收外部请求,然后发送外部请求的话,那肯定就会受到外部的影响。
04:06
就不光仅仅是在我们自己的这个p two p网络里面做校验了,就有可能会受到外界的攻击,所以这也是他本身设计的一个一个原则啊。呃,咱们可以看到就是以太坊的这个设计,其实它似乎不是很有效率,就是像我们现在用到的计算机似乎没有这么干。我们的很多端口都是开放的,我们要上网,我们开放8080端口,对吧,然后我们如果要用FTP,我们开放二三端口,我们,呃,如果要要让大家连接我的这个VNC。呃,同样也是要开放对应的端口,所以我们如果要是更方便的跟外部世界去做互联,去做通信的话,其实最好的方式是不要隔离起来。但是。
05:00
以太坊,它是要把这些隔离起来的。而且我们知道,如果要是作为一个,就是所谓的。所谓的世界计算机这个概念其实也就是一个分布式架构,如果大家之前对云所谓的云计算有所了解的话,其实能够想到云计算不也是某种意义上的分布式计算,呃,也是某种意义上的世界计算机吗?而且云计算我们可以看到,它是把每个连进去的节点,每个云节点它的计算能力是叠加在一起去做处理的。而我们的以太法跟它不一样的是什么呢?它是每个节点都单独是这个世界计算机的一个,可以说是一个副本,就是我们的EVM都要在任何一个上面跑,而且跑一样的东西去校验交易去,就是由校易触发对应的代码去执行合约,那这个事情是不是很没有效率呢?
06:02
如果说要是我们的云计算这种架构的话,那其实大家可以计算不同的东西嘛,所以我们计算能力就增强了,我们可以直接获取到我们自己没有的计算能力,但是EVM这样的设计似乎不行,这是为什么呢?其实就是因为以太坊本身从一开始来讲,我们的区块链这样的系统,它的创建就不是为了优化效率的。大家可以看到比特币如果说作为一个现金系统,论论这个支付的速度,论交易确认的速度,比我们传统的交易确认比银行,呃,比比比,这个就是传统的这种支付体系其实是要差很多的,所以他最初的出现就不是要优化效率。所以他对这种计算的处理,并行计算的处理,分布式计算的处理也是做了冗余处理的,而不是说做这个分块把计算任务去下发。
07:02
所以这样的一种方式,它主要是要来达成系统的共识。也就是说,我们牺牲了一部分效率,用冗余的方式去让系统更加安全,去让大家在不同的位置能够达成一个共识,实现去中心化。在这里就是花这么多时间跟大家解释这个,也是让大家就是更更深的去理解一下,我们这样一个分布式系统跟之前经典意义上的分布式系统有什么不同,它们最主要的区别就在这里。从效率上其实是低下的。但是保证了去中心化和安全。呃,最后两个其实已经跟大家说过很多了,也很好理解,就是合约的形式,在区块链上存在的形式是自己码,这也就是说,呃,我们为什么需要一个编译器,我们编写的源代码是扫编写的。
08:00
所AD是一个高级语言,它要通过EVM编译成字节码。然后再把它部署到区块链网络当中去永久存储起来调用。执行的时候还是由发起交易,之后由EVM去触发这一部分代码逻辑。这就是EVM的一个整体的一个综述啊,那当然字节码格式大家都清楚,它是二进制的,所以是机器能直接识别的格式。好,这里就再来说一下EVM和账户,因为账户我们都已经讲过了啊,大家讲的很详细,大家应该很清楚了,那以太坊里边EVM和账户有什么关系呢?因为呃,以太坊里边的两类账户,外部账户和合约账户,或者说内部账户,他们共用的是同一个地址空间。也就是换句话说,对于EVM来讲,这两类账户没有任何区别。
09:04
它处理方式完全一样,所以这也就是为什么我们说就是呃,所谓的合约,它实实实际上事实上是一个账户。它的整个处理逻辑是统一化的,归一化。另外有一个需要说的就是每个账户在EVM里面都有一个建制,对形式的初九,呃,持久化存储,这个大家都已经知道了,呃,那其中的K和value长度都是256位,这个存储空间,呃在因以太坊的定义里面就把它叫做存储,或者叫存储空间,就是英文就叫storage。之后讲到的时候,这个也是一个呃,可能会比较难理解,或者说是呃比较高级的一个一个概念,所以这里先跟大家提一句。因为在以太坊里面,它可以有不同存储数据的地方。
10:01
那存储空间这个大家可以直观的认为这是一个持久化的存储空间,所以存储到这个上面就代表着要发送交易,要让我们的数据写在永久的写在区块链上。所以这个这个存储空间调用是要耗费资源的,是消消耗大量该死的,所以大家要注意这个东西。这在以后我们可能就是对自己写的合约,如果大家有进阶的需求,想要去优化它的性能,想要让它消耗的gas更少一点,呃,这是必须要掌握的一部分内容。接下来我们再说一下EVM和交易,呃,交易我们也做了一个很详细的一个解释啊,交易其实就可以看作一个,一个账户发送到另一个账户,主要是普通账户,EOA发送到任何一个账户,可以是普通账户,也可以是合约账户。的这样的一个消息数据,它可以包含,包含首先可以包含一个余,就是以太币的数值,我要转多少币,其次还可以包含二进制的数据负载,就是我们所说的payload,大家都已经看到了,就是如果要调用合约的话,它前面有函数选择器,后面有函数参数啊。
11:18
这1CQ为什么一定会在这里?好,呃,那如果说目标账户还有代代码的话。它只要按照我们之前讲过的那种形式,函数选择题和函数参数拼接在一起的32字节的这样一个一个形式传递给EVM,那么EVM就会。根据预设的格式把它解析出来。那么这个代码就会在EVM里面执行,所以前面我们已经讲了,函数在调用合约的时候到底发送的是什么,我们现在就比较好理解了,对吧?就是他发送过了那个固定格式的数据之后,E一看,哦,你发的这个东西,那我知道了,你是要做合约调用,前面那部分我解析出来,这就是一个一个函数选择题,我就知道你要调用哪个合约的哪个函数。
12:13
那后面呢?就可以作为他的入参参数解析出来,然后传到我们要调用的合约里面,这整个这就是合约的调用交易的一个很重要的跟EVM的交互,就是要调用我们以太坊上的合约。另外还有一个就是我们说的零账户了,如果目标账户是零账户,那交易就将创建一个新合约,所以这个时候我们知道传递的数据是什么呢?它的纰漏是什么呢?就是我们新创建那一个合约的字节码,对吧?大家还能回想起来吗?这是我们之前讲过的内容啊。那么所有的这些自解码就作为这个交易的payload就会就会直接存储到我们的区块链上去,直行的输出就作为合约代码永久存储,这就是整个交易的过程跟EVM的一个交互,其实这就跟我们前面讲的这个账户和交易都都接起来了啊,就是他们的下一步就交给EVM去处理了。
13:22
那接下来我们再说一下和guess的关系,我们一直在说就是盖消耗,盖可以作为就是手续费的一个计算的依据,盖消耗的是系统资源,那它消耗的是谁的资源呢?其实消耗的就是EVM的资源。对吧,所以合约被交易触发调用的时候,指令它会在全全网的每个节点上,每个节点的EVM上执行,这就需要消耗算力成本,所以呢,每一个指令它执行都有,都有对应的消耗的一个量盖,是用来量化EVM的这个消耗成本的。
14:03
另外就是说一经创建的时候,每一笔交易它都是按照一定的GA来预付费用的,这就是我们所说的GA里,它是为了限制交易所执行的这个工作量,不要陷入死循环,不要做无限。复杂的操作,另外就是还可以为交易支付手续费,这都是我们已经讲过,已经清楚的东西啊,接下来的还有一点是EVM执行交易的时候呢,该他就按照特定的规则逐渐消耗性,大家注意这个逐渐的意思啊,其实就。EVM消耗death的规则是什么呢?它就是真正的按照我执行完一个指令之后,我要扣掉多少S。所以在这个过程,他不是说我一下子执行完,然后才去发现,诶你这个盖好像不够用啊,我我说你这个失败,那相当于他不是还是浪费了这么多算力嘛,还是浪费了这么多的资源嘛,而且我们也前面说过,呃,你既然整个以太坊是图灵完备的,你有可能构建出死循环来,那假如说我都不知道你运行到哪里,我就先要把你运行完才能算盖消耗的话,那这个就肯定不靠谱了,那那就一定会陷入死循环的状态,那就那就完蛋,所以整个EVM的机制就是说我执行一步操作就会扣掉一定的death。
15:28
执行一步扣掉一步,那如果要是执行到某个位置的时候,盖直接消耗掉,消耗到零,或者说直接在为负了。Guess耗尽了。那我盖是汽油嘛,那那直接没油了,没油了怎么办呢。对,那车就要停,那EVM就要停下,所以这个时候就直接会触发一个out of gas异常没油了。所以呃,在这种情况下,这这里有一个很专业的词,这个大家不需要太详细去了解啊,就有一个call frame,就是调调用帧的一个概念,就是说相当于就是说,呃,大家可以理解成就是当前执行的这个环境,当前执行的这个上下文,他就要把当前的这个所做的状态修改全部回滚回去。
16:21
所以这就是我们整个death消耗和假如不够的情况下的一个处理模式。大家如果还有印象的话,可以可以想起来,就是咱们上一次做过的有一个实验性质的操作,就是我们交易失败的时候,同时还转一笔仪态。交易失败我们知道是因为我们代码里边的限制没有满足,结果他交易失败了,那交易失败的时候,大家发现那个以太最后转过去了吗。转过去了吗?转过去了是吗?有有印象吗?那那回头我们到时候再来,再来试验一下啊,正常情况下应该是没有转过去的,就是说当它里边的代码,当然我们那个不是auTo Ga啊就是。
17:11
如果要是发生这种异常的状况的时候,发生这种异常回滚的时候,是整个交易要回滚的。呃,所以这种状态下,正常的修改是都要被回国啊,这是EVM和盖的一个关系,对。就是这个GA是不会,即使你这个auTo Ga,你你是那个回滚了,交易回滚了,但是盖还是消耗,所以大家就会发现你发送一个失败的交易,你的钱可能没有扣掉,但是你的guess照样扣掉的,所以这部分盖是不会给。退回去,已经消耗的该不会退回去,这其实这个还是很好理解的,就是就像呃,大家就是平常坐车,平常呃有我看到有一个有一个比喻啊,就是当然就是说E的这个设计,可能就是导致我们跟现实当中的很多情况它有一点联系,但是又不是那么贴合的比喻。
18:14
我看到过一个比喻,怎么比喻这个盖这件事情呢?就是大家可以想象成一个一个很很特别的出租车的模式,它这个出租车的模式是什么呢?是。乘客。就是他的,首先他的司机,他不知道你要去的那个地方有多远,司机都是都是凶他,他他就对这个路很不熟,那怎么办呢?你不能说无限远的地方都要去啊,就是乘客每次上来的时候。就要指定说我耗多少油,然后我要去哪里,你去吧。那这个司机的做法就是什么呢?好,你告诉我了,耗多少油,我就耗这么多油,到了地方,如果这些油耗完了没到地,没到你要的目的地,那不好意思,你下车。
19:11
这有点像这样一个一个很奇怪的一个机制。这就是因为司机不知道目的地在在哪,他不知道有多远,所以说你得先给我提供一个保证,要不然我这边一直烧着我的油,我不知道烧到什么时候是个头啊。呃,那另外还有一个概念就是GA price,大家知道GA price是相当于盖的一个单价。如果说交易执行完了之后还会有剩余的话,咱们咱们知道这个盖是会退回来的,对吧,那就相当于你你预先。乘客上来的时候,你指定要消耗那么多油,你就得先把这么多钱先给我放在这里,那这个钱给多少呢?这个油钱的单价是我们自己商量好的,不是统一的油价。
20:00
商量好这个钱之后,你先拍在这儿作为一个押金,如果到了目的地,你那些油没完全的用完,那剩下的那部分那部分钱我再给你退回来。所以它整个的模式是这样的,就就有有一点绕的一个模式。好,接下来我们就来看一下EVM的数据存储,前面我们已经说到了storage这个数据存储,大家可以看到,呃,主要DVM里面的数据存储在这样的三个地方。一个叫做storage。一个叫做memory,还有一个叫step。Memory和step其实比较好理解啊,Memory就相当于是我们平常所说的内存了,Step就是占,呃,大家学计算机相关的知识理论肯定这些都都接触过,那storage就比较比较不好理解了,那它是个什么东西呢?它其实就是一块持续化的存储空间,甚至大家可以打一个不太精确的比喻,大家可以认为它是一个一个硬盘存储,或者说它是一个就是持久化的数据库存储存储,但可以认为它是MYSQL1类的东西。
21:17
但是这个不不是很贴切啊,但是大家可以这么认为,我们说整个区块链系统是一个分布式的数据库,它有存储的这一部分,主要说的就是storage,在以太坊上的话,主要说的就是storage这一部分。所以这是真正持久化的数据库。呃,那它主要的这个数据结构是什么呢?就是我们前面所说的,它是一个KY6的一个建设,对的一个存储区,它是256位次,映射到256位字,就是256位的K 256位的value。是它的最大长度,它是这样的一个映射关系,所以它直观的理解就是我们合约或者说整个以太坊上的一个数据库,它的特点就是永久存储,只要放在里面的东西,所有人都能够看到,而且会永久的保存下来。
22:12
所以我们知道合约里边不是有一个balance,有一个余额,然后还有自己的代码和存储空间吗?那个所说的存储空间就是这里,就是这个storage。所以在这个storage里面,它会永久保存合约的所有的状态变量,我们自己定义好的那些东西全部都会存在这里。所以他读写的GA消耗是非常非常大的。呃,在这里,呃,再跟大家多说一句,就是storage本身它不是一个建值对的存储区域嘛,K6嘛。那大家一想的话,这个就跟我们学过的,比如说像map这样的数据格式很像,对吧?呃,或者说就是你甚至大家认为它就是一个大对象,一个一个一个K,一个value这样去存,或者它是一个哈希表,大家可能都会有这样的印象,就觉得它是这样的一个东西。
23:08
但是这里要跟大家明确说明的是,他跟我们习惯意义上的那些哈希表呀,或者map呀,还不是一回事。如果我们在别的编程语言里边,Java Python啊,或者呃呃,像像其他有一些我不是很熟悉的编程语言,但是肯定都能知道。它们里边的像映射这样的结构,像k value的存储这样的结构,它一定是可以便利它的K的,对吧。你这样Java里面呢,就是直接一个一个一个这个map的key.key你就可以拿出所有的key对吧?点values就可以直接拿出所有的value。这件事情在以太坊上做不到。为什么呢?因为整个的storage在以太坊上是一个非常庞大的。
24:00
存储空间。庞大到什么程度?我们没有办法去便利。所以事实上,Storage的。这个存储方式它是,当然它本身也应该是一个哈希表,本质还是一个哈希表,但是它的哈希表是又做了一重哈希之后的哈希表。所以这个可能会相对复杂一点,我们如果要去,就是从单纯它最后生成的那个哈希表上去便利所有的K的话,这要消耗极大的系统资源,这个复杂度是非常非常高的,所以也就几乎是不可能的。所以大家要注意了,就是我们在storage上去读写的时候,本身它就很耗资源,要消耗大量的guess,而如果我们想要去让他去做一个便利查询的话,这几乎就是不可能的。所以说呃,之后我们在真正编写智能合约的时候一定要注意这一点。接下来另外一部分就是memory memory就是我们所说的内存了,那内存其实简单的理解就是就像我们电脑里边的RA这样的东西一样。
25:11
他的操操作的速度会比较快。但是。它也会比较小,大家知道在在我们的电脑里面肯定就是数据库,这硬盘那肯定大了,存储空间大,但是内存memory这个就就会比较小了,它的成本也会比较高,所以大家可以看到它的行为跟我们电脑的内存也都是非常非常接近,非常小的。每一次消息调用合约就会临时获得一块干净的,就是清空了的,完全初始化好的一块内存空间。这个就用来。处理一些临时变量,还有系统的就是函数的传入的参数和它返回的参数都会放在这个干净的内存空间里面去。它的生命周期就是整个方法执行的期间,简单说就是函数调用过程当中,它的生命周期就是这个函数内部。
26:05
函数调用结束之后,系统就回收了。因为它只是保存这个临时变量,所以读写的盖开销就会比较小。呃,这是memory的一个一个特点,那如果再讲的细一点的就是memory本身,它不是像storage那样,就是就开始直接哈希到很大的一个空间里边,你要便利,不可能它是线性的。可以按字节去寻址的一个存储空间。所以呃,在memory的里边,我们是可以去扩容的,就是直接我们让他再去线性的增扩大一一些,这个,这个就是我们要存储的这个空间,但这个也是需要消耗的。随着内存memory使用量的增长,它的GA的消耗费用也会增长的比较快,而且这个数量级它是用平方的这种方式去增长,所以大家也要注意使用内存的时候不要消耗太大。
27:08
这当然涉及到编程的一些很底层很细节的地方啊,相对可能也会比较枯燥一点,先先跟大家就是有一个概念吧,最后就是STEM战,那战这一部分呢,就是呃,EVM,它我们知道站有一个特点就是后进先出,对吧,压站的时候先到的压在底下,后到的放在上面,所以这个是非常适合一层一层的这种函数调用和一步一步的这种这种计算的,我们计算器的实现不就是最容易的,就是用一个呃站结构嘛,对吧,把每一步的计算结果压进站,然后一步一步弹出来,所以em一样,它的做计算的这个临时变量,还有一些很很多这种函数调用的参数都都会放到,呃,不是说函数函数调用的传数参数啊,而是说就是函数的整个的这个引用上下文的这个指针会放到我们的站里面去。
28:08
这个也就大家跟之前的学过的一些计算机知识相同的都是差不多的啊,这就是EVM不是基于寄存器,基于站。呃,这一部分内容呢,它比就就更小了,比比memory就更小了,它可以说是就是有点像我们计算机里边的寄存器。我们计算机里边不是寄存器内存,然后硬盘嘛,对吧?呃,一样这个关系大家可以理解成这样的关系,它的读写速度就会更快,呃,它只是存放部分值类型的变量,几乎是免费使用,所以呃,它的数量也是非常有限的,但不会不会有太大的存储空间,我们如果要存比较大的东西的话,也是放不到这里面的,这就是em数据存储这一块,呃,我们之后再讲solid的时候,会分成两部分来讲。
29:03
就是今天要讲的一部分,可能相对比较基础,就是大家主要是上手去多多跑一跑,多写一点这个简单的智能合约,然后我们带着大家一起分析分析这样的一个状况,那明天我预计啊,就是会给大家把扫的一些底层或者说深入的一些东西给大家也做一个讲解。呃,这个我觉得大家就像今天的EVM,还有就是明天有可能会给大家讲的扫的一些底层的东西。我觉得不要求大家全都明白,就是不要求大家对这个就是特别特别了解,什么东西都都搞得清清楚楚,我们从应用的角度来讲,这个也也没必要,但我觉得是,呃,大家就是有必要去去知道一个概念,就是知道知道一些了解,或者就是说如果要是学有余力的同学感兴趣,可以把这方面钻的再深一点。
我来说两句