Ethereum
世界状态 (world state)
Ether(以太币)记载在 世界状态(world state) 里
世界状态 = 每个人账户的状态的总集合
余额变化
Ether(以太币)余额因交易而变化
以太坊既可以看作静态的区块的串联, 也可以看作一个状态机, 随着用户不断地发起交易,促成这个状态机进行状态转移
账户状态
已执行交易总数
该值会随着用户不断发送交易而递增,保障用户发出的交易是按照顺序被收纳入最终的区块链。 因为在同一个账户中,已执行交易总数不可以在区块链中再次出现。 当用户创建智能合约时,要指定合约地址,该地址是由用户账户的已执行交易总数和用户账户地址联合计算而得出的。
代码区的哈希值
该值为 智能合约独有,外部账户不包含该值。 代码区即为智能合约代码本身。 在合约的生命周期中,该区域的内容是不可更改的 只读状态。 代码区存放于以太坊网络节点的硬盘中,当运行时被读入虚拟机执行。代码区的内容通过散列函数得出校验哈希值,该值即为代码区的哈希值。
公开地址从私钥派生逻辑
- 32字节私钥生成的长度为65字节的公钥
- 删除为首的一个字节0x04,还剩64字节
- 将其放入keccak256哈希算法,生成一个256位的哈希值
- 截取哈希值的最后20字节,既为所求的公开地址
- (可选)辅以0x的开头装饰,表明这是一个16进制的书面记录形式
EIP-55格式的账户地址
为了解决转账地址填错的问题,该提议按照一定逻辑,将地址中的部分字母大写,与剩余的小写字母来形成校验和,让地址拥有自校验的能力,具体的代码参考下方
const createKeccakHash = require('keccak')
function toChecksumAddress (address) {
address = address.toLowerCase().replace('0x', '')
var hash = createKeccakHash('keccak256').update(address).digest('hex')
var ret = '0x'
for (var i = 0; i < address.length; i++) {
if (parseInt(hash[i], 16) >= 8) {
ret += address[i].toUpperCase()
} else {
ret += address[i]
}
}
return ret
}
交易(Transaction)
交易活动步骤:
- 用户签名打包一条或多条消息,组装成一笔交易,发送到以太坊网络上
- 该交易被矿工捕获,并收纳入某一个区块
- 区块形成后,被矿工广播到节点网络中,最终加入区块链
交易与消息的区别
消息在账户与账户之间传递数据(data)与价值(value),消息的具体表现形式如下:
- 数据:一个账户向另一个账户请求函数调用
- 价值:一个账户向另一个账户发送以太币
交易的特性
"原子性"
要么完全执行,要么完全不执行
"串行"执行
不会同时并行执行
"进入区块链的顺序不确定"
交易的结构
Web3.js代发的交易结构
{
nonce: web3.toHex(10),
GasPrice: web3.toHex(100000000000),
Gas: web3.toHex(140000),
from: '0x633296baebc20f33ac2e1c1b105d7cd1f6a0718b',
to: '0xD1E1cdbCE15f1009B5A7874053E09C728Df91d47',
value: web3.toHex(0),
data: '0xcc9ab24952616d6100000000000000000000000000000000000000000000000000000000'
}
字段解释:
- nonce 该账户已发送交易数量,一个正整数
- GasPrice 和 Gas 与现实生活中的汽油费类似,用以支付交易费(交易费 = Gas x GasPrice)
汽油数量 *Gas *由用户指定,汽油单价 *GasPrice *由市场价格决定。运行交易后多余的 *Gas *会退还给用户(惩罚性虚拟机指令assert例外,会扣除所有的剩余未花费的 *Gas *) 。 *Gas *的数值人工预测并不准确,可以让客户端软件在签发交易时代为预测,无须自己手动填写。
- From 该笔交易的发送方
- To 该笔交易的接收方
若该交易为普通以太币转账交易,则接收方为受益人账户地址;若该交易为调用智能合约的交易,则接收方为智能合约的地址;若该交易为创建智能合约,则接收方地址可空缺。
- Value具体转移多少以太币到接收方
- Data数据字段
当若进行以太币纯转账交易时,该字段可空缺;若进行为智能合约调用,则该值包含编码后的函数名和参数的字节码;若为进行合约创建,则该值包含初始化合约的字节码。
交易的生命周期
- 客户端软件在收集完交易信息,组织成相应的结构体,需要使用用户的私钥来签名该交易。
- 交易后编码为一个公开消息,通过节点网络发出并逐渐扩散到网络中各个节点。
- 挖矿节点和众多其他普通节点同时收到该消息,矿工将其暂时缓存起来。
- 若矿工决定将该交易打包入某区块,则执行该消息内容并获得执行结果。
- 矿工把打包好的区块(包含该交易)广播到网络中,参与共识算法挑选。
- 区块进入最终的区块链被永久保存。
一笔交易在以太坊网络的节点间传播过程
以太坊上最常见的交易:
- 以太币转账
- 智能合约调用
- 智能合约创建
这三种交易在交易发送时经历的步骤是 *一模一样 *的,区别仅在于填写交易时选择传递数据 data 还是传递价值 value 。 传递数据的即为合约相关操作,传递价值即为转账操作。
共识与工作量证明
分布式系统的问题就在于如何协调各个节点步调一致、共同进退。这称之为“拜占庭将军问题”。
中心化支付系统与中心化支付系统的拓扑结构
数据结构
Radix树
Merkle树和Merkle证明
Merkle树是一个在密码学中常使用的树形结构,适用于解决数据完整性问题。 它的结构功能异常简单,通常我们用二叉树的形式来表示一棵 Merkle树, 它存在的作用是数据分组并保证数据在存储中的完整性。 例如一棵包含了数字 1 到 8 的 Merkle二叉树
如果碰上基础数据集不是偶数个元素(例如仅有7个数据),则复制一个元素,让它与自己的双胞胎做哈希运算
Merkle Patricia树
以太坊团队开创性地融合了Radix树和Merkle树的结构,并进一步优化该结构的存储性能,形成了实用的Merkle Patricia树
Merkle Patiricia树 (以下简称MPT树) 是一个可以存储键值对 (key-value pair) 的高效数据结构,键、值分别可以是任意的序列化过的字符串。它具有Merkle树的密码学安全校验功能,提供树中的数据完整性与存在性的 Merkle 证明。它具有强大的互动能力,在log(N)级别的时间内完成插入、删除、查询等操作。我们来循序渐进地查看如何构建一棵 MPT树。
状态树(以及存储树)
存储树,账户状态,世界状态的构成关系
根据以太坊黄皮书,账户若是一个智能合约账户,则必定包含了 *存储树 *(storageRoot)和 *代码存储 *(codeHash)
交易树
每个区块都有一棵独立的 交易树 (transactionsRoot)
单笔交易和交易树的构成
收据树
每个区块都有一棵独立的 收据树 (receiptsRoot)
从左到右:日志、交易收据、收据树的构成
区块
*区块头 *较为轻量级,包含了一系列的数值、引用的数值以及哈希值, 总长度在 508Byte 左右;
*区块体 *较为重量级,包含了该区块收纳的交易列表和叔块列表,长度视包含的交易多寡而定,目前在 20KB 左右 。
一个以太坊区块的示意图