以太坊账户

对于以太坊中可能出现的replay attack对于账户中的每一笔交易都加上一个nonce值来记录这是第几次交易,然后将nonce值和交易一起进行签名,之后如果有人重放这笔交易,经过验证发现nonce值对应的交易已经执行过一次了,就不再执行了。所以全节点也应该保存每个节点的nonce值。

账户的概念有利于合约的执行。

  1. 外部账户(普通账户)
  • 与比特币中的账户差不多,通过公私钥对账户进行控制。
  • 账户状态:账户余额,nonce(计数器)
  1. 合约账户
  • 不是通过公私钥进行控制,不能发起交易,所有的交易只能由外部账户发起。
  • 可以被调用,调用其他合约。产生合约账户的时候会返回一个地址,调用地址来调用合约。
  • 合约账户的状态还包括code,storage。

ETH状态树

数据结构的选择:

  • 选择使用hash表?

    怎么提供merkle proof:如果使用hash表来构造merkle tree再把根hash放到区块头当中。如果是这样的话,每当发布一个新的区块,需要将所有的账户状态构成一颗新的merkle tree,但实际上每次发生状态变化的只有少部分账户。所以,简单的将所有的账户组成一颗merkle tree的方法代价有点大。而且难以保持区块链中全节点的一致性

  • 直接使用merkle tree?

    一个是不好查找和更新数据的状态。

    是否进行排序,如果不排序的话

    • 查找速度更慢
    • 难以保证构建的merkle tree的结构再区块链中是一致的。这样就会导致不同节点算出来的merkle tree的hash值不同。

    如果进行排序:

    • 数据的加入复杂度较高,如果插入一个数据,那么就有可能大半棵树需要重新计算hash值。

以太坊中选择的数据结构:

压缩前缀树

trie:

压缩后的trie:

这里的指针用的是hash指针


保存历史状态的必要性,在以太坊网络中,出现分叉的情况是十分常见的,而以太坊不像区块链中存储的是简单的交易,以太坊中的智能合约可以实现比较复杂的功能,所以要想通过代码分析来实现状态的回滚是比较困难的。

  • 状态中的值的存储是先经过序列化之后在进行存储,采用的是RLP方式(Recursive Length Profix)

ETH交易树与收据树

  • 交易树: 包含交易信息
  • 收据树: 对应每个交易都有一个收据信息,记录交易的相关信息,有利于查找相关交易的信息

两个树都采用的是MPT结构

bloom filter结构:

通过计算集合中元素的信息计算出hash值,将其对应到一个紧凑的digest当中,将对应的位置bit置为一。这种方式可以证明某个元素不在这个集合当中,但是不能说明某个元素在集合当中。因为会存在hash碰撞,有的采用采用多个hash算法来计算多个digest,减少hash碰撞的可能性。

块头里的bloom filter是下面bloom filter中的并集。

为什么用户状态不只保存与当前交易相关的用户状态

  • 如果有一笔交易,是A–>B,如果只保存部分用户状态,那么当你需要找B账户的状态时,因为只保存了部分用户状态,所以需要一直往前面寻找是否存在B账户,但如果B是一个新建账户,那么就要找到创世区块才能得到结果。

GHOST协议

如果继续使用比特币中的共识协议:

用为出块时间的缩短,所以出现临时性的分叉也会增加许多,当一个大型矿池挖到一个区块之后,他所在的区块会更有可能成为最长合法链,这也就意味着其他区块就白挖了。在比特币中个体用户相较于矿池来说,虽然在算力上同样不占有优势,但是比特币中的用户还是有可能会比矿池先挖到区块,而且挖到的这个区块绝大多数情况下是处于最长合法链上的(出块时间比较长,没那么容易就弄出分叉然后还更长。)

GHOST协议:

核心概念:uncle block

对于挖到了区块,但是没有成为最长合法链,这种区块被对于后面新加入最长合法链的区块来说,是它的叔父区块。如果最长合法链后面的区块包含了叔父区块,那么被包含的叔父区块会得到7/8的出块奖励,但是得不到gas fee,包含叔父区块的新区快会得到1/32的出块奖励。最多可以包含两个叔父区块。

叔父区块的定义最多只能隔着7代,而且随着代数的增加,叔父区块所获得的奖励会逐渐减少,包含他的哪个区块所得到的建立依旧是1/32。以太坊中的出块奖励并不会不断下调。

这样设计的意义:

  • 如果不规定叔父区块最多隔着几代,那么会使得全节点需要保存的很多叔父区块。
  • 鼓励尽早将叔父区块合并到最长合法链当中。

对于被包含的叔父区块:

  1. 交易并不执行,等到后面的最长合法链包含叔父区块中的交易的时候再执行
  2. 检查合法性,并不是检查交易的合法性,而是这个叔父区块是否满足难度要求。

叔父区块只能使分叉后的第一个区块,后续跟着的区块都不能在算作是叔父区块。

如果后续的区块也是叔父区块的话,那么会导致分叉攻击的失败代价太小了。

进行分叉攻击,如果我成功了,那么就可以实现交易回滚,就算失败了,也能够得到叔父区块的奖励。

而只认第一个区块为叔父区块,就可以使得分叉攻击失败的代价增加,从而促使分叉区块尽早合并。

ETH挖矿算法

memory-hard mining puzzle。以此来实现对asic芯片的不友好性。

litecoin的挖矿算法,scrypt

生成一个较大的数组,通过选择一个seed计算出一个hash值存放在数组的第一个位置,后续数组位置的hash值依次通过前一个数组元素的hash值计算出来。在计算puzzle的时候首先选取一个位置之后再由这个位置的hash值确定下一个要读取的位置。循环一定次数之后在配合nonce求解符合难度要求的nonce值。

加密货币的使用人数越少,越不安全。因为51%的算力比较容易达成。

以太坊中的挖矿算法(ethash):

以太坊中首先采取和litecoin中类似的方式,计算出一个16M的cache,之后再根据cache算出一个较大的DAG(cache和DAG的大小每隔一段时间会增大)。在seed中某一个数组位置开始,计算出一个hash之后由这个hash得到下一个要读取的位置,结合那个位置数组的值进行hash的更新,经过256轮更新迭代之后,将最终算出来的hash值填入DAG中的第一个位置。计算puzzle的过程为:根据block header和初始nonce值计算得到一个hash之后由这个hash所指向的位置获取一个位置,取出这个位置以及相邻位置的值,计算一个hash得到下一个位置,进行同样的操作更新hash值,循环64次得到最终hash值,与难度阈值进行比较,看是否符合难度要求,不符合则更改nonce值再次进行计算。

每隔30000个区块,seed的值会发生变化,cache的大小会增加初始大小的1/128,根据新的seed重新生成cache。

轻节点进行验证的时候,将所得到的区块中的header block和nonce一起计算出一个hash,由于轻节点没有保存,所以要临时生成DAG对应位置的值。(计算量略大,但是对于轻节点来说只要计算一个nonce值,但对于挖矿机来说,由于需要尝试的nonce值太多了,不保存DAG计算效率太低了)

Pos权益证明,不需要挖矿

使用ASIC芯片挖矿是安全的?:因为ASIC芯片是一种专门的挖矿芯片,如果使用ASIC芯片实现了51%的攻击,那么加密货币的安全性就被证明有问题,这样比特币的价格会下降,最后可能导致亏本,到时候买来的矿机又不能拿来做其他事情,就亏了。而如果普通用户机也能挖矿,那么发动攻击的成本就会降低,因为等到不需要挖矿的时候这些机器还可以用来干其他事情。

以太坊难度调整(此部分最好看代码)

https://www.bilibili.com/video/BV1Vt411X7JF” />权益证明

工作量证明的一个比较大的缺点是,耗电

以太坊中虽然还需要处理智能合约,但是出块时间短,耗电比比特币挖矿要低一些。

挖矿机制是通过算力的大小来决定获取收益的比例,而挖矿能力的大小又取决于投入的资金多少。所以权益证明的想法就是,直接通过投入区块链中资金的多少来决定收益的多少,而省略了挖矿这个步骤。

比特币中是通过算力来争取记账权,而以太坊中则是通过所投入的以太币的数量来争取记账权。就是记账权根据币龄来按概率分配记账权。不需要通过挖矿,发布区块之后同样获得出块奖励。以太币在设立之初预留了一部分的以太币用来给别人投资。

权益证明的优点:

维护以太币区块链的资源是一个闭环。

在比特币中维护区块链安全的资源来自比特币的外部环境,就是用来争取记账权的资源是可以通过外部环境进行获取的(使用加密货币之外的钱财,进而转换为加密货币中的竞争资源。用房地产的钱来买矿机)。比特币在世界经济的总市值比较小,如果有人愿意的话,它可以比较容易的凑到51%的算力。而在以太坊中,他如果想要发动51%攻击,那他就需要拥有以太币系统中51%的以太币,这也就意味着会有大量的以太币收购,这样以太币的价格也会随之上涨。

有些加密货币采取两种方式的结合:

既挖矿也进行权益证明,占有币比较多的挖矿难度对应降低。但是如果简单的这样进行设计会导致币多的人挖矿越来越容易,进而获得更多的币,然后挖矿又会更加容易。所以有些设计成挖到区块之后一段时间内币不能马上继续使用。

权益证明中存在的挑战:

两边下注:

就是当区块链出现分叉之后,可以同时在两边进行下注(权益证明需要交类似于保证金一样的东西来获得权益,为了防止节点打包违法的交易,如果打包了违法的交易,就会得不到出块奖励,而且保证金也会被拿走),这种两边下注的行为在工作量证明的情况中是会分散算力的,而在权益证明中则没有这种问题

Casper:区块链中准备使用的权益证明

在挖矿和权益证明的混合阶段,Validator(验证者)成为Validator的前提是要投入一定金额的保证金。其职责是推进系统达成共识。Validator投票来决定哪一条链成为最长合法链,2/3以上的票数才可以通过。

每50个区块为一个epoch在每一个epoch后进行一次投票。只有当连续两个epoch都有2/3以上的节点同意之后才可以确认。Validator可以从投票的这件事中得到奖励,不过得等一段时间,这段时间可以对Validator是否合法履行职责并对其做出处理。

智能合约

智能合约是一段运行在区块链上的一段代码,代码的逻辑定义了合约

solidity语言中的hash不支持遍历。

bid函数后面添加的payable表示这个函数接收外部转账

外部账户如何调用智能合约

调用合约与转账交易是类似的,如果转账对象是一个合约账户,也就意味着调用了这个合约,具体调用的那个函数会在data域中说明

一个合约如何调用另外一个合约

  • 直接调用

  • 使用address类型的call函数

  • 代理调用delegatecall函数

前面两个调用的区别在于,前一个如果调用的函数出错的话会导致调用的合约也跟着出错,但是使用call函数,如果调用失败,则会返回false

fallback函数(如果没有找到对应的函数就调用这个函数)

智能合约的创建和运行

汽油费(gas fee)

以太坊中会一次性扣除最大的汽油费,如果最后算出来需要的汽油费没有达到那个标准,会退回多收的汽油费,如果不够,则会导致回滚,而且收掉的汽油费不退回。(感觉区块链中一个防止恶意攻击的有效方法就是让一些攻击方法的失败付出较大的代价)

交易过程中如果出现任何错误,会导致整个交易回滚,就好像没有发生过这个交易一样。

一个发布的区块所容许的最大汽油费有限制,就是为了防止发布的区块过度消耗资源。类似于比特币中区块大小不能超过1M。

错误处理

revert()无条件抛出异常

嵌套调用

任何智能合约中对帐户的操作都是对本地所保存的状态树的操作,只有发布到区块链网络上之后才会被共识

先执行交易在进行挖矿,因为如果不先执行,状态树就没有办法确定,也就没有办法算出root值,就没有办法尝试nonce值。

如果有的矿工不对新发布的区块进行验证会怎么样?如果不进行验证就没法继续进行挖矿。因为验证的过程就是将发布的区块链中的内容再执行一遍,不执行的话后续继续挖矿所得到的状态就会与其他节点不一致而导致不被承认。另外一种做法是将别人执行完之后所得到的三棵树复制一份,这样的做法类似于矿池的做法。(直接从发布的区块上是得不到树中的内容的,只有一个hash在块头里)

发布到区块链上的交易并不一定都是成功执行的,因为如果不发布的话是没有办法扣掉汽油费的。

Receipt数据结构

智能合约可以获得的相关信息

三种转账方式:

  • transfer()会引起连锁回滚,给的汽油费很少
  • send()不会引起连锁回滚,给的汽油费很少
  • call.value()会将剩下的汽油费都发过去

智能合约如果设计不好,可能会导致存进去的钱取不出来。