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 共识算法中,矿工通过计算难题来创建新的区块。特殊情况会出现多个矿工几乎同时挖掘出有效的区块。

此时,只有一个区块能被选为主链上的区块,成为有效区块。其他几乎同时产生的有效区块则成为叔区块

尽管叔区块未被选为主链上的区块,但它们仍然在以太坊网络中发挥作用。叔区块的存在有助于提高整个网络的安全性和去中心化程度。

因此会通过奖励挖掘叔区块的矿工,以太坊鼓励矿工参与挖矿活动,增加网络的去中心化程度块。