区块链学习(二)以太坊详解
in Blog on Blockchain
以太坊相关知识
2.1 基本概念
2.1.1 以太坊基础定义
- 定义:以太坊的本质就是一个基于交易的状态机
- 本质流程:以太坊的状态有百万个交易。这些交易都被“组团”到一个区块中。一个区块包含了一系列的交易,每个区块都与它的前一个区块链接起来。只有交易全是有效的情况下,才能让一个状态转换为下一个状态。
- 运行过程:存在交易->矿工创建一个包含有效交易的区块->其他节点验证区块->证实矿工得到代币奖赏
2.1.2 GHOST协议
- 定义:GHOST = Greedy Heaviest Observed Subtree 贪婪最重可见子树协议
- 产生原因:为了确定哪个路径才是最有效的以及防止多条链的产生
- 工作原理:如果在区块A后面出现了多个分叉,假设区块B是最先发布的,那么区块B很可能成为主链,其他节点收到广播后就在区块B之后开始挖矿,假设后面区块A_a挖到矿了,此时区块A_a可以接纳其叔父块A1、A2、A3,也就是给点好处给他们,让他们不再继续在自己下面挖了,来区块A_a后面挖,根据这样的原理,就促使大家都在主链上延续下去,避免出现分叉而导致的问题。
2.2 以太坊组成部分
- 账户(accounts)
- 状态(state)
- 损耗和费用(gas and fees)
- 交易(transactions)
- 区块(blocks)
- 交易执行(transaction execution)
- 挖矿(mining)
- 工作量证明(proof of work)
2.2.1 账户
- 定义:以太坊的全局“共享状态”是有很多小对象(账户)来组成的,可以通过消息传递架构来与对方进行交互
- 分类:
- 外部拥有的账户,被私钥控制且没有任何代码与之关联
- 合约账户,被它们的合约代码控制且有代码与之关联
- 区别:
- 一个外部拥有账户可以通过创建和用自己的私钥来对交易进行签名,来发送消息给另一个外部拥有账户或合约账户。在两个外部拥有账户之间传送的消息只是一个简单的价值转移。但是从外部拥有账户到合约账户的消息会激活合约账户的代码,允许它执行各种动作。(比如转移代币,写入内部存储,挖出一个新代币,执行一些运算,创建一个新的合约等等)
- 合约账户只有在接收到一个交易之后(从一个外部拥有账户或另一个合约账户接),为了响应此交易而触发一个交易
- 格式:每个账户都有一个与之关联的状态(state)和一个20字节的地址(address)。在以太坊中一个地址是160位的标识符,用来识别账户的
2.2.2 状态
- 账户状态
- nonce:如果账户是一个外部拥有账户,nonce代表从此账户地址发送的交易序号。如果账户是一个合约账户,nonce代表此账户创建的合约序号
- balance: 此地址拥有Wei的数量。1Ether=10^18Wei
- storageRoot: Merkle Patricia树的根节点Hash值(我们后面在解释Merkle树)。Merkle树会将此账户存储内容的Hash值进行编码,默认是空值
- codeHash:此账户EVM(以太坊虚拟机,后面细说)代码的hash值。对于合约账户,就是被Hash的代码并作为codeHash保存。对于外部拥有账户,codeHash域是一个空字符串的Hash值
- 世界状态
- 以太坊的全局状态就是由账户地址和账户状态的一个映射组成。这个映射被保存在一个叫做Merkle Patricia树的数据结构中,而任何节点想要验证一些数据都可以通过Merkle证明来进行验证
- 证明所需:
- 需要验证的数据
- 树的根节点Hash
- 一条“分支”路径上的所有Hash值
- Merkle树优点:该结构的根节点加密取决于存储在树中的数据,而且根据点的hash还可以作为该数据的安全标识。由于块的头包含了状态、交易、收据树的根hash,所有任何节点都可以验证以太坊的一小部分状态而不用保存整个状态,这整个状态的的大小可能是非常大的
2.2.3 gas和费用
- 为什么需要费用: 以太坊可以运作的一个重要方面就是每个网络执行的操作同时也被全节点所影响。然而,计算的操作在以太坊虚拟机上是非常昂贵的。因此,以太坊智能合约最好是用来执行最简单的任务,比如运行一个简单的业务逻辑或者验证签名和其他密码对象,而不是用于复杂的操作,比如文件存储,电子邮件,或机器学习,这些会给网络造成压力。施加费用防止用户使网络超负荷。 以太坊是一个图灵完备语言(短而言之,图灵机器就是一个可以模拟任何电脑算法的机器。对于图灵机器不太熟悉的人可以看看这个 和这个 )。这就允许有循环,并使以太坊受到停机问题 的影响,这个问题让你无法确定程序是否无限制的运行。如果没有费用的话,恶意的执行者通过执行一个包含无限循环的交易就可以很容易的让网络瘫痪而不会产生任何反响。因此,费用保护网络不受蓄意攻击
- gas:gas就是用来衡量在一个具体计算中要求的费用单位
- gas price:就是你愿意在每个gas上花费Ether的数量,以“gwei”进行衡量。“Wei”是Ether的最小单位,1Ether表示10^18Wei. 1gwei是1,000,000,000 Wei,通常有公式gas limit*gas price=max trasaction fee
- gas limit:代表用户愿意花费在gas上的钱的最大值。如果在他们的账户余额中有足够的Ether来支付这个最大值费用,那么就没问题。在交易结束时任何未使用的gas都会被返回给发送者,以原始费率兑换
2.2.4 交易和消息
- 概念,一个交易就是被外部拥有账户生成的加密签名的一段指令,序列化,然后提交给区块链(交易是外部世界和以太坊内部状态的桥梁)
- 类型:消息通信和合约创建(也就是交易产生一个新的以太坊合约)。
- 组成:
- nonce:发送者发送交易数的计数
- gasPrice:发送者愿意支付执行交易所需的每个gas的Wei数量
- gasLimit:发送者愿意为执行交易支付gas数量的最大值。这个数量被设置之后在任何计算完成之前就会被提前扣掉
- to:接收者的地址。在合约创建交易中,合约账户的地址还没有存在,所以值先空着
- value:从发送者转移到接收者的Wei数量。在合约创建交易中,value作为新建合约账户的开始余额
- v,r,s:用于产生标识交易发生着的签名
- init(只有在合约创建交易中存在):用来初始化新合约账户的EVM代码片段。init值会执行一次,然后就会被丢弃。当init第一次执行的时候,它返回一个账户代码体,也就是永久与合约账户关联的一段代码。
- data(可选域,只有在消息通信中存在):消息通话中的输入数据(也就是参数)。例如,如果智能合约就是一个域名注册服务,那么调用合约可能就会期待输入域例如域名和IP地址
2.2.5 区块
- 包含:
- 区块头
- 关于包含在此区块中交易集的信息
- 与当前块的ommers相关的一系列其他区块头(ommer:叔块)
- 区块头:
- parentHash:父区块头的Hash值(这也是使得区块变成区块链的原因)
- ommerHash:当前区块ommers列表的Hash值
- beneficiary:接收挖此区块费用的账户地址
- stateRoot:状态树根节点的Hash值(回忆一下我们之前所说的保存在头中的状态树以及它使得轻客户端认证任何关于状态的事情都变得非常简单)
- transactionsRoot:包含此区块所列的所有交易的树的根节点Hash值
- receiptsRoot:包含此区块所列的所有交易收据的树的根节点Hash值
- logsBloom:由日志信息组成的一个Bloom过滤器 (数据结构)
- difficulty: 此区块的难度级别
- number:当前区块的计数(创世纪块的区块序号为0,对于每个后续区块,区块序号都增加1)
- gasLimit:每个区块的当前gas limit
- gasUsed: 此区块中交易所用的总gas量
- timestamp:此区块成立时的unix的时间戳
- extraData:与此区块相关的附加数据
- mixHash:一个Hash值,当与nonce组合时,证明此区块已经执行了足够的计算
- nonce:一个Hash值,当与mixHash组合时,证明此区块已经执行了足够的计算
注:斜体为树结构,此三个即为Merkle Patricia树结构
- 日志:账户地址;交易的相关数据
- 交易收据:
- 区块序号
- 区块Hash
- 交易Hash
- 当前交易使用了的gas
- 在当前交易执行完之后当前块使用的累计gas
- 执行当前交易时创建的日志
2.2.6 交易执行
- 判断交易是否有效:
- 交易必须是正确格式化的RLP:”RLP”代表Recursive Length Prefix,它是一种数据格式,用来编码二进制数据嵌套数组。以太坊就是使用RLP格式序列化对象。
- 有效的交易签名
- 有效的交易序号:回忆一下账户中的nonce就是从此账户发送出去交易的计数。如果有效,那么交易序号一定等于发送账户中的nonce。
- 交易的gas:limit 一定要等于或者大于交易使用的intrinsic gas,intrinsic
- 合约创建
- 设置nonce为0
- 如果发送者通过交易发送了一定量的Ether作为value,那么设置账户的余额为value
- 将存储设置为0
- 设置合约的codeHash为一个空字符串的Hash值
2.2.7 工作量证明
- 定义:即找到一个nonce值,使得新区块头的哈希值小于某个指定的值,即区块头结构中的“难度目标”。在节点成功找到满足的Hash值之后,会马上对全网进行广播打包区块,网络的节点收到广播打包区块,会立刻对其进行验证。
- 步骤:
- 生成Coinbase交易,并与其他所有准备打包进区块的交易组成交易列表,并生成默克尔哈希;
- 把默克尔哈希及其他相关字段组装成区块头,将区块头(Block Header)作为工作量证明的输入,区块头中包含了前一区块的哈希,区块头一共80字节数据;
- 不停地变更区块头中的随机数即nonce的数值,也就是暴力搜索,并对每次变更后的的区块头做双重SHA256运算,即SHA256(SHA256(Block_Header))),将结果值与当前网络的目标值做对比,如果小于目标值,则解题成功,工作量证明完成。