以太坊基础

1. 以太坊帐户

一个以太坊帐户是一个具有以太币 (ETH) 余额的实体,可以在以太坊上发送交易。帐户和钱包不同,帐户是用户拥有的以太坊帐户的密钥对。 钱包是界面或应用程序,可以让你与以太坊帐户交互。

以太坊有两种帐户类型:

  • 外部所有的帐户 (EOA) – 由任何拥有私钥的人控制
  • 合约帐户 – 部署到网络上的智能合约,由代码控制。

1.1 外部持有

  • 创建帐户是免费的
  • 可以发起交易
  • 外部所有的帐户之间只能进行以太币和代币交易
  • 由一对加密密钥组成:控制帐户活动的公钥和私钥

1.2 合约账户

  • 创建合约存在成本,因为需要使用网络存储空间
  • 只能在收到交易时发送交易
  • 从外部帐户向合约帐户发起的交易能触发可执行多种操作的代码,例如转移代币甚至创建新合约
  • 合约帐户没有私钥。 相反,它们由智能合约代码逻辑控制

1.3 账户构成

以太坊帐户有四个字段:

  • nonce - 一个计数器,用来显示外部帐户发送的交易数量合约帐户创建的合约数量。 每个帐户只能执行具有一个给定随机数的一笔交易,以防范重放攻击,重放攻击指多次广播和重复执行已签署的交易。
  • balance – 这个地址拥有的 Wei 数量。 Wei 是以太币的计数单位,每个 ETH 有 1e+18 个 Wei。
  • storageRoot – 有时被称为存储哈希。 Merkle Patricia trie 根节点的 256 位哈希已编码了帐户的存储内容(256 位整数值映射),并编码为 Trie,作为来自 256 的 Keccak 256 位哈希的映射位整数键,用于 RLP 编码的 256 位整数值。 此 Trie 对此帐户存储内容的哈希进行编码,默认情况下为空。
  • codeHash - 该哈希表示以太坊虚拟机 (EVM) 上的帐户代码。合约帐户具有编程的代码片段,可以执行不同的操作。如果帐户收到消息调用,则执行此 EVM 代码。与其他帐户字段不同,不能更改。所有代码片段都被保存在状态数据库的相应哈希下,供后续检索。此哈希值称为 codeHash。对于外部所有的帐户,codeHash 字段是空字符串的哈希。
image-20240518215208920

1.4 私钥

外部账户的私钥由 64 个十六进制字符组成,可以用密码加密保存。

1
fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036415f

合约帐户也有一个 42 个字符组成的十六进制地址:

1
0x06012c8cf97bead5deae237070f9587f8e7a266d

合约地址通常在将合约部署到以太坊区块链时给出。 地址产生自创建人的地址和从创建人地址发送的交易数量(”nonce”)。

2. 交易

交易是由帐户发出,带密码学签名的指令。 帐户将发起交易以更新以太坊网络的状态。 最简单的交易是将 ETH 从一个帐户转到另一个帐户。

以太坊交易是指由外部持有帐户发起的行动,换句话说,是指由人管理而不是智能合约管理的帐户。 例如,如果 Bob 发送 Alice 1 ETH,则 Bob 的帐户必须减少 1 ETH,而 Alice 的帐户必须增加 1 ETH。 交易会造成状态的改变。

2.1 交易信息

所提交的交易包括下列信息:

  • from - 发送者的地址,该地址将签署交易。 这将是一个外部帐户,因为合约帐户不能发送交易。
  • to — 接收地址(如果是外部帐户,交易将传输值。 如果是合约帐户,交易将执行合约代码)
  • signature – 发送者的标识符。 当发送者的私钥签署交易并确保发送者已授权此交易时,生成此签名。
  • nonce - 一个有序递增的计数器,表示来自帐户的交易数量
  • value – 发送者向接收者转移的以太币数量(面值为 WEI,1 个以太币 = 1e+18wei)
  • input data – 可包括任意数据的可选字段
  • gasLimit – 交易可以消耗的最大数量的燃料单位。 以太坊虚拟机 指定每个计算步骤所需的燃料单位
  • maxPriorityFeePerGas - 作为小费提供给验证者的已消耗燃料的最高价格
  • maxFeePerGas - 愿意为交易支付的每单位燃料的最高费用(包括 baseFeePerGasmaxPriorityFeePerGas

2.2 交流流程

交易对象看起来像这样。交易对象需要使用发送者的私钥签名,这证明交易只可能来自发送者,而不是欺诈。

1
2
3
4
5
6
7
8
9
{
from: "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8",
to: "0xac03bb73b6a9e108530aff4df5077c2b3d481e5a",
gasLimit: "21000",
maxFeePerGas: "300"
maxPriorityFeePerGas: "10"
nonce: "0",
value: "10000000000",
}

Geth 这样的以太坊客户端将处理此签名过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"id": 2,
"jsonrpc": "2.0",
"method": "account_signTransaction",
"params": [
{
"from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
"gas": "0x55555",
"maxFeePerGas": "0x1234",
"maxPriorityFeePerGas": "0x1234",
"input": "0xabcd",
"nonce": "0x0",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x1234"
}
]
}

示例响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
"tx": {
"nonce": "0x0",
"maxFeePerGas": "0x1234",
"maxPriorityFeePerGas": "0x1234",
"gas": "0x55555",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x1234",
"input": "0xabcd",
"v": "0x26",
"r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
"s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
"hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
}
}
}

2.3 Input Data 字段

绝大多数交易都是从外部所有的帐户访问合约。 大多数合约用 Solidity 语言编写,并根据应用程序二进制接口 (ABI) 解释其 data 字段。

前四个字节使用函数名称和参数的哈希指定要调用的函数,调用数据的其余部分是参数。

例如,我们来看一下 这笔交易(opens in a new tab)

1
2
3
4
5
Function: transfer(address to,uint256 value)

MethodID: 0xa9059cbb
[0]: 0000000000000000000000004f6742badb049791cd9a37ea913f2bac38d01279
[1]: 000000000000000000000000000000000000000000000000000000003b0559f4

函数选择器是 0xa9059cbb。 有几个 具有此签名的已知函数(opens in a new tab)。 本例中 合约源代码(opens in a new tab) 已经上传到 Etherscan,所以我们知道该函数是 transfer(address, uint256)

根据应用程序二进制接口规范,整型值(例如地址,它是 20 字节整型)在应用程序二进制接口中显示为 32 字节的字,前面用零填充。 所以我们知道 to 地址是 4f6742badb049791cd9a37ea913f2bac38d01279(opens in a new tab)。 value 是 0x3b0559f4 = 990206452。

2.4 交易类型

以太坊有几种不同类型的交易:

  • 常规交易:从一个帐户到另一个帐户的交易。
  • 合约部署交易:没有 “to” 地址的交易,数据字段用于合约代码。
  • 执行合约:与已部署的智能合约进行交互的交易。 在这种情况下,”to” 地址是智能合约地址。

2.5 交易生命周期

交易提交后,就会发生以下情况:

  1. 以加密方式生成的交易哈希: 0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017
  2. 然后,该交易被广播到网络,并添加到由所有其他待处理的网络交易组成的交易池中。
  3. 验证者必须选择你的交易并将它包含在一个区块中,以便验证交易并认为它 “ 成功 “。
  4. 随着时间的流逝,包含你的交易的区块将升级成 “ 合理 “ 状态,然后变成 “ 最后确定 “ 状态。 通过这些升级,可以进一步确定 你的交易已经成功并将无法更改。 区块一旦 “ 最终确定 “,只能通过耗费数十亿美元的网络级攻击来更改。

3. 燃料

Gas 对以太坊网络至关重要。 正是这种燃料使它能够运行,正如车辆需要汽油一样。

3.1 燃料费

燃料费是用于执行某些操作的燃料数量,乘以每单位燃料的成本。 无论交易成功与否,都要支付燃料费。

img

燃料费必须用以太坊的本币支付,即以太币 (ETH)。 燃料通常以 gwei 计价,gwei 是以太币的一种计量单位。 一个 gwei 等于一个以太币的十亿分之一(0.000000001 个以太币,或 10-9 个以太币)。

比如,你可以说你的燃料费是 1 gwei,而不说 0.000000001 个以太币。

“Gwei” 是 “giga-wei” 的缩写,意思是 “ 十亿个 wei”。 一个 gwei 等于十亿个 wei。 Wei 本身(以 b-money(opens in a new tab) 的发明者 Wei Dai(opens in a new tab) 的名字命名)是以太币的最小单位。

3.2 如何计算燃料费?

当提交交易时,你可以设置你愿意支付的燃料数量。 通过提供一定数量的燃料,你出价将你的交易添加到下一个区块中。 如果你提供的燃料太少,验证者就不太可能选择添加你的交易,这意味着你的交易可能会延迟执行或不会被执行。 如果提供太多,你可能浪费一些以太币。 那么,怎么知道你应该支付多少燃料费呢?

你支付的总燃料费分为两部分:base feepriority fee(小费)。

Base fee 由协议设定——你必须至少支付这些金额,然后你的交易才会被视为有效。 Priority fee 是基础费以外的小费,它可以吸引验证者选择将你的交易添加到下一个区块。

只支付 base fee 的交易从技术上讲是有效的,但不太可能被添加到区块,因为它没有激励验证者优先选择它而不是其他交易。 “ 合适的 “ priority 费由发送交易时的网络使用情况决定——如果有大量需求,那么你可能不得不将你的 priority 费设置得更高;但当需求较少时,你也可以减少该费用。

例子一

例如,假设 Jordan 要向 Taylor 支付 1 个以太币。 一笔以太币转账需要 21,000 单位的燃料,基础费是 10 gwei。 Jordan 支付了 2 gwei 作为小费。

**总费用等于:使用的燃料单位数 *(基础费 + 小费)*EHOLDER}

其中 base fee 由协议设置,priority fee 是用户设置的支付给验证者的小费。

21,000 * (10 + 2) = 252,000 gwei(0.000252 个以太币)。

当 Jordan 转账时,将从 Jordan 帐户中扣除 1.000252 个以太币。

Taylor 的帐户增加 1.0000 个以太币。

验证者收到价值 0.000042 个以太币的小费。 0.00021 个以太币的 base fee 被销毁。[21000 * 10 * 0.000000001(10^-9)]

例子二

执行交易需要耗费燃料。 简单的转账交易需要 21000 单位燃料。

因此,如果 Bob 要在 baseFeePerGas 为 190 Gwei 且 maxPriorityFeePerGas 为 10 Gwei 时给 Alice 发送一个以太币,Bob 需要支付以下费用:

1
2
3
(190 + 10) * 21000 = 4,200,000 gwei
--或--
0.0042 ETH

Bob 的帐户将会扣除 1.0042 个以太币(1 个以太币给 Alice,0.0042 个以太币作为燃料费用)

Alice 的帐户将会增加 +1.0 ETH

基础费将会燃烧 -0.00399 ETH

验证者获得 0.000210 个以太币的小费

任何智能合约交互也需要燃料,任何未用于交易的燃料都会退还给用户帐户。

img

3.3 基础费

每个区块都有一个基础费作为底价。 要想有资格添加到区块中,燃料费出价必须至少等于基础费。 基础费独立于当前区块计算,是由当前区块之前的区块决定的,这使得用户更容易预测交易费。 在创建区块时,它的**基础费将被 “ 销毁 “**并退出流通。

基础费由一个公式计算得出,该公式将上一个区块的大小(所有交易中使用的燃料数量)与目标大小进行比较。 如果超过目标区块大小,每个区块的基础费将最多增加 12.5%。

区块编号已包含燃料费用增加当前基本费用
115M0%100 gwei
230M0%100 gwei
330M12.5%112.5 gwei
430M12.5%126.6 gwei
530M12.5%142.4 gwei
630M12.5%160.2 gwei
730M12.5%180.2 gwei
830M12.5%202.7 gwei

根据以上表格,要在 9 号区块创建交易,钱包会让用户明确知晓:要将交易添加到下一个区块的最高基础费等于 current base fee * 112.5% 或者 202.7 gwei * 112.5% = 228.1 gwei

3.4 优先费(小费)

优先费(小费)激励验证者将交易添加到区块中。 如果没有小费,验证者会发现开采空区块在经济上可行,因为它们会获得相同的区块奖励。

小额小费是对验证者将交易添加到区块的最小激励。 在相同区块中,对于要优先于其他交易执行的交易,可以添加更高的小费来尝试使出价高于竞争性交易。

3.5 最高费用

要在网络上执行交易,用户可以为他们愿意支付的交易执行费用指定最高限额。 此可选参数称为 maxFeePerGas。 为了执行交易,最高费用必须超过基础费和小费的总和。 交易完成后,会将最高费用与基础费和小费总和之间的差额退还给交易发送人。

尽管交易中包含费用限制,但交易中未使用的所有燃料将退还给用户(即退还 max fee - (base fee + tip))。

3.6 什么是燃料限额?

燃料限额是指你愿意在交易中消耗的最大燃料数量。 涉及 智能合约 的更复杂交易需要进行更多的计算工作,因此相比简单的支付,它们需要更高的燃料限额。 标准以太币转账要求燃料限额为 21,000 单位燃料。

例如,如果你对简单的以太币转账设置 50,000 单位燃料限额,以太坊虚拟机将消耗 21,000 单位,你将收到剩余的 29,000 单位。 然而,如果你设置的燃料太少,比如说,对于简单的以太币转账,设置燃料限额为 20,000 单位,以太坊虚拟机将消耗 20,000 单位燃料并尝试执行交易,但最后不会完成。 然后,以太坊虚拟机回滚所有变化,但由于验证者已经完成了价值 20k 单位燃料的工作,这些燃料就被消耗了。

4. 区块

区块是指一批交易的组合,并且包含链中上一个区块的哈希。 这将区块连接在一起(成为一个链),因为哈希是从区块数据中加密得出的。 这可以防止欺诈,因为以前的任何区块中的任何改变都会使后续所有区块无效,而且所有哈希都会改变,所有运行区块链的人都会注意到。

4.1 块提交

为了确保以太坊网络上的所有参与者保持同步状态并就交易的确切历史达成共识,我们将交易分为多个区块。 这意味着同时有数十个(甚至数百个)交易被提交、达成一致并同步。

通过间隔提交,所有网络参与者有足够时间达成共识:即使交易请求每秒发生数十次,但以太坊上的区块仅仅大约每十二秒创建并提交一次。

img

4.2 区块如何工作

为了保存交易历史,区块被严格排序(创建的每个新区块都包含一个其父块的引用),区块内的交易也严格排序。 除极少数情况外,在任何特定时间,网络上的所有参与者都同意区块的确切数目和历史, 并且正在努力将当前的活动交易请求分批到下一个区块。

随机选择的验证者在网络上构建完区块后,该区块将传播到整个网络;所有节点都将该区块添加至其区块链的末尾,然后挑选新的验证者来创建下一个区块。 目前,确切的区块构建过程和提交/共识过程由以太坊的 “ 权益证明 “ 协议规定。

4.3 权益证明协议

权益证明是指:

  • 验证节点必须向存款合约中质押 32 个以太币,作为抵押品防止发生不良行为。 这有助于保护网络,因为如果发生不诚实活动且可以证实,部分甚至全部质押金额将被销毁。
  • 在每个时隙(12 秒的时间间隔)中,会随机选择一个验证者作为区块提议者。 他们将交易打包并执行,然后确定一个新的 “ 状态 “。 他们将这些信息包装到一个区块中并传送给其他验证者。
  • 其他获悉新区块的验证者再次执行区块中包含的交易,确定他们同意对全局状态提出的修改。 假设该区块是有效的,验证者就将该区块添加进各自的数据库。
  • 如果验证者获悉在同一时隙内有两个冲突区块,他们会使用自己的分叉选择算法选择获得最多质押以太币支持的那一个区块。

4.4 区块包含什么?

一个区块中包含很多信息。 区块的最高层包含以下字段:

字段简介
slot区块所属的时隙
proposer_index提出区块的验证者的 ID
parent_root上一个区块的哈希
state_root状态对象的根哈希
body包含多个字段的对象,定义如下

区块的 body 包含一些自有字段:

栏目简介
randao_reveal用于选择下一个区块提议者的值
eth1_data关于存款合约的信息
graffiti用于标记区块的任意数据
proposer_slashings将要惩罚的验证者列表
attester_slashings被惩罚的验证者列表
attestations支持当前区块的认证列表
deposits存款合约新增存款的列表
voluntary_exits退出网络的验证者列表
sync_aggregate用于服务轻客户端的验证者子集
execution_payload从执行客户端传送来的交易

attestations 字段包含区块中所有认证的列表。 认证有自己的数据类型,其中包含多条数据。

4.5 区块时间

区块时间是指两个区块之间的时间间隔。 在以太坊中,时间划分为每 12 秒一个单位,称为 “ 时隙 “。 在每个时隙内,选择一个单独的验证者提议区块。 假设所有验证者都在线且完全正常运行,则每个时隙内都会有一个区块产生,意味着区块时间是 12 秒。 但是,偶尔验证者在被要求提议区块时不在线,导致有时候一些时隙是空的。

这种实现与基于工作量证明的系统不同。在工作量证明系统中,区块时间是带有概率性的,并由协议的目标挖矿难度调节。 以太坊的 平均区块时间(opens in a new tab) 是一个很好的例子,根据不变的新的 12 秒区块时间,可以清楚地推断出从工作量证明到权益证明的过渡。

4.6 区块大小

最后一条重要提示是,区块本身的大小是有界限的。

每个区块的目标大小为 1500 万单位燃料,但区块的大小将根据网络需求而增加或减少,直至达到 3000 万单位燃料的区块限制(目标区块大小的 2 倍)。 区块中所有交易消耗的总燃料量必须低于区块的燃料限制。 这很重要,因为它可以确保区块不能任意扩大。

如果区块可以任意扩大,由于空间和速度方面的要求,性能较差的全节点将逐渐无法跟上网络。 区块越大,在下一个时隙中及时处理它们需要的算力就越强大。 这是一种集中化的因素,可以通过限制区块大小来抵制。

5. 以太坊虚拟机 (EVM)

EVM 它是真实存在并由数以千计运行以太坊客户端的计算机共同维护的一个实体。

以太坊不是分布式账本,而是分布式 状态机器(opens in a new tab)。 以太坊的状态是一个大型数据结构,它不仅保存所有帐户和余额,而且还保存一个 (基础费 + 小费),它可以根据预定义的一组规则在不同的区块之间进行更改,并且可以执行任意的机器代码。 在区块中更改状态的具体规则由 EVM 定义。

img

6. 网络

以太坊只有一个主网,但可以创建符合相同协议规则的独立网络用于测试和开发。 有许多独立的 “ 网络 “ 遵循该协议,但彼此之间没有交互。 你甚至可以在自己的计算机上本地启动一个以太坊网络,以测试你的智能合约和 web3 应用程序。

以太坊帐户可在不同的网络上使用,但是帐户余额和交易历史记录不会结转到以太坊主网以外。 进行测试时,了解哪些网络可用以及如何获取可以试用的测试网以太币是很有用的。 一般来说,出于安全考虑,不建议在测试网上重复使用主网帐户,反之亦然。

6.1 以太坊测试网

在部署到主网之前,你应该在测试网测试编写的任何合约代码。 在与现有智能合约集成的去中心化应用程序中,大多数项目将副本部署到测试网。

客户端开发者目前还在维护的两个公共测试网是 Sepolia 和 Goerli。 Sepolia 是一个供合约和应用程序开发者测试其应用程序的网络。 Goerli 网络是让协议开发者测试网络升级,并让质押人测试运行验证者。

Sepolia 是应用程序开发时推荐使用的默认测试网。 Sepolia 网络使用一种需要许可的验证者设置。 它相对较新,即它的状态和历史记录都非常小。 这意味着网络可以快速同步,并且在其上运行节点需要的存储空间更少。 这对于希望快速启动节点并直接与网络交互的用户来讲,是非常有用的。

7. 参考资料