区块链 2.0


以太坊概述

  1. 更改出块时间为15s,并使用一套相适应的GHOST协议(该协议非以太坊发明,只是改进)
  2. 更改mining puzzle,变成memory-hard mining puzzle,一定程度上ASIC-resistant
  3. 将来会将工作量证明Proof of Work替换成权益证明Proof of Stake,按照类似股份投票的方法决定下一区块如何产生
  4. 智能合约(Smart Contract):比特币实现货币的去中心化,智能合约实现合同的去中心化,从代码层面保证一开始就不会违约
  5. 以太坊block header中包含三个哈希值,分别对应状态树、交易树、收据树的根哈希
  6. 以太币没有矿工费定期减半的规定

以太坊账户

  1. 不同于比特币基于交易的账本,以太坊是基于账户的
  2. 分类:
    • 外部账户(Externally Owned Account):包含账户余额(balance)及计数器(nonce)
    • 智能合约账户(Smart Contract Account):包含账户余额(balance)、计数器(nonce)、代码(code,不变)和存储(storage,数据结构为MPT)
  3. 原因:对智能合约的支持要求账户、身份的稳定性
  4. 基于账户的账本天然地对双花攻击有抵抗性,但是却多了replay attack的风险
    • 双花攻击:转账者是恶意节点,将手里的一笔钱花两次
    • 重放攻击:收款人是恶意节点,将转账者的钱转两次到自己账上
    • 重入攻击:利用智能合约中的安全漏洞,将合约中的余额多次转到黑客账户上

状态树

  1. 目的:实现账户地址到账户状态的映射(addr为160bit,40个16进制数)
  2. 数据结构
    • 如果直接采用哈希表,那么如何签名?将整个哈希表做成merkel tree再取哈希代价太大,也不好修改
    • 如果采用merkel tree,那么叶节点必须有序,否则各个区块链节点产生的根哈希不同。即使采用sorted Merkel Tree,插入新账户时,可能插在中间,那么大半棵树就要重新生成,代价太大。
    • 前缀树(Trie):如字典单词go、god、genesis、generate
    • 压缩前缀树(PT)(键值分布稀疏时,压缩效果较好)
    • 以太坊采用的是(Modified )Merkel Patricia Trie(MPT):增加了Extension Node、Branch Node,用哈希指针代替普通PT中的指针
    • 状态树中MPT是共享的,只有实际发生变更的节点需要新建分支
    • 账户以key-value的形式存储在MPT中,key为账户hash,value以RLP(Recursive Length Prefix,feature:极简) 编码作序列化后存储
  3. 为什么状态树中不能只包含最近发生交易的账户?主要是不方便查询。e.g. A → B(10ETH),需要沿着链查找A、B账户余额。如果B为新账户,就需要一直搜索到创世纪块,效率太低

交易树&收据树

  1. 交易树包含这段时间内发布的新交易,与比特币的Merkel Tree类似;收据树包含所有交易执行的结果,主要是为了便于查找,特别是对于智能合约。
  2. 对比:状态树包含所有账户,会大量引用之前的MPT;交易树和状态树只包含新产生的交易。
  3. 交易树和收据树都采用MPT,可能只是为了代码的统一。此外,MPT支持自顶向下查找。
  4. bloom filter
    • 支持比较高效地查找某笔交易是否在某个集合里
    • 根据收据树的log生成。通过哈希函数对收据树中每笔交易进行处理,然后将哈希表中相应位置置1(初值0)
    • 可能发生哈希碰撞,出现false positive。查询某笔交易时,hash后查询哈希表,若相应位置为0,说明该笔交易不存在;若为1,则可能存在,也可能不存在(只是跟其他交易发生了哈希碰撞)
    • 由于哈希碰撞的存在,一般不支持删除操作。除非将哈希表元素改为计数器,但这会使数据结构复杂化
    • 查找一段时间内某笔交易是否存在:先根据每个block header里的bloom filter过滤掉一定不存在该交易的区块,在从剩余block里仔细查找交易

GHOST协议

  1. 以太坊出块时间15s,比较短。容易出现多个分叉。如果继续采用BTC中的方式,对产生分叉区块的节点不公平。
  2. 最初的GHOST协议
    • 每个区块可以包含两个上一代的叔父区块
    • 被包含的uncle block获得7/8的出块奖励(block reward),新区块每包含一个uncle block,获得出块奖励的1/32
    • 未被包含的uncle block没有奖励
    • 叔父区块上的交易作废,无法得到矿工费(gas fee)
    • 缺点:① uncle block个数大于2时,部分未能被包含 ②新节点故意不包含某个叔父区块
  3. 改进后的GHOST协议
    • 新区块可以包含之多7代以内的叔父区块,最多6代叔父。uncle reward随代数递减7/8、6/8……2/8。以此鼓励叔父区块尽早合并
    • 叔父区块分支上只有第一个区块能获得奖励,否则forking attack代价就太小了

挖矿算法

  1. ASIC resistance —— memory hard mining puzzle

  2. ASIC芯片算力强,但在内存访问速率上和普通芯片差距不大

  3. 莱特币(LiteCoin):追求ASIC/GPU Resistance。mining puzzle采用Scrypt,通过产生一个大的哈希表,同时算法中涉及到伪随机(后一个元素根据前一个元素生成),以此增大对内存的要求。但对轻节点不友好,所以LiteCoin将内存要求设为128k。但128k内存对GPU和ASIC的限制不大,后期GPU和ASIC挖矿均出现了

  4. 以太坊挖矿算法(ethash)用一大一小两个数据集——16M Cache和 1G dataset(DAG)(每3w区块,对seed取hash生成新seed,数据集大小增大初始大小的1/128,即128k和8M,以适应内存发展)

    • cache中第一个元素由seed取hash得到;后一个元素由前一个元素取hash得到

    • dataset中第i个元素从cache中取256次元素计算得到。dataset中的元素彼此独立,可以分别根据序号 i 和 cache 计算得到

    • 对于每个nonce,根据header和nonce取hash,然后以伪随机的方法从dataset中取128个数(64次,每次2个数),最终得到一个hash。如最终的hash<=target,则满足要求

    • 因为矿工需要验证非常多nonce,如果每次都从cache生成,效率太低,所以需要保存到内存。而轻节点只需验证一个nonce,所以只需根据cache计算即可

      eth

权益证明

智能合约

组成

  1. 智能合约是比特币和以太坊的最大区别
  2. 智能合约时运行在区块链上的一段代码,代码的逻辑定义了合约的内容
  3. 智能合约账户保存了智能合约当前的运行状态,包括:balance、nonce、code、storage
  4. 智能合约一般用solidity编写,语法和JavaScript类似
  5. 要运行函数能够接受外部转账,需要写出payable
  6. fallback()函数:
    • function() public [payable] { ... }
    • 匿名函数,没有参数也没有返回值
    • 两种情况会调用:
      • 直接向合约地址转账而data域为空
      • 调用的函数不存在
    • 一般payable是需要的。否则转账金额不为零时抛出异常

调用流程

  1. 外部账户调用智能合约流程:创建一个交易,接收地址为要调用的智能合约账户地址,data域中填写要调用的函数及其参数的编码值
  2. 内部调用(一个智能合约调用另一个智能合约的函数):
    • 直接调用:直接在合约B中使用另一个合约A的成员函数。但若调用的函数在执行中抛出异常,则B也会抛出异常。可以通过通过.gas()和.value()调整提供的gas或ETH
    • 使用call()调用:在合约B中使用call(要调用的函数签名, 参数)调用函数,若调用的函数抛出异常,则call()返回false,否则为true。合约B不会抛出异常,而会正常执行。可以通过通过.gas()和.value()调整提供的gas或ETH
    • 使用delegatecall():与call()类似,但不会切换上下文(余额、存储等),只会使用到调用的函数代码。不能通过.value()调整提供的gas或ETH

智能合约创建&运行

  1. 智能合约代码写完后,需要编译成bytecode
  2. 创建合约:外部账户发起一个转账交易到0x0地址
    • 转账金额为0,但需支付汽油费
    • 合约代码写在data域里
  3. 智能合约运行在EVM(Ethereum Virtual Machine),主要是为了增加可移植性
  4. 以太坊是一个交易驱动的状态机
    • 调用智能合约的交易发布到区块链上后,每个矿工都会执行这个交易,从当前状态确定性的转移到下一个状态
  5. 汽油费:
    • 智能合约是一个图灵完备的模型
      • 出现死循环怎么办?
    • 执行合约中的指令需要支付汽油费,汽油费由交易发起者支付。矿工一次性扣除price*gasLimit,再多退少补
    • EVM中不同指令消耗的汽油费是不一样的——简单指令便宜,复杂指令贵

错误处理

  1. 以太坊中的交易具有原子性

  2. 智能合约中没有try-catch结构,但出现异常时,会发生回滚,恢复到交易前的状态

  3. 可以抛出错误的语句:

    • assert:条件不满足则抛出,一般用于内部错误
    • require:条件不满足则抛出,一般用于外部错误
    • revert:无条件抛出,终止运行并回滚
  4. 嵌套调用(一个合约调用另一个合约中的函数)时,如果是直接调用,则调用者和被调用的函数都会连锁式回滚;如果是间接调用,比如用call(),则只会返回false,调用者不会回滚

  5. 一个合约向另一个合约账户中转账,仍可以发生嵌套调用(fallback函数)

  6. 地址类型
    011

  7. 智能合约可以获得的调用信息02

  8. 以太坊中先运行合约再挖矿,还是先挖矿再计算合约?答案:先运行合约,否则三个根哈希值不确定

  9. 每个节点都需要独立验证合约合法性

  10. 某个节点能否不进行验证,直接接受区块?-- 不行,不独立验证的话,本地的三个根哈希值无法更新

  11. 不支持多线程,因为产生的结果可能具有不确定性,其他节点无法验证。随机数同理,只能支持伪随机数。

  12. 合约代码在发布到区块链之前必须经过仔细的测试,否则一旦上链,即使是bug也无法修改

DAO

  1. DAO: Decentralized Autonomous Organization
  2. DAC: Decentralized Autonomous Corporation
  3. The DAO: 利于DAO理念的一个投资基金,由于受黑客重入攻击,只存活了3个月

反思

  1. Smart contract is anything but smart.
  2. Irrevocability is a double-edged sword.
    • 不可篡改性保证了合约的公正性
    • 当区块链中存在问题时,难以修改
  3. Nothing is revocable.
    • 分叉攻击
    • 比如The DAO事件中,以太坊团队通过软件升级回退交易
  4. Is solidity is the right programming language?
  5. What does decentralization mean? 存在分叉恰恰是一种民主的体现
  6. decentralized ≠ distributed
  7. 去中心化不一定是好的。The business model is bad, then it is bad in the internet.