Ethereum 关键结构介绍:Recept、TxPool 和 Transaction
Receipt
Receipt(收据)是交易执行后的返回结构。
下面是一个例子:
1{
2 "blockHash": "0x83eaba432089a0bfe99e9fc9022d1cfcb78f95f407821be81737c84ae0b439c5",
3 "blockNumber": "0x38",
4 "contractAddress": "0x03d8c4566478a6e1bf75650248accce16a98509f",
5 "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
6 "to": "0x853f43d8a49eeb85d32cf465507dd71d507100c1",
7 "cumulativeGasUsed": "0x927c0",
8 "gasUsed": "0x927c0",
9 "logs": [
10 {
11 "address": "0x03d8c4566478a6e1bf75650248accce16a98509f",
12 "topics": [
13 ],
14 "data": "0x03d8c4566478a6e1bf75650248accce16a98509f",
15 "blockNumber": "0x38",
16 "transactionHash": "0x422fb0d5953c0c48cbb42fb58e1c30f5e150441c68374d70ca7d4f191fd56f26",
17 "transactionIndex": "0x0",
18 "blockHash": "0x83eaba432089a0bfe99e9fc9022d1cfcb78f95f407821be81737c84ae0b439c5",
19 "logIndex": "0x0",
20 "removed": false
21 }
22 ],
23 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
24 "root": null,
25 "transactionHash": "0x422fb0d5953c0c48cbb42fb58e1c30f5e150441c68374d70ca7d4f191fd56f26",
26 "transactionIndex": "0x0",
27 "status": "0x1",
28 "effectiveGasPrice": "0x100"
29}
包含的信息如下:
-
blockHash
: 表示包含此交易的区块的哈希值。 -
blockNumber
: 表示包含此交易的区块号。 -
contractAddress
: 如果交易是部署智能合约的交易,则表示所部署的智能合约的地址。 -
from
: 发送交易的账户地址。 -
to
: 接收交易的账户地址。 -
cumulativeGasUsed
: 执行此交易时的累积 Gas 使用量。cumulativeGasUsed 包括当前交易以及之前在同一个区块中执行的所有交易的 Gas 消耗总和。
-
gasUsed
: 此交易实际消耗的 Gas 量。 -
logs
: 包含与此交易相关的日志条目的数组。每个日志条目都包含关于合约事件的详细信息。 -
logsBloom
: 一个位图,用于快速检索与交易相关的日志条目。 -
root
: Merkle树的根哈希,用于验证交易是否在区块中正确执行。 -
transactionHash
: 交易的哈希值。交易哈希是通过对交易的 RLP 编码数据进行哈希运算而计算出来的。
-
transactionIndex
: 交易在区块中的索引位置。 -
status
: 交易的执行状态。-
0x0:交易失败。
-
0x1:交易成功。
-
其他非零值:可能是合约定义的其他自定义状态。
-
-
effectiveGasPrice
: 交易的有效 Gas 价格。(实际 Gas 价格,可能与交易中指定的初始 Gas 价格不同。)交易池中的交易会根据effectiveGasPrice进行排序,较高的effectiveGasPrice交易有更高的优先级,更可能被矿工打包进区块中。 logsBloom 提供一种高效的方式来过滤和匹配日志条目,而无需遍历所有的日志。它的原理是 Bloom Filter
具体原理是:
-
对于每个日志条目,使用Keccak-256哈希算法对其主题(topics)和地址进行哈希运算。
-
将哈希结果的低三个字节(共12个字节)作为索引,将对应的位设置为1。该索引范围为0-2047(共2048个位置)。
-
对于每个主题,使用相同的方式进行哈希运算并设置对应的位。
-
当需要查询或过滤特定日志时:
-
将目标主题和地址分别进行哈希运算
-
将哈希结果的低三个字节(共12个字节)作为索引
-
检查对应的位是否被设置为1。如果位被设置为1,则表示该日志可能与目标事件相关;如果位为0,则表示该日志与目标事件无关。
-
(Raw) Transaction
下面的结构中 raw 是原始交易数据,通过 RLP 解码得到 tx。
1{
2 "raw": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675",
3 "tx": {
4 "hash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b",
5 "nonce": "0x0",
6 "blockHash": "0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b",
7 "blockNumber": "0x15df",
8 "transactionIndex": "0x1",
9 "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
10 "to": "0x853f43d8a49eeb85d32cf465507dd71d507100c1",
11 "value": "0x7f110",
12 "gas": "0x7f110",
13 "gasPrice": "0x09184e72a000",
14 "input": "0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360",
15 "s": "0x777"
16 }
17}
-
raw: 交易的原始数据,以十六进制表示。
-
tx.hash: 交易的哈希值,用于唯一标识这笔交易。
-
tx.nonce: 发起者地址的交易计数器,表示发起者的交易数量。
-
tx.blockHash: 交易所在区块的哈希值。
-
tx.blockNumber: 交易所在区块的编号。
-
tx.transactionIndex: 交易在区块中的索引位置。
-
tx.from: 发起者的以太坊地址。
-
tx.to: 接收者的以太坊地址。如果为 null,则表示创建合约。
-
tx.value: 交易发送的以太币数量。
-
tx.gas: 交易可使用的 Gas 数量。
-
tx.gasPrice: 交易的 Gas 价格。
-
tx.input: 交易的输入数据,用于调用合约或传递附加信息。
-
tx.s: 交易签名的一部分,用于验证交易的合法性。
r和s是用来表示 ECDSA(Elliptic Curve Digital Signature Algorithm)数字签名的两个组成部分。
生成交易的数字签名涉及以下步骤:
-
将交易的特定字段进行哈希处理。这些字段通常包括交易的链ID、交易的发送者地址、接收者地址、数额、Gas 限制、Gas 价格和交易的附加数据。
-
使用发送者的私钥对哈希后的数据进行 ECDSA 签名。私钥对应于发送者地址。
-
生成的数字签名包括两个部分:r和s。r是签名的一部分,s是签名的另一部分。
TxPool
TxPool 在以太坊中类似于其他区块链网络中的 MemPool(内存池)的概念。它用于存储待处理交易的内存数据结构,从而管理和维护待确认的交易。
例子:
1> txpool.content
2{
3 pending: {
4 0x0216d5032f356960cd3749c31ab34eeff21b3395: {
5 806: {
6 blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
7 blockNumber: null,
8 from: "0x0216d5032f356960cd3749c31ab34eeff21b3395",
9 gas: "0x5208",
10 gasPrice: "0xba43b7400",
11 hash: "0xaf953a2d01f55cfe080c0c94150a60105e8ac3d51153058a1f03dd239dd08586",
12 input: "0x",
13 nonce: "0x326",
14 to: "0x7f69a91a3cf4be60020fb58b893b7cbb65376db8",
15 transactionIndex: null,
16 value: "0x19a99f0cf456000"
17 }
18 },
19 0x24d407e5a0b506e1cb2fae163100b5de01f5193c: {
20 34: {
21 blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
22 blockNumber: null,
23 from: "0x24d407e5a0b506e1cb2fae163100b5de01f5193c",
24 gas: "0x44c72",
25 gasPrice: "0x4a817c800",
26 hash: "0xb5b8b853af32226755a65ba0602f7ed0e8be2211516153b75e9ed640a7d359fe",
27 input: "0xb61d27f600000000000000000000000024d407e5a0b506e1cb2fae163100b5de01f5193c00000000000000000000000000000000000000000000000053444835ec580000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
28 nonce: "0x22",
29 to: "0x7320785200f74861b69c49e4ab32399a71b34f1a",
30 transactionIndex: null,
31 value: "0x0"
32 }
33 }
34 },
35 queued: {
36 0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c: {
37 3: {
38 blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
39 blockNumber: null,
40 from: "0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c",
41 gas: "0x15f90",
42 gasPrice: "0x4a817c800",
43 hash: "0x57b30c59fc39a50e1cba90e3099286dfa5aaf60294a629240b5bbec6e2e66576",
44 input: "0x",
45 nonce: "0x3",
46 to: "0x346fb27de7e7370008f5da379f74dd49f5f2f80f",
47 transactionIndex: null,
48 value: "0x1f161421c8e0000"
49 }
50 },
51 0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a: {
52 2: {
53 blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
54 blockNumber: null,
55 from: "0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a",
56 gas: "0x15f90",
57 gasPrice: "0xba43b7400",
58 hash: "0x3a3c0698552eec2455ed3190eac3996feccc806970a4a056106deaf6ceb1e5e3",
59 input: "0x",
60 nonce: "0x2",
61 to: "0x24a461f25ee6a318bdef7f33de634a67bb67ac9d",
62 transactionIndex: null,
63 value: "0xebec21ee1da40000"
64 },
65 6: {
66 blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
67 blockNumber: null,
68 from: "0x9b11bf0459b0c4b2f87f8cebca4cfc26f294b63a",
69 gas: "0x15f90",
70 gasPrice: "0x4a817c800",
71 hash: "0xbbcd1e45eae3b859203a04be7d6e1d7b03b222ec1d66dfcc8011dd39794b147e",
72 input: "0x",
73 nonce: "0x6",
74 to: "0x6368f3f8c2b42435d6c136757382e4a59436a681",
75 transactionIndex: null,
76 value: "0xf9a951af55470000"
77 }
78 }
79 }
80}
宏观上看,例中的TxPool 可以分为两个部分:pending(待处理)和queued(排队)。
微观上,以一个具体的交易为例。有如下字段:
-
hash(交易哈希): 标识交易的身份,怎么来的上面说过。
-
from(发送者地址): 交易的发送者的以太坊地址
-
to(接收者地址): 交易的接收者的以太坊地址。
-
value(金额): 交易中转移的以太币数量。例如,
0x19a99f0cf456000
表示转移了0x19a99f0cf456000
个以太币。(单位是 wei) -
gasPrice(Gas 价格): 交易发送者愿意支付的每单位 gas 的价格。例如,
0xba43b7400
表示每单位 gas 的价格为0xba43b7400
wei。 -
gas(Gas 限制): 交易所允许的最大 gas 用量。例如,
0x5208
表示最大允许使用0x5208
单位的 gas。
Log
下面是一个 Log
结构体的 JSON 示例:
1{
2 "address": "0x7f69a91a3cf4be60020fb58b893b7cbb65376db8",
3 "topics": [
4 "0xaf953a2d01f55cfe080c0c94150a60105e8ac3d51153058a1f03dd239dd08586",
5 "0x976a3fc5d6f7d259ebfb4cc2ae75115475e9867c"
6 ],
7 "data": "0x0123456789abcdef",
8 "blockHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
9 "blockNumber": "0x1234",
10 "transactionHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
11 "transactionIndex": "0x12",
12 "logIndex": "0x34",
13 "transactionLogIndex": "0x56",
14 "logType": "event",
15 "removed": false
16}
字段的解释:
-
address
(地址):该日志所属的合约地址。 -
topics
(主题):一个主题数组,用于标识该日志的主题或事件。每个主题是一个 32 字节的哈希值(H256)。 -
data
(数据):日志的数据部分,以字节数组(Bytes)的形式表示。 -
blockHash
(区块哈希):包含该日志的区块的哈希值(H256)。 -
blockNumber
(区块号):包含该日志的区块的编号(U64)。 -
transactionHash
(交易哈希):包含该日志的交易的哈希值(H256)。 -
transactionIndex
(交易索引):该日志在所在交易的索引位置(Index)。 -
logIndex
(日志索引):该日志在所在区块的日志列表中的索引位置(U256)。 -
transactionLogIndex
(交易日志索引):该日志在所在交易的日志列表中的索引位置(U256)。 -
logType
(日志类型):日志的类型(String)。开发人员可以定义不同类型的事件,并在日志中记录这些事件的发生。 -
removed
(已移除):表示该日志是否已被移除(Option)。 在以太坊中,区块链的状态是可以回滚的,这意味着先前确认的交易和日志可能会被撤销或移除。当一个日志被移除时,removed 字段将被设置为 true,表示该日志已不再有效。
Block
一个以太坊区块。
1 {
2 "miner": "0x0000000000000000000000000000000000000001",
3 "number": "0x1b4",
4 "hash": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
5 "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5",
6 "mixHash": "0x1010101010101010101010101010101010101010101010101010101010101010",
7 "nonce": "0x0000000000000000",
8 "sealFields": [
9 "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2",
10 "0x0000000000000042"
11 ],
12 "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
13 "logsBloom": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
14 "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
15 "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
16 "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff",
17 "difficulty": "0x27f07",
18 "totalDifficulty": "0x27f07",
19 "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
20 "size": "0x27f07",
21 "gasLimit": "0x9f759",
22 "minGasPrice": "0x9f759",
23 "gasUsed": "0x9f759",
24 "timestamp": "0x54e34e8e",
25 "transactions": [],
26 "uncles": []
27}
-
miner
(矿工):挖掘该区块的矿工的地址。 -
number
(编号):该区块在区块链中的编号。 -
hash
(哈希):该区块的唯一哈希值,用于标识该区块。 -
parentHash
(父区块哈希):该区块的父区块的哈希值。区块链中的每个区块都包含了前一个区块的哈希值,称为父区块哈希(parent block hash)。通过追溯父区块的哈希值,可以构建整个区块链的链式结构。
-
mixHash
(混合哈希):用于工作量证明(Proof of Work)的混合哈希值。混合哈希值是通过将区块头部数据与随机数(nonce)进行哈希运算得到的。
-
nonce
(随机数):用于工作量证明的随机数。 -
sealFields
(封印字段):一个包含两个元素的数组,记录了区块的封印字段信息。 sealFields 是一个包含两个元素的数组,其中包含了以下两个字段:-
sealFields[0]
:“nonce”(随机数),它是挖矿过程中的一个关键参数。矿工需要不断尝试不同的随机数值,将其与区块的头部数据进行组合,并计算混合哈希,以满足挖矿的要求。 -
sealFields[1]
:“signature”(签名),用于验证区块的合法性。签名是由挖矿节点创建的,用于证明该节点有权将该区块添加到区块链中。签名通常基于矿工的私钥和区块头部数据进行计算,以确保只有具有相应私钥的矿工才能创建有效的签名。
-
-
sha3Uncles
(叔区块哈希):叔区块的哈希值。 -
logsBloom
(日志布隆过滤器):用于快速检索区块中日志的布隆过滤器。 -
transactionsRoot
(交易根哈希):区块中交易的 Merkle 根哈希。 -
receiptsRoot
(收据根哈希):区块中交易收据的 Merkle 根哈希。 -
stateRoot
(状态根哈希):区块的状态根哈希,表示区块执行后的状态。 -
difficulty
(难度):该区块的挖掘难度。以太坊的难度调整算法旨在使新区块的平均产生时间保持在大约15秒左右。如果矿工整体的计算能力增加,导致新区块的产生速度过快,难度值将会自动增加。 通过一个称为"Difficulty Bomb"(难度炸弹)的机制来实现的
-
totalDifficulty
(总难度):从创世区块到当前区块的总挖掘难度。 -
extraData
(额外数据):额外的自定义数据。 -
size
(大小):区块的大小(字节数)。 -
gasLimit
(燃料限制):该区块中的交易所允许的最大燃料数量。 -
minGasPrice
(最低燃料价格):该区块中的交易所需的最低燃料价格。 -
gasUsed
(已使用燃料):该区块中的交易所使用的总燃料数量。 -
timestamp
(时间戳):该区块的时间戳,表示区块生成的时间。 -
transactions
(交易列表):该区块中包含的交易列表。 -
uncles
(叔区块列表):该区块所包含的叔区块列表。
Block Hash
在以太坊中,每个区块都有一个唯一的哈希值,称为区块哈希(block hash)。区块哈希是通过对区块的头部数据进行哈希运算得到的。
区块头部数据包括以下字段:
-
父区块哈希(parentHash):前一个区块的哈希值。
-
交易根哈希(transactionsRoot):区块中所有交易的 Merkle 根哈希。
-
收据根哈希(receiptsRoot):区块中所有交易收据的 Merkle 根哈希。
-
状态根哈希(stateRoot):区块执行后的状态根哈希。
-
时间戳(timestamp):区块的时间戳。
将这些字段编码、哈希运算,得到区块哈希。
Uncle Block
叔区块(Uncle Block)也称为叔块或叔表(Uncle Table)。指在挖矿过程中产生的有效但未被选为主链上的区块。
PoS 共识算法中,矿工通过计算难题来创建新的区块。特殊情况会出现多个矿工几乎同时挖掘出有效的区块。
此时,只有一个区块能被选为主链上的区块,成为有效区块。其他几乎同时产生的有效区块则成为叔区块。
尽管叔区块未被选为主链上的区块,但它们仍然在以太坊网络中发挥作用。叔区块的存在有助于提高整个网络的安全性和去中心化程度。
因此会通过奖励挖掘叔区块的矿工,以太坊鼓励矿工参与挖矿活动,增加网络的去中心化程度块。