Skip to content

Remove Block RLP hooks - #304

Merged
StephenButtolph merged 26 commits into
mainfrom
StephenButtolph/remove-block-rlp-hook
Aug 10, 2026
Merged

Remove Block RLP hooks#304
StephenButtolph merged 26 commits into
mainfrom
StephenButtolph/remove-block-rlp-hook

Conversation

@StephenButtolph

@StephenButtolph StephenButtolph commented Aug 6, 2026

Copy link
Copy Markdown

Why this should be merged

While I do think it's nice to reduce the number of required hooks to implement. The primary point of this PR is introducing an efficient types.BlockBytes function that efficiently combines header bytes and body bytes into block bytes.

Technically, this BlockBytes function doesn't need to live in libevm (I could implement it in SAE) - but I think that it makes sense to solidify the hook implementations against a concrete requirement.

benchstat: referenceBlockBytes (reencode.txt) vs BlockBytes (blockbytes.txt)

goos: darwin
goarch: arm64
pkg: github.com/ava-labs/libevm/core/types
cpu: Apple M2 Max
                                              │  reencode.txt  │            blockbytes.txt            │
                                              │     sec/op     │    sec/op     vs base                │
BlockBytes/0_txs_0_uncles_0_withdrawals-12       2618.5n ± 58%   201.6n ±  5%  -92.30% (p=0.000 n=10)
BlockBytes/1_txs_0_uncles_0_withdrawals-12       4069.0n ± 18%   237.3n ± 12%  -94.17% (p=0.000 n=10)
BlockBytes/0_txs_1_uncles_0_withdrawals-12       4823.5n ±  5%   349.1n ±  6%  -92.76% (p=0.000 n=10)
BlockBytes/0_txs_0_uncles_1_withdrawals-12       3167.5n ±  1%   211.6n ±  6%  -93.32% (p=0.000 n=10)
BlockBytes/10_txs_0_uncles_0_withdrawals-12     11912.5n ±  6%   536.8n ± 11%  -95.49% (p=0.000 n=10)
BlockBytes/10_txs_2_uncles_4_withdrawals-12     16616.5n ±  6%   727.9n ± 12%  -95.62% (p=0.000 n=10)
BlockBytes/100_txs_0_uncles_0_withdrawals-12     98.445µ ±  9%   2.546µ ± 11%  -97.41% (p=0.000 n=10)
BlockBytes/100_txs_2_uncles_16_withdrawals-12   101.776µ ± 24%   3.007µ ± 13%  -97.05% (p=0.000 n=10)
geomean                                          11.58µ          566.3n        -95.11%

                                              │  reencode.txt  │            blockbytes.txt            │
                                              │      B/op      │     B/op       vs base               │
BlockBytes/0_txs_0_uncles_0_withdrawals-12        3421.0 ± 0%     640.0 ± 0%   -81.29% (p=0.000 n=10)
BlockBytes/1_txs_0_uncles_0_withdrawals-12        4143.0 ± 0%     768.0 ± 0%   -81.46% (p=0.000 n=10)
BlockBytes/0_txs_1_uncles_0_withdrawals-12       6.217Ki ± 0%   1.250Ki ± 0%   -79.89% (p=0.000 n=10)
BlockBytes/0_txs_0_uncles_1_withdrawals-12        3638.0 ± 0%     640.0 ± 0%   -82.41% (p=0.000 n=10)
BlockBytes/10_txs_0_uncles_0_withdrawals-12      9.714Ki ± 0%   2.000Ki ± 0%   -79.41% (p=0.000 n=10)
BlockBytes/10_txs_2_uncles_4_withdrawals-12     15.417Ki ± 0%   3.376Ki ± 0%   -78.10% (p=0.000 n=10)
BlockBytes/100_txs_0_uncles_0_withdrawals-12     66.85Ki ± 0%   13.26Ki ± 0%   -80.17% (p=0.000 n=10)
BlockBytes/100_txs_2_uncles_16_withdrawals-12    75.21Ki ± 0%   16.01Ki ± 0%   -78.72% (p=0.000 n=10)
geomean                                          11.07Ki        2.188Ki        -80.23%

                                              │  reencode.txt  │            blockbytes.txt            │
                                              │   allocs/op    │  allocs/op     vs base               │
BlockBytes/0_txs_0_uncles_0_withdrawals-12        42.000 ± 0%     1.000 ± 0%   -97.62% (p=0.000 n=10)
BlockBytes/1_txs_0_uncles_0_withdrawals-12        56.000 ± 0%     1.000 ± 0%   -98.21% (p=0.000 n=10)
BlockBytes/0_txs_1_uncles_0_withdrawals-12        62.000 ± 0%     1.000 ± 0%   -98.39% (p=0.000 n=10)
BlockBytes/0_txs_0_uncles_1_withdrawals-12        49.000 ± 0%     1.000 ± 0%   -97.96% (p=0.000 n=10)
BlockBytes/10_txs_0_uncles_0_withdrawals-12      161.000 ± 0%     1.000 ± 0%   -99.38% (p=0.000 n=10)
BlockBytes/10_txs_2_uncles_4_withdrawals-12      210.000 ± 0%     1.000 ± 0%   -99.52% (p=0.000 n=10)
BlockBytes/100_txs_0_uncles_0_withdrawals-12    1163.000 ± 0%     1.000 ± 0%   -99.91% (p=0.000 n=10)
BlockBytes/100_txs_2_uncles_16_withdrawals-12   1230.000 ± 0%     1.000 ± 0%   -99.92% (p=0.000 n=10)
geomean                                           155.7           1.000        -99.36%

Block reencoding showed up as a significant performance bottleneck while serving blocks. Reencoding currently happens when calling rawdb.ReadBlock. By implementing this change the GetAncestors optimization observes:

benchstat: GetAncestors optimization in avalanchego before & after this change

                        │      old      │                 new                  │
                        │    sec/op     │    sec/op     vs base                │
GetAncestors/batched-12   16223.1µ ± 5%   655.8µ ± 17%  -95.96% (p=0.000 n=10)

                        │     B/op      │     B/op      vs base                │
GetAncestors/batched-12   14.247Mi ± 0%   4.008Mi ± 0%  -71.87% (p=0.000 n=10)

                        │   allocs/op   │  allocs/op   vs base                │
GetAncestors/batched-12   251.167k ± 0%   8.277k ± 0%  -96.70% (p=0.000 n=10)

How this works

While the goal of this PR was adding BlockBytes - most of the effort fell to removing BlockRLPFieldsForEncoding and BlockRLPFieldPointersForDecoding.

These functions needed to be removed because they allowed a hook provider to break the Header + Body = Block assumption. There was nothing stopping someone from re-ordering body fields when they were serialized just as a body vs when they were serialized as part of a block.

This PR re-uses BodyRLPFieldsForEncoding and BodyRLPFieldPointersForDecoding so that we are guaranteed that the body bytes follow the same order as the block bytes.

How this was tested

  • Added fuzz test and benchmarks for BlockBytes
  • Extended testing to assert extras equvilance.

Comment thread core/types/block.libevm.go Outdated
@StephenButtolph
StephenButtolph marked this pull request as ready for review August 6, 2026 20:58
@StephenButtolph StephenButtolph self-assigned this Aug 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Updates block/body RLP encoding to route extras through Body hooks (removing the block-specific proxy), and adds a fast path to combine RLP-encoded header/body into block bytes.

Changes:

  • Replace BlockRLPProxy-based block encoding/decoding with Body hook-driven field assembly.
  • Introduce BlockBytes(headerBytes, bodyBytes) fast-path and add fuzz/bench coverage for equivalence/performance.
  • Update tests and temporary extras to use Body hooks and payload access patterns.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
core/types/tempextras.libevm_test.go Updates temporary extras test to set/get body extras via extras.Body and BodyRLPFieldsForEncoding.
core/types/rlp_payload.libevm.go Adds (*Block).extraOrNil() helper to avoid allocating extras when unregistered.
core/types/block.libevm_test.go Adds body-extra roundtrip test plus fuzz/bench coverage for the new BlockBytes constructor.
core/types/block.libevm.go Reworks block RLP encoding/decoding to use Body hook fields; adds BlockBytes.
core/types/block.go Replaces extblock.hooks with extblock.extra and wires encode/decode through extraOrNil().
core/types/backwards_compat.libevm_test.go Removes now-obsolete BlockRLP* hook methods from compat extras test type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/types/block.libevm_test.go
Comment thread core/types/block.libevm.go Outdated
Comment thread core/types/block.libevm.go Outdated
Comment thread core/types/block.libevm.go
Comment thread core/types/block.libevm_test.go Outdated
Comment thread core/types/block.libevm_test.go Outdated
Comment thread core/types/block.libevm_test.go Outdated
Comment thread core/types/block.libevm_test.go Outdated
Comment thread core/types/block.libevm_test.go
Comment thread core/types/block.libevm_test.go Outdated
Comment thread core/types/block.libevm.go Outdated
Comment thread core/types/block.libevm_test.go
Comment thread core/types/block.libevm_test.go Outdated

@ARR4N ARR4N left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for me to re-review after suggestions.

@StephenButtolph
StephenButtolph merged commit c1a6474 into main Aug 10, 2026
13 checks passed
@StephenButtolph
StephenButtolph deleted the StephenButtolph/remove-block-rlp-hook branch August 10, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants