-
Notifications
You must be signed in to change notification settings - Fork 491
Add RPC endpoint testing_buildBlockV1 #710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f6a782c
fd7c82b
30f28de
967fc4d
e81918a
61698b4
7273058
cadd123
41855df
519429b
8958124
bb72039
2798132
20b3be1
b73f381
3c2c673
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,115 @@ | ||||||
| # `testing_buildBlockV1` | ||||||
|
|
||||||
| This document specifies the `testing_buildBlockV1` RPC method. This method is a debugging and testing tool that simplifies the block production process into a single call. It is intended to replace the multi-step workflow of sending transactions, calling `engine_forkchoiceUpdated` with `payloadAttributes`, and then calling `engine_getPayload`. | ||||||
|
|
||||||
| This method is considered sensitive and is intended for testing environments only. See [**Security Considerations**](#security-considerations) for more details. | ||||||
|
|
||||||
| ### Request | ||||||
|
|
||||||
| * method: `testing_buildBlockV1` | ||||||
| * params: | ||||||
| 1. `parentBlockHash`: `DATA`, 32 Bytes - block hash of the parent of the requested block. | ||||||
| 2. `payloadAttributes`: `Object` - instance of [`PayloadAttributesV3`](../engine/cancun.md#payloadattributesv3) | ||||||
| 3. `transactions`: `Array of DATA` - an array of raw, signed transactions (hex-encoded `0x...` strings) to forcibly include in the generated block. | ||||||
| 4. `extraData`: `DATA|null`, 0 to 32 Bytes - data to be set as the `extraData` field of the built block. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another feature I'd like here is a gas limit. Besides the obvious reason to create bigger blocks for benchmarks, there might also be other reasons to control the gas limit, for instance instead of going up, bringing it down (this has influence on the fee market).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can obviously be done via client config, but with EELS as driver for the test/benchmark generator it is handy to be able to control this from EELS directly, without having to edit a config to set a specific gas target. |
||||||
|
|
||||||
| ### Response | ||||||
|
|
||||||
| * result: `OBJECT` - instance of [`engine_getPayloadV5`](../engine/osaka.md#response) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not really a good description, are we returning the method? or the same response as getPayloadV5
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I read this as defining the schema of the response object to the the same as |
||||||
| * error: code and message set in case an exception happens while building the requested block. | ||||||
|
|
||||||
| ### Specification | ||||||
|
|
||||||
| * The client MUST build a new execution payload using the block specified by `parentBlockHash` as its parent. | ||||||
|
|
||||||
| * The client MUST use the provided `payloadAttributes` to define the context of the new block. | ||||||
|
|
||||||
| * The client MUST include all transactions from the `transactions` array on the block's transaction list, in the order they were provided. | ||||||
|
|
||||||
| * The client MUST NOT include any transactions from its local transaction pool. The resulting block MUST only contain the transactions specified in the `transactions` array. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
It might be a useful (optional?) feature to allow for selectiung txpool transactions in the case where the transaction array is empty.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would keep it as simple as possible in
Also here, if majority of people will opt to have such feature, I'm fine with adding it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think my wording keeps the option open without specifying what the client should do in the case of empty transaction array. In Besu's case, we actually have a similar RPC method we use for internal testing,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think if an empty array is passed then an empty transaction list should be used, otherwise it's non-deterministic, in the case of test chains and devnets, you may very well be trying to generate empty blocks. And to that effect, only the txs in the array should be included.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if we make an empty array be "build an empty block" and null be "use your mempool if you want to"? Or is that too much?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be fine with that, as long as the option is possible in some form that is spec-compliant, don't mind about the syntax.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The transactions parameter MUST accept an array, not null. Accepting mempool transactions contradicts the intent:
With null + mempool, we are back to multi-step: eth_sendRawTransaction + testing_buildBlockV1.[1] This endpoint will be heavily used for benchmarking. We should keep the underlying logic straightforward and slim. As always, we can extend if there is pressing need for the mempool path for testing later. Lets keep v1 simple for now. [1]: mempool test case
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then don't send null for your use case
marcindsobczak marked this conversation as resolved.
|
||||||
|
|
||||||
| * If `extraData` is provided, the client MUST set the `extraData` field of the resulting payload to this value. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The extraData field is a bit weird, since we didn't use it before, it needs to be piped through to the miner
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. extraData is part of the blockhash though, is it not? I think it's important that all the fields that would contribute to the blockHash of a real block are properly represented otherwise the returned blockhash wouldn't match the actual/expected blockhash (which is very useful for testing).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With extraData edits you can generate the same block, but with a different hash, which is a nice property. All other fields will edit something in the block which is observable from EVM. |
||||||
|
|
||||||
| * This method MUST NOT modify the client's canonical chain or head block. It is a read-only method for payload generation. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if invalid txs get added?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a test case for that, it's a defined error in my PR |
||||||
|
|
||||||
| ### Security Considerations | ||||||
|
|
||||||
| * **HIGHLY SENSITIVE**: This method is a powerful debugging tool intended for testing environments ONLY. | ||||||
|
|
||||||
| * It allows for the creation of arbitrary blocks. It can be in used to forcibly include any valid transaction. | ||||||
|
|
||||||
| * This method MUST NOT be exposed on public-facing RPC APIs. | ||||||
|
|
||||||
| * It is strongly recommended that this method, and its `testing_` namespace, be disabled by default. Enabling it should require an explicit command-line flag or configuration setting by the node operator. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this fit better under
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea of creating a new namespace is to prevent enabling this method by mistake, together with other @spencer-tb has another idea - to make it
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Besu actually already has a similar endpoint called I think debug_ prefix is also fine because there are already debug_ methods that can do quite a lot of damage so it would be a misconfiguration to leave them publicly accessible IMO.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem with the debug namespace is that it contains the tracing methods, and basically everyone needs/wants the tracing methods, so debug is very often forced to be on, even for people who don't want the expose "dangerous methods" I have no problem with a new namespace called testing being created, but I also have no problem with it going under debug since debug has been treated like a catchall and one more dangerous method isn't going to break the mess that is the debug namespace any more than it already is.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's probably the trace ones that are most dangerous, in terms of resource consumption at least 😆
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also hide it behind the jwt?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not opposed to hiding behind jwt but there are a few things to consider:
Since this is a new namespace, I think it's overkill to require it to be hidden behind auth |
||||||
|
|
||||||
| ### Example | ||||||
|
|
||||||
| #### Request | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "id": 1, | ||||||
| "jsonrpc": "2.0", | ||||||
| "method": "testing_buildBlockV1", | ||||||
| "params": [ | ||||||
| { | ||||||
| "parentBlockHash": "0x3b8fb240d288781d4f1e1d32f4c1550BEEFDEADBEEFDEADBEEFDEADBEEFDEAD", | ||||||
| "payloadAttributes": { | ||||||
| "timestamp": "0x6705D918", | ||||||
| "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||||||
| "suggestedFeeRecipient": "0x0000000000000000000000000000000000000000", | ||||||
| "withdrawals": [], | ||||||
| "parentBeaconBlockRoot": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884365149a42212e8822" | ||||||
| }, | ||||||
| "transactions": [ | ||||||
| "0xf86c0a8504a817c80082520894...cb61163c917540a0c64c12b8f50015", | ||||||
| "0xf86b0f8504a817c80082520894...5443c01e69d3b0c5112101564914a2" | ||||||
| ], | ||||||
| "extraData": "0x746573745F6E616D65" | ||||||
| } | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| #### Response (Success) | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "id": 1, | ||||||
| "jsonrpc": "2.0", | ||||||
| "result": { | ||||||
| "executionPayload": { | ||||||
| "blockHash": "0x8980a3a7f8b16053c4dec86e1050e6378e176272517852f8c5b56f34e9a0f9b6", | ||||||
| "parentHash": "0x3b8fb240d288781d4f1e1d32f4c15509a2b7538b8e0e719541a50a31a2631a01", | ||||||
| "feeRecipient": "0x0000000000000000000000000000000000000000", | ||||||
| "stateRoot": "0xca3699d05d3369680315af1d03c6f8f73188945f3c83756bde2575ddc25c6040", | ||||||
| "receiptsRoot": "0x2e0617b0c3545d1668e8d8cb530a6c71b0a8f82875b1d0b38c352718016feff6", | ||||||
| "logsBloom": "0x00...00", | ||||||
| "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||||||
| "blockNumber": "0x1b4", | ||||||
| "gasLimit": "0x1c9c380", | ||||||
| "gasUsed": "0xa410", | ||||||
| "timestamp": "0x6705D918", | ||||||
| "extraData": "0x746573745F6E616D65", | ||||||
| "baseFeePerGas": "0x7", | ||||||
| "excessBlobGas": "0x0", | ||||||
| "blobGasUsed": "0x0", | ||||||
| "transactions": [ | ||||||
| "0xf86c0a8504a817c80082520894...cb61163c917540a0c64c12b8f50015", | ||||||
| "0xf86b0f8504a817c80082520894...5443c01e69d3b0c5112101564914a2" | ||||||
| ], | ||||||
| "parentBeaconBlockRoot": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884365149a42212e8822", | ||||||
| "depositRequests": [], | ||||||
| "consolidationRequests": [] | ||||||
| }, | ||||||
| "blockValue": "0x1b681a965684000", | ||||||
| "blobsBundle": { | ||||||
| "commitments": [], | ||||||
| "blobs": [], | ||||||
| "kzgProofs": [] | ||||||
| }, | ||||||
| "shouldOverrideBuilder": false, | ||||||
| "executionRequests": [] | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the current spec is tied to specific types like
PayloadAttributesV3andengine_getPayloadV5does this implicitly mean that for each fork which would change these types (PayloadAttributesV4) we thus also need a newtesting_buildBlockVxinstance? (Which is fine).Another main motivation of this method is bypassing the txpool. We could start a client and only inject specific txs into txpool, but if we then ask it to build a block via normal route (engine API) then it is not guaranteed all txs are in it, and the order of the txs is also not guaranteed.