# 以太坊( Ethereum )
基于全球协作的开源区块链项目。该项目具有一个运行时虚拟机,提供了能够进行大规模分布式开发应用的平台。
它的目标是成为一个 全球化的计算基础设施,也就是具有分布式、 公开透明、永不下线特性的全球计算机 。 智能合约 是在这台分布式计算机上运行的小程序。
以太坊的要素
区块链:记录所有的交易
网络:分布式的节点
共识机制:可信度证明
特殊的环境:运行智能合约
状态:每次交易不断变化的世界机
# 账户
# 状态
- 区块链中所有人账户的状态的总集合,个人的以太币记录在该世界状态中。
- 世界状态是一份不断变化的数据,每个以太坊节点中都有一份一模一样的拷贝。
- 在交易的过程中,区块链中的状态都会发生变化,即进行状态转移
# 区块
区块(block)是多个合法签名的交易的有序集合,任意一个区块包含几个部分
- 前一区块的哈希值
- 当前区块的哈希值
- 计算工作量的参数
- 时间戳
- 该区块包含的交易列表的哈希值
# 账本
区块是“多个合法签名交易的有序集合”。这就好比是账本中的一页记录。
创世区块是账本的第一页
区块链不断的记账,经过共识算法挑选的合法区块逐一堆叠而形成的区块链是一个巨大的账本
想推翻某个区块的记录,就需要计算后面的所有区块
# 账户
以太坊的账户被分为两类
# 外部账户(EOA)
外部账户是将一个私钥与该私钥对应的公开地址来表示,一般情况,私钥掌握在用户的手中
# 智能合约(CA)
智能合约没有私钥,只有公开的地址,它的行为由合约自身的代码逻辑控制
# 账户的元素
nonce
已执行交易总数,用来标示该账户发出的交易数量;- 随着用户的交易而不断递增
- 被纳入最终区块链
- 需要指定合约地址
balance
持币数量,记录用户的以太币余额;- 可花费的以太币数量
storage hash
存储区的哈希值,指向智能合约账户的存储数据区;- 智能合约独有
- 外部账户不包含该值
- 存储区即为智能合约运行
code hash
代码区的哈希值,指向智能合约账户存储的智能合约代码。- 智能合约独有
- 代码区的内容是不可更改的只读状态
# keyStore 与 私钥保存
私钥相当于个人保险箱的密码,因此保存私钥是至关重要的,目前私钥的保存方式大都采用keystore格式的加密进行。
# keystore加密
- 通过
KDF
的算法的变种算法Scrypt
算法,将我们选中的密码:123456,变换为一个 AES-128-CTR 对称加密算法所能采用的加密密匙 S 。 - 使用该密匙 S 通过 AES-128-CTR 对称加密算法加密明文的以太坊私钥。
- 将 2 步骤生成的结果保存为密文 cyphertext 。
- 为了防止可能的篡改或数据变更,将 cyphertext 与 S 联合起来作为输入,使用
SHA3
哈希算法对该值进行带入求值,得到一个完整性校验签名。
{
"version": 3,
"id": "7d5d99c8-f455-49aa-8b89-6c795a7cdd46",
"address": "7c52e508c07558c287d5a453475954f6a547ec41",
"crypto": {
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"salt": "a4f9677eaf6f72394da51e16695899ad3e9b4f2228ad4eca5ef2a5c36093fe12",
"n": 262144,
"r": 8,
"p": 1
},
"cipher": "aes-128-ctr",
"ciphertext": "d89df5ef74f51ae485308e6dce8991dd80674e111f8073f9efa52cb2dd6eca3f",
"cipherparams": {
"iv": "6b064c5b09a154d9877d3a07e610a567"
},
"mac": "30949eb085ce342a6a488fd51fa5e3231e45f7515efa10c19ea0d46270c73f06"
}
}
# keystore解密
用户输入准确的密码 123456 后,通过
KDF
的Scrypt
算法先计算出解密密匙 S’ 。如果密码正确,S = S’ ,否则提示用户密码输入错误。
最终通过 S’,经由 AES-128-CTR 对称加密算法反向计算出私钥。
名词 | 解释 |
---|---|
id | 随机生成的一个 uuid 格式的字符串 |
address | 该 keystore 对应的公开地址 |
crypto | 加密后的密文区域以及加密参数 |
kdf | 全称Key Derivation Function,加密密匙生成算法, 举例中选用了Scrypt 算法 |
kdfparams | Scrypt 算法所需要的必要参数 |
cypher | 加密私钥选用的对称加密算法,举例中选用了AES-128-CTR 算法 |
cyphertext | 加密后的密码文 |
cypherparams | AES-128-CTR 算法所需要的必要输入参数 |
mac | cyphertext与加密密匙的校验值,防止keystore被中途篡改 |
# 交易
交易是驱动状态发生变化的动因。
以太坊上的交易活动是由三个步骤完成的:
用户签名打包一条或多条消息,组装成一笔交易,发送到以太坊网络上。
该交易被矿工捕获,并收纳入某一个区块。
区块形成后,被矿工广播到节点网络中,最终加入区块链。
交易 是外部账户发起的,可以包含一条消息。
消息 在账户与账户之间传递数据(data)与价值(value),消息的具体表现形式如下:
数据:一个账户向另一个账户请求函数调用。
价值:一个账户向另一个账户发送以太币。
# 交易的特性
# “原子性”
如果将区块链看作是一个巨型分布式数据库,一次交易就是执行对该数据库的一次修改操作。例如某智能合约在执行的过程中修改了一个或者数个外部账户的余额。 这些修改操作要么完全执行,要么完全不执行 ,它不会部分执行,部分不执行。哪怕智能合约在执行某些操作后出现异常而失败,之前执行成功的部分操作也会被“回滚”来撤销影响,这就叫“原子性”。
# “串行” 执行
每一笔交易都会影响世界状态的一小部分,它们发生影响的顺序不是同时的,而是一个接一个的,单一时刻只有一个交易被执行,不会有并行出现。哪怕归入了同一个区块的数笔交易,在以太坊虚拟机上也有先后的执行顺序,并不会在虚拟机中多线程并发执行。
# “进入区块链的顺序不确定”
当全球的数万名用户向区块链中的节点发送交易时,交易最终进入区块链的顺序并不取决于发送的前后顺序。消息在因特网上广播扩散的快慢,交易费用的高低等诸多因素影响着交易最终进入区块链的顺序。负责记账的矿工因为受到共识规定的约束,所以打包出来的区块内含的多个交易也可能有顺序上的排列组合的考量。某个矿工成功打包的区块有可能不能入选最终的区块链,导致用户的交易没有在第一时间进入区块链,此时用户交易会临时等待,直到进入被另一个矿工捕获被打包。
# 交易的生命周期
交易的生命周期从用户通过某节点/软件广播该交易为起点,经过网络扩散、矿工挖矿记账、被共识算法选入最终区块链条,到达终点。
# 存储
# 基本数据结构
# Radix树
Trie是一种数据结构,用来存储经常变化的动态的数据,单条数据是一个键值对。
node
节点:用圆圈表示,节点包含键、值、与指向后继节点的路径。root
树根:树的起始节点,一棵树有且仅有一个树根。leaf
树叶:树的最下层末梢节点,再无后继节点。internal node
内部节点: 树中间的节点,承上启下,在上层与后继都有节点。Path
路径:连结两个节点之间的直线,可以是双向箭头,也可以是单向箭头。
Radix将数据集的值存储在节点中,键存储在路径中。
{
"do": 0,
"dog": 1,
"dax": 2,
"dogu": 3,
"dodo": 4,
"house": 5,
"houses": 6
}
# Merkle树
数据完整性在通信协议中是需要考虑的基本问题,因此数据完整性的验证是重中之重。
- Merkle树是一个在密码学中常使用的树形结构,适用于解决数据完整性问题。
- 它存在的作用是数据分组并保证数据在存储中的完整性。
在图中的 Merkle树里,我们一共持有8个基础数据, 1
到 8
,它们两两分组形成最初的小组。
例如数据 1
与数据 2
,它们分在一组中,并计算它们串联后的哈希值 H1=hash(1,2)H1=hash(1,2) ,
同理将数据 3
与数据 4
串联的串联哈希值计算为 H2H2,依次类推,形成第二层的树中节点。
之后再将第二层的树中节点H1H1~ H4H4两两分组。例如 (H1H1与H2H2串联计算第三层的哈希值H5H5)。
逐层聚合反复递归,执行哈希计算。直到的仅剩下一个哈希值节点:根节点哈希值 Root ,该过程完毕。
# MPT树
区块链中的数据要进行大量的动态的节点的查询、插入和删除以及真伪的鉴定。
# 状态树
账户的集合组成了世界状态,账户在组成世界状态时采用 MPT 树的组织形式 。
- 账户处于树的叶子节点中
- 树的组织按照排列顺序进行串联哈希
- 层层哈希得出世界状态
- 某一账户的改变会引发其所在分支的更改
# 交易树
每个区块都有一棵独立的 交易树
每笔交易最后都会被永久记录下来,并且组织成 MPT 树的叶子节点,经过层层哈希计算后,树根的256位哈希值被记录下来进入区块头,该值,在代码中被命名为transactionsRoot,即为 交易树
# 收据树
交易收据,由 MPT 树组织起来,计算得出根哈希值,称为receiptsRoot 交易树 ,该值将进入区块头部被永久保留
# 区块
区块 = 区块头+区块体
区块头 较为轻量级,包含了一系列的数值、引用的数值以及哈希值, 总长度在 508Byte
左右;
区块体 较为重量级,包含了该区块收纳的交易列表和叔块列表,长度视包含的交易多寡而定,目前在 20KB
左右 。