{"content":{"title":"EIP-4844 开发使用介绍","body":">- 原文链接：https://github.com/colinlyguo/EIP-4844-dev-usage \r\n>- 译文出自：[登链翻译计划](https://github.com/lbc-team/Pioneer)\r\n>- 译者：[Tiny 熊](https://learnblockchain.cn/people/15)\r\n>- 本文永久链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/7570)\r\n\r\n\r\n本文内容涉及：\r\n\r\n- Blob 交易格式\r\n- 如何发送 blob 交易？\r\n- 新操作码和预编译\r\n- Blob 浏览器\r\n- 如何查询 blob 内容？\r\n\r\n## Blob 交易格式\r\n\r\nBlob 交易是符合 [EIP-2718：类型交易封装](https://eips.ethereum.org/EIPS/eip-2718) 的一种新交易类型。该格式定义了交易及其收据如下：\r\n\r\n### 交易结构\r\n\r\n- **TransactionType**：交易类型的唯一标识符，对于 blob 交易，交易类型设置为`BLOB_TX_TYPE (0x3)`。\r\n\r\n- **TransactionPayload**：blob 交易的有效载荷（payload）结构如下：`rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, y_parity, r, s])`。\r\n    - `max_fee_per_blob_gas (uint256)`：发送方愿意支付的最大 blob gas 费用。实际收取的费用是区块的 blob 基础费用。\r\n    - `blob_versioned_hashes`：可用于验证 blob 内容完整性的哈希数组。每个哈希是一个 0x01 字节（表示版本），后跟 KZG 的 SHA256 哈希的最后 31 个字节。此方法设计用于[与 EVM 兼容和未来兼容性](https://notes.ethereum.org/@vbuterin/proto_danksharding_faq#Why-use-the-hash-of-the-KZG-instead-of-the-KZG-directly) 。\r\n\r\n> **注意**：`gas_limit`不包括 blob gas，blob gas 单独计算，每个 blob 为`131072 (0x20000)`。\r\n\r\n### 交易收据结构\r\n\r\n- **ReceiptPayload**：blob 交易的收据有效载荷定义为：`rlp([status, cumulative_transaction_gas_used, logs_bloom, logs])`。\r\n\r\n> **注意**：`cumulative_transaction_gas_used`仅反映执行交易所使用的累积 gas，不包括 blob gas。\r\n\r\n## 发送 Blob 交易\r\n\r\n### 网络形式\r\n\r\n在 EIP-4844 的网络层中，blob 交易使用不同的格式进行发送。协议要求执行节点在传播时检查 blob 交易的有效性。\r\n\r\n- **协议片段**：\r\n    - 在交易 gossip 传播响应（`PooledTransactions`）期间，blob 交易的 EIP-2718 `TransactionPayload`被包装为：`rlp([tx_payload_body, blobs, commitments, proofs])`。\r\n    - 节点必须验证`tx_payload_body`并针对其验证包装数据。[Geth 示例](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/txpool/validation.go#L133-L160) 。有关`VerifyBlobProof`的工作原理，请参阅 [KZG-commitment](https://dankradfeist.de/ethereum/2020/06/16/kate-polynomial-commitments.html) 和[trusted setups](https://vitalik.eth.limo/general/2022/03/14/trustedsetup.html)的介绍。\r\n\r\n### Curl\r\n\r\n#### eth_sendRawTransaction\r\n\r\n发送 blob 交易：\r\n\r\n```shell\r\ncurl --data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_sendRawTransaction\",\"params\":[\"0x03fa...\"],\"id\":1}' -H \"Content-Type: application/json\" -X POST $RPC_PROVIDER_URL | jq\r\n```\r\n\r\n```json\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"id\": 1,\r\n  \"result\": \"0x50dc1e2ec14cafb5acac600debe7b8765c73cbb7105ea33121284c3538ffbbc6\"\r\n}\r\n```\r\n\r\n获取 blob 交易体（使用标准的 EIP-2718 blob 交易`TransactionPayload`）：\r\n\r\n```shell\r\ncurl --data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionByHash\",\"params\":[\"0x50dc1e2ec14cafb5acac600debe7b8765c73cbb7105ea33121284c3538ffbbc6\"],\"id\":1}' -H 'Content-Type: application/json' -X POST $RPC_PROVIDER_URL | jq\r\n```\r\n\r\n```json\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"id\": 1,\r\n  \"result\": {\r\n    \"blockHash\": \"0xdd59ee9b848353ce4b30a907582d0e90f387e3622b34ca38dde796ab124cd5db\",\r\n    \"blockNumber\": \"0x51c6d4\",\r\n    \"from\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n    \"gas\": \"0x6270\",\r\n    \"gasPrice\": \"0x5da256f\",\r\n    \"maxFeePerGas\": \"0x357dee4a\",\r\n    \"maxPriorityFeePerGas\": \"0x14bb44\",\r\n    \"maxFeePerBlobGas\": \"0x4d29c618fa\",\r\n    \"hash\": \"0x50dc1e2ec14cafb5acac600debe7b8765c73cbb7105ea33121284c3538ffbbc6\",\r\n    \"input\": \"0x\",\r\n    \"nonce\": \"0x20\",\r\n    \"to\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n    \"transactionIndex\": \"0x7c\",\r\n    \"value\": \"0x0\",\r\n    \"type\": \"0x3\",\r\n    \"accessList\": [],\r\n    \"chainId\": \"0xaa36a7\",\r\n    \"blobVersionedHashes\": [\r\n      \"0x01ce755b14983c26efbad511bb2594f9aba54d199ffe762b507a1b5a9d4b3a61\"\r\n    ],\r\n    \"v\": \"0x1\",\r\n    \"r\": \"0xeeec1c9f227c6886c9901c2a6792e88f694abae4cd1d9e19a0cb284a9b4e8567\",\r\n    \"s\": \"0x5375e093b941ab9a25f53548b5b8728f6f2fb8de4822342a2d699fda362b6c4c\",\r\n    \"yParity\": \"0x1\"\r\n  }\r\n}\r\n```\r\n\r\n获取 blob 交易收据：\r\n\r\n```shell\r\ncurl --data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"0x50dc1e2ec14cafb5acac600debe7b8765c73cbb7105ea33121284c3538ffbbc6\"],\"id\":1}' -H 'Content-Type: application/json' -X POST $RPC_PROVIDER_URL | jq\r\n```\r\n\r\n```json\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"id\": 1,\r\n  \"result\": {\r\n    \"blobGasPrice\": \"0x80679abe1\",\r\n    \"blobGasUsed\": \"0x20000\",\r\n    \"blockHash\": \"0xdd59ee9b848353ce4b30a907582d0e90f387e3622b34ca38dde796ab124cd5db\",\r\n    \"blockNumber\": \"0x51c6d4\",\r\n    \"contractAddress\": null,\r\n    \"cumulativeGasUsed\": \"0xae3bec\",\r\n    \"effectiveGasPrice\": \"0x5da256f\",\r\n    \"from\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n    \"gasUsed\": \"0x5208\",\r\n    \"logs\": [],\r\n    \"logsBloom\": \"0x0000...\",\r\n    \"status\": \"0x1\",\r\n    \"to\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n    \"transactionHash\": \"0x50dc1e2ec14cafb5acac600debe7b8765c73cbb7105ea33121284c3538ffbbc6\",\r\n    \"transactionIndex\": \"0x7c\",\r\n    \"type\": \"0x3\"\r\n  }\r\n}\r\n```\r\n\r\n[用于生成 curl 命令的 Go 代码](./blob-eth_sendRawTransaction-curl-generator/main.go)：使用`go run main.go`运行。\r\n\r\n[包含生成的 curl 命令的脚本](./blob-eth_sendRawTransaction-curl-generator/blob_eth_sendRawTransaction.sh)：使用`./blob_eth_sendRawTransaction.sh`运行。\r\n\r\n[Etherscan](https://sepolia.etherscan.io/tx/0x50dc1e2ec14cafb5acac600debe7b8765c73cbb7105ea33121284c3538ffbbc6)：搜索非 blob 字段的信息，目前提供更好的用户体验和更多信息。\r\n\r\n[Blobscan](https://sepolia.blobscan.com/tx/0x4b97a7378f4d63fb07d0424fee2dabc810ec263cb6f9d5b23f9b6b8e01226b05)：搜索 blob 字段的信息。\r\n\r\n> **注意**：需要 RPC provider URL，或者需要运行节点来执行上述 curl 命令，并且仅在测试网中进行测试以防止资金损失。\r\n\r\n#### eth_sendTransaction\r\n\r\n发送 blob 交易：\r\n\r\n```shell\r\ncurl --data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_sendTransaction\",\"params\":[{\"accessList\":[],\"blobVersionedHashes\":[\"0x01ce755b14983c26efbad511bb2594f9aba54d199ffe762b507a1b5a9d4b3a61\"],\"blobs\":[\"0x0001...\"],\"chainId\":\"0xaa36a7\",\"commitments\":[\"0x854288889c16ba728d66f58ef6f40a2e0041a89e0453b1af934bf45c8a0e26e48e35cb3abade84db8b39d65b85265e3f\"],\"from\":\"0xd932073c0350d17057b6da602356b2ae92648465\",\"gas\":\"0x6270\",\"gasPrice\":null,\"hash\":\"0x23f2cbce16c8a144a653d9f919741143129d701f2cbe6cd7649b343ae6d0f0d3\",\"input\":\"0x\",\"maxFeePerBlobGas\":\"0x385d3c6730\",\"maxFeePerGas\":\"0xed46be3a46\",\"maxPriorityFeePerGas\":\"0x2540be400\",\"nonce\":\"0x29\",\"proofs\":[\"0xb54876f23a0bcf4d95d05bafd3091676562447b3a31ae1caaad208fb794a53aad24336fe0c636a882081aa57d220abb4\"],\"r\":\"0x0\",\"s\":\"0x0\",\"to\":\"0xd932073c0350d17057b6da602356b2ae92648465\",\"type\":\"0x3\",\"v\":\"0x0\",\"value\":\"0x0\",\"yParity\":\"0x0\"}],\"id\":1}' -H \"Content-Type: application/json\" -X POST http://127.0.0.1:8545 | jq\r\n```\r\n\r\n```json\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"id\": 1,\r\n  \"result\": \"0x158173e2e27938f0605232e32f5fd524506439b7555d027b273bb70d07a3c899\"\r\n}\r\n```\r\n\r\n`eth_sendTransaction`的 JSON 有效载荷：\r\n\r\n```json\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"method\": \"eth_sendTransaction\",\r\n  \"params\": [\r\n    {\r\n      \"accessList\": [],\r\n      \"blobVersionedHashes\": [\r\n        \"0x01ce755b14983c26efbad511bb2594f9aba54d199ffe762b507a1b5a9d4b3a61\"\r\n      ],\r\n      \"blobs\": [\r\n        \"0x0001...\"\r\n      ],\r\n      \"chainId\": \"0xaa36a7\",\r\n      \"commitments\": [\r\n        \"0x854288889c16ba728d66f58ef6f40a2e0041a89e0453b1af934bf45c8a0e26e48e35cb3abade84db8b39d65b85265e3f\"\r\n      ],\r\n      \"from\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n      \"gas\": \"0x6270\",\r\n      \"gasPrice\": null,\r\n      \"hash\": \"0x23f2cbce16c8a144a653d9f919741143129d701f2cbe6cd7649b343ae6d0f0d3\",\r\n      \"input\": \"0x\",\r\n      \"maxFeePerBlobGas\": \"0x385d3c6730\",\r\n      \"maxFeePerGas\": \"0xed46be3a46\",\r\n      \"maxPriorityFeePerGas\": \"0x2540be400\",\r\n      \"nonce\": \"0x29\",\r\n      \"proofs\": [\r\n        \"0xb54876f23a0bcf4d95d05bafd3091676562447b3a31ae1caaad208fb794a53aad24336fe0c636a882081aa57d220abb4\"\r\n      ],\r\n      \"r\": \"0x0\",\r\n      \"s\": \"0x0\",\r\n      \"to\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n      \"type\": \"0x3\",\r\n      \"v\": \"0x0\",\r\n      \"value\": \"0x0\",\r\n      \"yParity\": \"0x0\"\r\n    }\r\n  ],\r\n  \"id\": 1\r\n}\r\n```\r\n\r\n获取 blob 交易体（使用标准的 EIP-2718 blob 交易`TransactionPayload`）：\r\n\r\n```shell\r\ncurl --data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionByHash\",\"params\":[\"0x158173e2e27938f0605232e32f5fd524506439b7555d027b273bb70d07a3c899\"],\"id\":1}' -H 'Content-Type: application/json' -X POST $RPC_PROVIDER_URL | jq\r\n```\r\n\r\n```json\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"id\": 1,\r\n  \"result\": {\r\n    \"blockHash\": \"0x2daaeca77155d06e64c130170cb1b2f53ed8e26c0e02fe24b7d0208ecd782488\",\r\n    \"blockNumber\": \"0x51e294\",\r\n    \"from\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n    \"gas\": \"0x6270\",\r\n    \"gasPrice\": \"0x1ab5ea2e27\",\r\n    \"maxFeePerGas\": \"0xed46be3a46\",\r\n    \"maxPriorityFeePerGas\": \"0x2540be400\",\r\n    \"maxFeePerBlobGas\": \"0x385d3c6730\",\r\n    \"hash\": \"0x158173e2e27938f0605232e32f5fd524506439b7555d027b273bb70d07a3c899\",\r\n    \"input\": \"0x\",\r\n    \"nonce\": \"0x29\",\r\n    \"to\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n    \"transactionIndex\": \"0x16\",\r\n    \"value\": \"0x0\",\r\n    \"type\": \"0x3\",\r\n    \"accessList\": [],\r\n    \"chainId\": \"0xaa36a7\",\r\n    \"blobVersionedHashes\": [\r\n      \"0x01ce755b14983c26efbad511bb2594f9aba54d199ffe762b507a1b5a9d4b3a61\"\r\n    ],\r\n    \"v\": \"0x0\",\r\n    \"r\": \"0xd20ae6b93cee8467802601846df41bac73948553ce513e7cbe0e1998ff7e6fb9\",\r\n    \"s\": \"0x5edcbd6ccd4462d0a33a747a5d9bf5653703566808b38007e3ce4532a1611348\",\r\n    \"yParity\": \"0x0\"\r\n  }\r\n}\r\n```\r\n\r\n获取 blob 交易收据：\r\n\r\n```shell\r\ncurl --data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"0x158173e2e27938f0605232e32f5fd524506439b7555d027b273bb70d07a3c899\"],\"id\":1}' -H 'Content-Type: application/json' -X POST http://127.0.0.1:8545 | jq\r\n```\r\n\r\n```json\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"id\": 1,\r\n  \"result\": {\r\n    \"blobGasPrice\": \"0x44831ac79\",\r\n    \"blobGasUsed\": \"0x20000\",\r\n    \"blockHash\": \"0x2daaeca77155d06e64c130170cb1b2f53ed8e26c0e02fe24b7d0208ecd782488\",\r\n    \"blockNumber\": \"0x51e294\",\r\n    \"contractAddress\": null,\r\n    \"cumulativeGasUsed\": \"0xacdde\",\r\n    \"effectiveGasPrice\": \"0x1ab5ea2e27\",\r\n    \"from\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n    \"gasUsed\": \"0x5208\",\r\n    \"logs\": [],\r\n    \"logsBloom\": \"0x0000...\",\r\n    \"status\": \"0x1\",\r\n    \"to\": \"0xd932073c0350d17057b6da602356b2ae92648465\",\r\n    \"transactionHash\": \"0x158173e2e27938f0605232e32f5fd524506439b7555d027b273bb70d07a3c899\",\r\n    \"transactionIndex\": \"0x16\",\r\n    \"type\": \"0x3\"\r\n  }\r\n}\r\n```\r\n\r\n[用于生成 curl 命令的 Go 代码](./blob-eth_sendTransaction-curl-generator/main.go)：使用`go run main.go`运行。\r\n\r\n[包含生成的 curl 命令的脚本](./blob-eth_sendTransaction-curl-generator/blob_eth_sendTransaction.sh)：使用`./blob_eth_sendTransaction.sh`运行。\r\n\r\n[Etherscan](https://sepolia.etherscan.io/tx/0x158173e2e27938f0605232e32f5fd524506439b7555d027b273bb70d07a3c899)：搜索非 blob 字段的信息，目前提供更好的用户体验和更多信息。\r\n\r\n[Blobscan](https://sepolia.blobscan.com/tx/0x158173e2e27938f0605232e32f5fd524506439b7555d027b273bb70d07a3c899)：搜索 blob 字段的信息。\r\n\r\n> **注意**：大多数 RPC 提供程序（如 Infura 和 Alchemy）不提供`eth_sendTransaction`。\r\n\r\n> **注意**：对于 Geth： [这是一个正在进行的功能，但几乎完成](https://github.com/ethereum/go-ethereum/pull/28976) ，目前需要调整一些代码。签名字段被忽略，Geth 将使用解锁的账户对交易进行签名。\r\n\r\n### Go-SDK（在底层使用`eth_sendRawTransaction`）\r\n\r\n构造非 blob 字段（与 EIP-1559 交易相同）：\r\n\r\n```golang\r\nprivateKey, err := crypto.HexToECDSA(os.Getenv(\"PRIVATE_KEY\"))\r\nif err != nil {\r\n\tlog.Crit(\"failed to create private key\", \"err\", err)\r\n}\r\npublicKey := privateKey.Public()\r\npublicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)\r\nif !ok {\r\n\tlog.Crit(\"failed to cast public key to ECDSA\")\r\n}\r\nfromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)\r\n\r\nclient, err := ethclient.Dial(os.Getenv(\"RPC_PROVIDER_URL\"))\r\nif err != nil {\r\n\tlog.Crit(\"failed to connect to network\", \"err\", err)\r\n}\r\n\r\nchainID, err := client.NetworkID(context.Background())\r\nif err != nil {\r\n\tlog.Crit(\"failed to get network ID\", \"err\", err)\r\n}\r\n\r\nnonce, err := client.PendingNonceAt(context.Background(), fromAddress)\r\nif err != nil {\r\n\tlog.Crit(\"failed to get pending nonce\", \"err\", err)\r\n}\r\n\r\ngasTipCap, err := client.SuggestGasTipCap(context.Background())\r\nif err != nil {\r\n\tlog.Crit(\"failed to get suggest gas tip cap\", \"err\", err)\r\n}\r\n\r\ngasFeeCap, err := client.SuggestGasPrice(context.Background())\r\nif err != nil {\r\n\tlog.Crit(\"failed to get suggest gas price\", \"err\", err)\r\n}\r\n\r\ngasLimit, err := client.EstimateGas(context.Background(),\r\n\tethereum.CallMsg{\r\n\t\tFrom:       fromAddress,\r\n\t\tTo:         &fromAddress,\r\n\t\tGasFeeCap:  gasFeeCap,\r\n\t\tGasTipCap:  gasTipCap,\r\n\t\tValue:      big.NewInt(0),\r\n\t\t// 这里提供 BlobHash 如果交易时合约调用，\r\n\t\t// 并且合约内使用 blobhash 操作码\r\n\t})\r\nif err != nil {\r\n\tlog.Crit(\"failed to estimate gas\", \"err\", err)\r\n}\r\n```\r\n\r\n构造 blob 字段：\r\n\r\n```golang\r\n// 估算待打包区块的 blobFeeCap  \r\nparentHeader, err := client.HeaderByNumber(context.Background(), nil)\r\nif err != nil {\r\n\tlog.Crit(\"failed to get previous block header\", \"err\", err)\r\n}\r\nparentExcessBlobGas := eip4844.CalcExcessBlobGas(*parentHeader.ExcessBlobGas, *parentHeader.BlobGasUsed)\r\nblobFeeCap := eip4844.CalcBlobFee(parentExcessBlobGas)\r\n\r\nblob := randBlob()\r\nsideCar := makeSidecar([]kzg4844.Blob{blob})\r\nblobHashes := sideCar.BlobHashes()\r\n\r\nfunc makeSidecar(blobs []kzg4844.Blob) *types.BlobTxSidecar {\r\n\tvar (\r\n\t\tcommitments []kzg4844.Commitment\r\n\t\tproofs      []kzg4844.Proof\r\n\t)\r\n\r\n\tfor _, blob := range blobs {\r\n\t\tc, _ := kzg4844.BlobToCommitment(blob)\r\n\t\tp, _ := kzg4844.ComputeBlobProof(blob, c)\r\n\r\n\t\tcommitments = append(commitments, c)\r\n\t\tproofs = append(proofs, p)\r\n\t}\r\n\r\n\treturn &types.BlobTxSidecar{\r\n\t\tBlobs:       blobs,\r\n\t\tCommitments: commitments,\r\n\t\tProofs:      proofs,\r\n\t}\r\n}\r\n```\r\n> **注意**：一个 blob 交易可以有 0 到 6 个 blob，因为每个区块的最大 blob 数量为 `MAX_BLOB_GAS_PER_BLOCK` / `GAS_PER_BLOB` = 786432 / 131072 = 6。\r\n\r\n> **注意**：Geth 的交易池（最广泛采用的执行客户端）将拒绝0 blob 大小的 blob 交易，在将交易添加到交易池之前验证时返回 `blobless blob transaction` 错误（ [验证交易之前添加到 tx pool](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/txpool/validation.go#L120-L122)）。\r\n\r\n签名并发送交易：\r\n\r\n```golang\r\ntx := types.NewTx(&types.BlobTx{\r\n\tChainID:    uint256.MustFromBig(chainID),\r\n\tNonce:      nonce,\r\n\tGasTipCap:  uint256.MustFromBig(gasTipCap),\r\n\tGasFeeCap:  uint256.MustFromBig(gasFeeCap),\r\n\tGas:        gasLimit * 12 / 10,\r\n\tTo:         fromAddress,\r\n\tBlobFeeCap: uint256.MustFromBig(blobFeeCap),\r\n\tBlobHashes: blobHashes,\r\n\tSidecar:    sideCar,\r\n})\r\n\r\nauth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)\r\nif err != nil {\r\n\tlog.Crit(\"failed to create transactor\", \"chainID\", chainID, \"err\", err)\r\n}\r\n\r\nsignedTx, err := auth.Signer(auth.From, tx)\r\nif err != nil {\r\n\tlog.Crit(\"failed to sign the transaction\", \"err\", err)\r\n}\r\n\r\nerr = client.SendTransaction(context.Background(), signedTx)\r\nif err != nil {\r\n\tlog.Crit(\"failed to send the transaction\", \"err\", err)\r\n}\r\n```\r\n\r\n[完整实现](./blob-send-transaction-Go-SDK/main.go)：使用 `go run main.go` 运行。\r\n\r\n## 费用估算和提高\r\n\r\n### 估算 Blob 交易的费用\r\n\r\n#### Blob 费用和 Gas\r\n\r\nBlob 基础费用具有确定性计算方法：\r\n\r\n```python\r\ndef get_blob_base_fee(header: Header) -> int:\r\n    return fake_exponential(\r\n        MIN_BLOB_BASE_FEE,\r\n        header.excess_blob_gas,\r\n        BLOB_BASE_FEE_UPDATE_FRACTION\r\n    )\r\n\r\ndef fake_exponential(factor: int, numerator: int, denominator: int) -> int:\r\n    i = 1\r\n    output = 0\r\n    numerator_accum = factor * denominator\r\n    while numerator_accum > 0:\r\n        output += numerator_accum\r\n        numerator_accum = (numerator_accum * numerator) // (denominator * i)\r\n        i += 1\r\n    return output // denominator\r\n```\r\n\r\n`MIN_BLOB_BASE_FEE` 为 1 wei。\r\n\r\n`excess_blob_gas` 表示历史上比 `TARGET_BLOB_GAS_PER_BLOCK` * `Totel Number of Blocks` 多使用的“额外”累积的 gas，但它被限制为 0（>= 0）。\r\n\r\n`BLOB_BASE_FEE_UPDATE_FRACTION` 为 3338477，它控制 blob 基础费用的增长比率。\r\n\r\n`fake_exponential` 通过 [Taylor 展开](https://en.wikipedia.org/wiki/Taylor_series) 确定性地（向下取整）计算 `factor * e ** (numerator / denominator)`，以防止由于模拟指数函数的不同规则而导致共识分歧。\r\n\r\n> **注意**：blob 基础费用是基于 [指数 EIP-1559 机制](https://dankradfeist.de/ethereum/2022/03/16/exponential-eip1559.html) 计算的，其中 `excess_blob_gas` 将使 blob 基础费用增加到市场的预期价格。与此同时，每个区块的预期 blob 数量仍将是每个区块的目标的 Blob 数量，目前为 3。\r\n\r\nBlob Gas：每个 Blob 131072（0x20000）gas，每字节 1 gas，但增加 gas 的最小单位是一个 Blob。\r\n\r\n### Gas 费用：Blob vs Calldata\r\n\r\n#### Gas\r\n- **Blob 存储**：每字节大约 1 gas（因为字段为 `BLS_MODULUS`），按 Blob 单位收费。\r\n- **Calldata**：每个非零字节 16 gas，每个零字节 4 gas。\r\n\r\n> **注意**：充分利用每个 Blob，避免支付未使用空间的费用。\r\n\r\n#### Gas 价格\r\n- **Blob 交易**：使用 Blob 基础费用计算成本。\r\n- **EIP-1559 交易**：成本由 EIP-1559 基础费用加上小费决定。\r\n\r\n#### 大小\r\n- **Blob**：每个 Blob > 127KiB 并且 < 128KiB，因为字段为 `BLS_MODULUS`。\r\n- **Calldata**：受区块的 gas limit 限制，此外每个交易受执行客户端中的限制（ [一个著名的 128KiB 限制](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/txpool/legacypool/legacypool.go#L54-L50) ）。\r\n\r\n#### 结论\r\n\r\n基于供需的[多维费用市场](https://ethresear.ch/t/multidimensional-eip-1559/11651) 。难以事先确定哪个更便宜。\r\n\r\n- **一些直觉**：\r\n    - Calldata 用于许多目的：合约调用、Rollup DA 等。→ blob 更便宜！\r\n    - 仅 blob 承诺的 32 字节哈希存在于 EVM 中，设计用于 Rollup。→ blob 更便宜！\r\n    - Blob 相对稀缺，目前目标为每个区块 3 个 Blob，而每个交易可以包含一个 Calldata 字段，每个区块可容纳数百个交易。→ 如果 blob 交易拥挤，Calldata 甚至可能更便宜！\r\n\r\n- **统计**：\r\n    - Blobscan 的仪表板：[Sepolia](https://sepolia.blobscan.com/stats/block) 和 [Goerli](https://goerli.blobscan.com/stats/block)。请注意，Blob 中的零字节（不知道它是有效的 0 还是虚拟值）被视为 Calldata 中的零字节，因此节省的费用被高估。添加用于比较 gas 费用的仪表板将更有用。\r\n\r\n- **其他可能性**：\r\n    - 使用私人交易服务（例如 flashbots），可以直接向构建者支付小费。\r\n\r\n### 优先处理交易\r\n\r\n与 EIP-1559 交易一样，只需增加有效小费：`min(exec tip, exec cap - base fee)`。\r\n\r\n- [Geth](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/miner/ordering.go#L62-L70) 和 [Nethermind](https://github.com/NethermindEth/nethermind/blob/bf658d8525d8b1b3007c49ddc38b12a061e033a2/src/Nethermind/Nethermind.Consensus/Comparers/GasPriceTxComparerHelper.cs#L11-L30) 在从交易池中选择交易时使用优先小费。\r\n- 即使对于更复杂的 MEV 策略（例如解决多维背包问题），增加有效小费也会给区块构建者带来更高的收入。\r\n\r\n### 提高待处理交易的费用（替换具有相同 nonce 的交易）\r\n\r\n由于 blob 交易池对最低提高比率有限制（例如 [Geth](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/txpool/blobpool/blobpool.go#L1145-L1150) 和 [Nethermind](https://github.com/NethermindEth/nethermind/blob/bf658d8525d8b1b3007c49ddc38b12a061e033a2/src/Nethermind/Nethermind.TxPool/Comparison/CompareReplacedBlobTx.cs#L30-L32)），为了替换已发送的交易，需要至少将 `exec tip`、`exec cap` 和 `blob cap` 提高至少 100%，这种防御措施是为了防止 DoS 攻击，因为 blob 交易的有效负载很大。\r\n\r\n```golang\r\nconst escalateMultiplier = 2\r\n\r\n// Bumping gas fee.\r\ngasTipCap = new(big.Int).Mul(gasTipCap, big.NewInt(escalateMultiplier))\r\ngasFeeCap = new(big.Int).Mul(gasFeeCap, big.NewInt(escalateMultiplier))\r\nblobFeeCap = new(big.Int).Mul(blobFeeCap, big.NewInt(escalateMultiplier))\r\n\r\ntx := types.NewTx(&types.BlobTx{\r\n\tChainID:    uint256.MustFromBig(chainID),\r\n\tNonce:      nonce,\r\n\tGasTipCap:  uint256.MustFromBig(gasTipCap),\r\n\tGasFeeCap:  uint256.MustFromBig(gasFeeCap),\r\n\tGas:        gasLimit * 12 / 10,\r\n\tTo:         fromAddress,\r\n\tBlobFeeCap: uint256.MustFromBig(blobFeeCap),\r\n\tBlobHashes: blobHashes,\r\n\tSidecar:    sideCar,\r\n})\r\n\r\nauth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)\r\nif err != nil {\r\n\tlog.Crit(\"failed to create transactor\", \"chainID\", chainID, \"err\", err)\r\n}\r\n\r\nsignedTx, err = auth.Signer(auth.From, tx)\r\nif err != nil {\r\n\tlog.Crit(\"failed to sign the transaction\", \"err\", err)\r\n}\r\n\r\nerr = client.SendTransaction(context.Background(), signedTx)\r\nif err != nil {\r\n\tlog.Crit(\"failed to send the transaction\", \"err\", err)\r\n}\r\n```\r\n\r\n> **注意**：替换待处理交易的罚金很高，通常发生在 Blob 交易拥挤时。可以先尝试重新提交交易，看看是否已被 Blob 交易池驱逐，否则提高 gas 价格。\r\n\r\n> **注意**：错误消息示例：`replacement transaction underpriced: new tx gas fee cap 67186612857 <= 44791075238 queued + 100% replacement penalty`。\r\n\r\n## 基于 Blob 交易池实现的故障排除\r\n\r\n通过gossip协议在以太坊网络中传播交易，并临时存储在交易池中。由于 blob 交易携带大量有效负载，主要客户端在其交易池中实施了某些限制。强调这些限制中的一些对故障排除和防止 blob 交易被拒绝或优先级降低（卡住）可能至关重要。\r\n\r\n### Geth（主要执行客户端，许多 RPC 提供者都是基于它的）：\r\n\r\n- 一个地址不能同时在传统池和 blob 池中持有交易：`address already reserved`。\r\n- 替换交易需要显著提高 `exec tip`、`exec cap` 和 `blob cap`（100%）：`replacement transaction underpriced`。\r\n- 每个账户的最大待处理 blob 交易数量限制：`account limit exceeded: pooled 16 txs`。\r\n- [Blob 交易驱逐](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/txpool/blobpool/evictheap.go#L94-L115) 依赖于每个账户的 3 个最低费用（`exec tip`、`exec cap` 和 `blob cap`）。\r\n- 限制每个交易中 Blob 的数量最多为 6（区块中允许的最大数量）：`too many blobs in transaction: have 7, permitted 6`。\r\n- 排除非 blob 交易：`blobless blob transaction`。\r\n- 不允许nonce 间隔的 blob 交易：`nonce too high`。\r\n\r\n> **参考**：[Geth 的 blob pool \"手册\"](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/txpool/blobpool/blobpool.go#L132-L293)。\r\n\r\n### Nethermind（排名第二执行客户端）：\r\n\r\n- 明确设置标志以启用 blob 池。\r\n- 地址不能同时在传统池和 blob 池中持有交易。\r\n- 每个账户待出处理的 blob 交易有最大数量限制。\r\n- 拒绝 `MaxPriorityFeePerGas` 低于 1 gwei 的 blob。\r\n- 不允许 nonce 间隔的 blob 交易。\r\n- 拒绝用较少的 blob 替换 blob 交易。\r\n\r\n> **参考**：[Blob Pool Unit Tests](https://github.com/NethermindEth/nethermind/blob/bf658d8525d8b1b3007c49ddc38b12a061e033a2/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.Blobs.cs)。\r\n\r\n## 新的操作码和预编译\r\n\r\n### BLOBHASH 操作码\r\n\r\nEIP-4844 引入了 `BLOBHASH` 操作码，其 Gas成本为 3。合约可以使用它来检索交易 blob 的哈希。它接受一个 `index` 参数，指定 blob 的 `index`；如果 `index` 超出范围，则返回一个零的 bytes32 值。参见 [Geth 实现](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/vm/eips.go#L273-L283)。\r\n\r\n### 点评估（Point Evaluation）预编译\r\n\r\n在 0x0A 处有一个预编译，用于验证 KZG 证明，该证明声称一个 blob（由承诺表示）在给定点上评估为给定值。每次调用消耗 50000 gas。\r\n\r\n**EIP-4844 中的演示代码**：\r\n\r\n```python\r\ndef point_evaluation_precompile(input: Bytes) -> Bytes:\r\n    \"\"\"\r\n    给定与多项式 p(x) 相对应的承诺和 KZG 证明，验证 p(z) = y。\r\n    还要验证所提供的承诺与所提供的版本控制哈希值（versioned_hash）是否匹配。 \r\n    \"\"\"\r\n    #数据编码如下: versioned_hash | z | y | commitment | proof | with z and y being padded 32 byte big endian values\r\n    assert len(input) == 192\r\n    versioned_hash = input[:32]\r\n    z = input[32:64]\r\n    y = input[64:96]\r\n    commitment = input[96:144]\r\n    proof = input[144:192]\r\n\r\n    # 验证承诺与 versioned_hash 是否匹配\r\n    assert kzg_to_versioned_hash(commitment) == versioned_hash\r\n\r\n    # 使用 z 和 y （大段格式） 验证 KZG 证明\r\n    assert verify_kzg_proof(commitment, z, y, proof)\r\n\r\n    #  返回 FIELD_ELEMENTS_PER_BLOB 和 BLS_MODULUS 扩展到 32 字节大端值\r\n    return Bytes(U256(FIELD_ELEMENTS_PER_BLOB).to_be_bytes32() + U256(BLS_MODULUS).to_be_bytes32())\r\n```\r\n\r\n[Geth 实现](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/vm/contracts.go#L1094-L1128)。\r\n\r\n### 示例\r\n\r\n#### 直接调用点评估预编译\r\n\r\n```golang\r\npointEvaluationPrecompileAddress := common.HexToAddress(\"0x0A\")\r\nblob := randBlob()\r\nsideCar := makeSidecar([]kzg4844.Blob{blob})\r\nversionedHash := sideCar.BlobHashes()[0]\r\npoint := randFieldElement()\r\ncommitment := sideCar.Commitments[0]\r\n\r\nproof, claim, err := kzg4844.ComputeProof(blob, point)\r\nif err != nil {\r\n\tlog.Crit(\"failed to create KZG proof at point\", \"err\", err)\r\n}\r\n\r\nvar calldata []byte\r\ncalldata = append(calldata, versionedHash.Bytes()...)\r\ncalldata = append(calldata, point[:]...)\r\ncalldata = append(calldata, claim[:]...)\r\ncalldata = append(calldata, commitment[:]...)\r\ncalldata = append(calldata, proof[:]...)\r\n\r\ngasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{\r\n\tFrom:      fromAddress,\r\n\tTo:        &pointEvaluationPrecompileAddress,\r\n\tGasFeeCap: gasFeeCap,\r\n\tGasTipCap: gasTipCap,\r\n\tValue:     big.NewInt(0),\r\n\tData:      calldata,\r\n})\r\nif err != nil {\r\n\tlog.Crit(\"failed to estimate gas\", \"err\", err)\r\n}\r\n\r\ntx := types.NewTx(&types.DynamicFeeTx{\r\n\tChainID:   chainID,\r\n\tNonce:     nonce,\r\n\tGasTipCap: gasTipCap,\r\n\tGasFeeCap: gasFeeCap,\r\n\tGas:       gasLimit,\r\n\tTo:        &pointEvaluationPrecompileAddress,\r\n\tValue:     big.NewInt(0),\r\n\tData:      calldata,\r\n})\r\n```\r\n\r\n[完整实现](./invoke-EIP-4844-point-evaluation-precompile/main.go)：使用 `go run main.go` 运行。\r\n\r\n[成功的示例（具有有效的 calldata）](https://sepolia.etherscan.io/tx/0x021e5ee48c1eaa747ff4fd4bdffc5cd595d9fff7c2447a7aabca00fa1605f6fc)：calldata + 转账（21000）+ 点评估预编译（50000）。\r\n\r\n[失败的示例（没有 calldata）](https://sepolia.etherscan.io/tx/0x8236fa15da85272a47ed390491fafc28447db8af9057ccb3bd0c3ce2047559a7)：回退，消耗了提供的所有 gas。\r\n\r\n#### 在合约内调用点评估预编译\r\n\r\n**一个玩具合约**：\r\n```solidity\r\n// SPDX-License-Identifier: MIT\r\n// EVM VERSION: cancun\r\n// Enable optimization: 2000000\r\npragma solidity ^0.8.24;\r\n\r\ncontract PointEvaluationPrecompileDemo {\r\n    address private constant POINT_EVALUATION_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000000A;\r\n    uint256 private constant BLS_MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513;\r\n    uint256 private constant HASH_OPCODE_BYTE = 0x49;\r\n\r\n    event ProofVerificationSuccess(bytes32 indexed versionedHash, uint256 indexed point, bytes32 indexed claim);\r\n    event ProofVerificationFailure(bytes32 indexed versionedHash, uint256 indexed point, bytes32 indexed claim);\r\n\r\n    function verifyProofAndEmitEvent(\r\n        bytes32 claim,\r\n        bytes memory commitment,\r\n        bytes memory proof\r\n    ) external {\r\n        require(commitment.length == 48, \"Commitment must be 48 bytes\");\r\n        require(proof.length == 48, \"Proof must be 48 bytes\");\r\n\r\n        bytes32 versionedHash = blobhash(0);\r\n\r\n        // Compute random challenge point.\r\n        uint256 point = uint256(keccak256(abi.encodePacked(versionedHash))) % BLS_MODULUS;\r\n\r\n        bytes memory pointEvaluationCalldata = abi.encodePacked(\r\n            versionedHash,\r\n            point,\r\n            claim,\r\n            commitment,\r\n            proof\r\n        );\r\n\r\n        (bool success,) = POINT_EVALUATION_PRECOMPILE_ADDRESS.staticcall(pointEvaluationCalldata);\r\n\r\n        if (success) {\r\n            emit ProofVerificationSuccess(versionedHash, point, claim);\r\n        } else {\r\n            emit ProofVerificationFailure(versionedHash, point, claim);\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n[部署的合约地址](https://sepolia.etherscan.io/address/0x45d38ded8a95656f72be2bd4de44f33e10eba1da)：尚未验证合约代码，因为 Etherscan 和 Blockscout 不支持 `EVM VERSION: cancun`。[例如](https://sepolia.etherscan.io/address/0x45d38ded8a95656f72be2bd4de44f33e10eba1da#code)\r\n\r\n```assembly\r\n'fe'(Unknown Opcode)\r\nLOG2\r\nPUSH5 0x6970667358\r\n'22'(Unknown Opcode)\r\nSLT\r\nSHA3\r\n'b1'(Unknown Opcode)\r\nPUSH25 0x82b1641cca545969b8e7de1dc1b6c11c110b36a71d2dda89e8\r\n'b3'(Unknown Opcode)\r\n```\r\n\r\n[使用 Go SDK 调用合约](./invoke-EIP-4844-demo-contract/main.go)：硬编码 gas limit，因为 Geth 的 EstimateGas 实现在内部调用 `blobhash` 操作码的合约调用中存在问题（`blobhash` 字段未通过 RPC 传输），希望这已在 [v1.13.13 版本的 geth 中得到修复](https://github.com/ethereum/go-ethereum/releases/tag/v1.13.13)。\r\n\r\n[成功的示例](https://sepolia.etherscan.io/tx/0x813b770e73d3cec73977437c3d52ef3b511982e9e8b71b63e4a557061afd0ca1#eventlog)。\r\n\r\n[失败的示例](https://sepolia.etherscan.io/tx/0xe0d210944193a52b7999532e6a91761dd2d0d71c4e5dcf9c06f09a65df4f7d45#eventlog)：将声明数组中的第一个字节设置为 0，合约返回错误：`error verifying kzg proof: can’t verify opening proof` [代码参考](https://github.com/ethereum/go-ethereum/blob/93c541ad563124e81d125c7ebe78938175229b2e/core/vm/contracts.go#L1123-L1125)。\r\n\r\n## Blob 浏览器\r\n\r\n- **Blobscan**：[Sepolia](https://sepolia.blobscan.com) 和 [Goerli](https://goerli.blobscan.com)。\r\n    - **区块**：blob 大小、blob gas 价格、blob gas 使用量、blob gas 限制、blob 作为 calldata gas 等。\r\n    - **交易**：总 blob 大小、blob 费用、blob gas 使用量、blob gas 价格、blob 作为 calldata gas 等。\r\n    - **Blob**：版本化哈希、承诺、大小、数据等。\r\n    - **指标**：\r\n        - **区块**：区块数量、blob gas 使用量、blob 费用、blob gas 价格、节省的费用、节省的 gas 等。\r\n        - **交易**：交易数量、blob gas 费用、唯一发送方、唯一接收方等。\r\n        - **Blob**：blob 数量、唯一 blob、blob 大小、平均 blob 大小等。\r\n    - **[开源](https://github.com/Blobscan)**： [支持私有部署](https://docs.blobscan.com/docs/installation) 。\r\n\r\n## 查询 Blob 内容\r\n\r\n### 动机之一：从 DA 同步\r\n\r\n如果所有节点都宕机，用户可以在自己的计算机上运行一个节点，从 DA 同步以恢复链的状态，然后将他们的资金从 L2 提取到 L1。\r\n\r\n### 共识节点（未修剪的 Blob）\r\n\r\n- Beacon API 的 [getBlobSidecars](https://ethereum.github.io/beacon-APIs/#/Beacon/getBlobSidecars)：\r\n  - [Lighthouse 示例](./query-blob-content/lighthouse.txt)\r\n  - [Prysm 示例](./query-blob-content/prysm.txt)\r\n\r\n> **注意**：流行的 RPC 提供商对共识客户端 API 的支持不佳。\r\n\r\n### Blob 服务提供商\r\n- [Blobscan 示例](./query-blob-content/blobscan.txt)。\r\n\r\n> **注意**：获取 blob 数据、kzg 承诺和 kzg 证明后，你可以在本地验证 blob 内容（因为 blob 哈希存储在链上），无需“信任”服务提供商。\r\n\r\n> **注意**：其他潜在的方式： [如果数据在 30 天后被删除，用户如何访问旧的 blob？](https://notes.ethereum.org/@vbuterin/proto_danksharding_faq#If-data-is-deleted-after-30-days-how-would-users-access-older-blobs)。\r\n\r\n---\r\n\r\n\r\n\r\n> 本翻译由 [DeCert.me](https://decert.me/) 协助支持， 在 DeCert 构建可信履历，为自己码一个未来。"},"author":{"user":"https://learnblockchain.cn/people/15","address":"0x3aA0c5bDce1EBedcB3FaabF69CE159da72bdD0A7"},"history":null,"timestamp":1710168000,"version":1}