Skip to content

Conversation

Tristan-Wilson
Copy link
Member

@Tristan-Wilson Tristan-Wilson commented Oct 2, 2025

This adds infrastructure to enhance one-step proofs with additional data required by the arbitrator, particularly for custom DA systems.

The proof enhancer system intercepts one-step proofs that have an enhancement flag set by the arbitrator. When the arbitrator needs additional data that it cannot access directly (like DA certificates or preimage data), it sets this flag along with a marker byte indicating what type of enhancement is needed.

The system includes:

  • ProofEnhancementManager: Routes proofs to appropriate enhancers based on marker bytes
  • ReadPreimageProofEnhancer: Handles DA preimage read requests (marker 0xDA)
  • ValidateCertificateProofEnhancer: Handles certificate validation requests (marker 0xDB)

Both enhancers retrieve the certificate from the sequencer message stored in the inbox, then use the daprovider.Validator interface to generate the appropriate proofs. This design allows the arbitrator to request DA operations without needing to store large certificates in its limited WASM memory.

The enhanced proofs are then sent to the OSP (on-chain prover) which can verify them against the actual DA system's validation logic.

The corresponding Arbitrator PR is #3548 (already merged)

This adds infrastructure to enhance one-step proofs with additional
data required by the arbitrator, particularly for custom DA systems.

The proof enhancer system intercepts one-step proofs that have an
enhancement flag set by the arbitrator. When the arbitrator needs
additional data that it cannot access directly (like DA certificates
or preimage data), it sets this flag along with a marker byte indicating
what type of enhancement is needed.

The system includes:
- ProofEnhancementManager: Routes proofs to appropriate enhancers based on marker bytes
- ReadPreimageProofEnhancer: Handles DA preimage read requests (marker 0xDA)
- ValidateCertificateProofEnhancer: Handles certificate validation requests (marker 0xDB)

Both enhancers retrieve the certificate from the sequencer message stored
in the inbox, then use the daprovider.Validator interface to generate the
appropriate proofs. This design allows the arbitrator to request DA operations
without needing to store large certificates in its limited WASM memory.

The enhanced proofs are then sent to the OSP (on-chain prover) which can
verify them against the actual DA system's validation logic.
Copy link

github-actions bot commented Oct 2, 2025

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
2153 3 2150 0
View the top 3 failed tests by shortest run time
TestVersion30
Stack Traces | 6.960s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
�[90mTime to activate multicall: 177.817626ms�[0;0m
�[90mwasm multicall deployed at 0x457b1BA688E9854BDbed2f473F7510C476A3dA09�[0;0m
    precompile_inclusion_test.go:90: goroutine 454989 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.3/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x40837d0, 0xc0271b4a80}, {0x4041a00, 0xc0a0c977a0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x55
        github.com/offchainlabs/nitro/system_tests.Require(0xc0271b4a80, {0x4041a00, 0xc0a0c977a0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/common_test.go:1723 +0x5d
        github.com/offchainlabs/nitro/system_tests.testPrecompiles(0xc0271b4a80, 0x1e, {0xc07c2e3db0, 0x6, 0x0?})
        	/home/runner/work/nitro/nitro/system_tests/precompile_inclusion_test.go:90 +0x371
        github.com/offchainlabs/nitro/system_tests.TestVersion30(0xc0271b4a80?)
        	/home/runner/work/nitro/nitro/system_tests/precompile_inclusion_test.go:67 +0x798
        testing.tRunner(0xc0271b4a80, 0x3ccbb78)
        	/opt/hostedtoolcache/go/1.25.3/x64/src/testing/testing.go:1934 +0xea
        created by testing.(*T).Run in goroutine 1
        	/opt/hostedtoolcache/go/1.25.3/x64/src/testing/testing.go:1997 +0x465
        
    precompile_inclusion_test.go:90: �[31;1m [] execution aborted (timeout = 5s) �[0;0m
--- FAIL: TestVersion30 (6.96s)
TestEthSyncing
Stack Traces | 23.490s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
DEBUG[10-17|12:04:57.993] Executing EVM call finished              runtime="174.475µs"
DEBUG[10-17|12:04:57.993] Served eth_call                          reqid=2738  duration="355.494µs"
INFO [10-17|12:04:57.994] New Key                                  name=Faucet            Address=0xaF24Ca6c2831f4d4F629418b50C227DF0885613A
DEBUG[10-17|12:04:57.993] Reinjecting stale transactions           count=0
DEBUG[10-17|12:04:57.994] Served eth_getTransactionReceipt         reqid=10231 duration="341.106µs"
INFO [10-17|12:04:57.994] New Key                                  name=RollupOwner       Address=0x57Ff0F473737a1c161bfF9efDF016F7991585088
DEBUG[10-17|12:04:57.993] Served eth_subscribe                     reqid=375   duration="26.449µs"
INFO [10-17|12:04:57.994] New Key                                  name=Sequencer         Address=0xb386a74Dcab67b66F8AC07B4f08365d37495Dd23
TRACE[10-17|12:04:57.994] Handled RPC response                     reqid=375   duration="4.208µs"
TRACE[10-17|12:04:57.993] Handled RPC response                     reqid=6296  duration="2.715µs"
DEBUG[10-17|12:04:57.993] Served eth_maxPriorityFeePerGas          reqid=10228 duration="776.865µs"
DEBUG[10-17|12:04:57.993] Executing EVM call finished              runtime="185.506µs"
DEBUG[10-17|12:04:57.994] Served eth_call                          reqid=10230 duration="701.698µs"
TRACE[10-17|12:04:57.993] Handled RPC response                     reqid=10229 duration="2.445µs"
TRACE[10-17|12:04:57.994] Handled RPC response                     reqid=2738  duration="3.076µs"
DEBUG[10-17|12:04:57.994] Executing EVM call finished              runtime="403.723µs"
DEBUG[10-17|12:04:57.994] Served eth_call                          reqid=4532  duration="451.763µs"
INFO [10-17|12:04:57.994] New Key                                  name=Validator         Address=0x83FFCFaCE2Fb0E1286686815503608A16EF41e47
TRACE[10-17|12:04:57.994] Handled RPC response                     reqid=4532  duration="2.655µs"
--- FAIL: TestEthSyncing (23.49s)
TestTimeboostExpressLaneTransactionHandling
Stack Traces | 29.910s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
DEBUG[10-17|12:12:24.234] Dereferenced trie from memory database   nodes=18  size=3.56KiB   time="69.298µs"  gcnodes=836  gcsize=159.68KiB  gctime=2.167849ms  livenodes=104   livesize=20.70KiB
DEBUG[10-17|12:12:24.234] Dereferenced trie from memory database   nodes=18  size=3.59KiB   time="54.081µs"  gcnodes=854  gcsize=163.27KiB  gctime=2.221469ms  livenodes=86    livesize=17.11KiB
DEBUG[10-17|12:12:24.234] Dereferenced trie from memory database   nodes=19  size=3.60KiB   time="48.881µs"  gcnodes=873  gcsize=166.87KiB  gctime=2.27025ms   livenodes=67    livesize=13.51KiB
DEBUG[10-17|12:12:24.235] Dereferenced trie from memory database   nodes=16  size=3.25KiB   time="47.278µs"  gcnodes=889  gcsize=170.12KiB  gctime=2.317378ms  livenodes=51    livesize=10.26KiB
DEBUG[10-17|12:12:24.235] Dereferenced trie from memory database   nodes=16  size=3.25KiB   time="48.37µs"   gcnodes=905  gcsize=173.37KiB  gctime=2.365647ms  livenodes=35    livesize=7.01KiB
DEBUG[10-17|12:12:24.235] Dereferenced trie from memory database   nodes=16  size=3.38KiB   time="44.252µs"  gcnodes=921  gcsize=176.75KiB  gctime=2.409779ms  livenodes=19    livesize=3.63KiB
DEBUG[10-17|12:12:24.235] Dereferenced trie from memory database   nodes=19  size=3.63KiB   time="43.862µs"  gcnodes=940  gcsize=180.38KiB  gctime=2.45354ms   livenodes=0     livesize=0.00B
DEBUG[10-17|12:12:24.235] Dereferenced trie from memory database   nodes=0   size=0.00B     time=160ns       gcnodes=940  gcsize=180.38KiB  gctime=2.45362ms   livenodes=0     livesize=0.00B
DEBUG[10-17|12:12:24.235] Dereferenced trie from memory database   nodes=0   size=0.00B     time=120ns       gcnodes=940  gcsize=180.38KiB  gctime=2.45367ms   livenodes=0     livesize=0.00B
INFO [10-17|12:12:24.235] Blockchain stopped
DEBUG[10-17|12:12:24.239] redis producer: check responses starting
DEBUG[10-17|12:12:24.288] checkResponses                           responded=0 errored=0 checked=0
DEBUG[10-17|12:12:24.288] Dereferenced trie from memory database   nodes=0   size=0.00B     time="1.052µs"   gcnodes=940  gcsize=180.38KiB  gctime=2.454231ms  livenodes=0     livesize=0.00B
DEBUG[10-17|12:12:24.288] Dereferenced trie from memory database   nodes=0   size=0.00B     time=922ns       gcnodes=940  gcsize=180.38KiB  gctime=2.454451ms  livenodes=0     livesize=0.00B
DEBUG[10-17|12:12:24.288] Dereferenced trie from memory database   nodes=0   size=0.00B     time=130ns       gcnodes=940  gcsize=180.38KiB  gctime=2.454501ms  livenodes=0     livesize=0.00B
DEBUG[10-17|12:12:24.288] Dereferenced trie from memory database   nodes=0   size=0.00B     time=121ns       gcnodes=940  gcsize=180.38KiB  gctime=2.454541ms  livenodes=0     livesize=0.00B
DEBUG[10-17|12:12:24.288] Dereferenced trie from memory database   nodes=0   size=0.00B     time=401ns       gcnodes=940  gcsize=180.38KiB  gctime=2.454752ms  livenodes=0     livesize=0.00B
TRACE[10-17|12:12:24.288] Refreshing our lock                      id=auctioneer-fbccc759-1760703115350666834
TRACE[10-17|12:12:24.289] P2P networking is spinning down
--- FAIL: TestTimeboostExpressLaneTransactionHandling (29.91s)

📣 Thoughts on this report? Let Codecov know! | Powered by Codecov

Copy link
Contributor

@rauljordan rauljordan left a comment

Choose a reason for hiding this comment

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

I really enjoyed reading the unit tests in this PR. I just found a few small items that seem problematic, but other than that it is almost ready for approval

MarkerCustomDAValidateCertificate = 0xDB
)

// ProofEnhancer enhances one-step proofs with additional data
Copy link
Contributor

Choose a reason for hiding this comment

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

It could be helpful to add information about why exactly these proofs need to be enhanced here for future reference. This will help folks understand the rationale without having to hunt down the source PR

Copy link
Member Author

Choose a reason for hiding this comment

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

Added some more description to the comments here.


// ProofEnhancementManager manages multiple proof enhancers by marker type
type ProofEnhancementManager struct {
enhancers map[byte]ProofEnhancer
Copy link
Contributor

Choose a reason for hiding this comment

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

A type alias for byte here would make it clear it is a ProofMarkerType

Copy link
Member Author

Choose a reason for hiding this comment

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

Added the type alias.

}

// Find marker at end of proof
if len(proof) < 1 { // Need at least the marker byte
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this come before the enhancement flag check, otherwise we will panic?

Copy link
Member Author

Choose a reason for hiding this comment

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

Above we're checking if len(proof) == 0, so we know there's at least 1 byte before checking proof[0].

I changed this line to be len(proof) < 2 since we need the enhancement flag and the marker byte.

}

// Extract and validate certificate from sequencer message
if len(sequencerMessage) < 41 {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a magic number, maybe we can extract or document what it means at minimum

Copy link
Member Author

Choose a reason for hiding this comment

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

Extracted all the hardcoded values

}

// Extract certificate from sequencer message (skip 40-byte header)
if len(sequencerMessage) < 41 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here

Copy link
Member Author

Choose a reason for hiding this comment

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

Extracted all the hardcoded values

rauljordan
rauljordan previously approved these changes Oct 7, 2025
@rauljordan rauljordan assigned tsahee and unassigned rauljordan Oct 7, 2025
)

const (
// Enhancement flag in machine status byte
Copy link
Contributor

Choose a reason for hiding this comment

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

add to comment (first byte in proof)

// Enhancement flag in machine status byte
ProofEnhancementFlag = 0x80

// Marker bytes for different enhancement types
Copy link
Contributor

Choose a reason for hiding this comment

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

ad to comment: (last byte in an un-enhanced proof)

@@ -0,0 +1,100 @@
// Copyright 2025, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md
package server_arb
Copy link
Contributor

Choose a reason for hiding this comment

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

one thing that's not really apparent yet is how you'll use this interface.
Having it in server_arb suggests the intention is to have the execution client connect to the custom_da proof enhancer directly.
I think (not sure) a better option would be that the arbitrator will keep working as it is (almost stateless, responds to requests without making them), and have the consensus side so all the custom-da communication, including enhancing proofs (after it got the response from arbitrator and before it sends it to a one-step-proof).
In that case server_arb will be left unaffected.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, I put it in the wrong place. I'll move it to validator/proofenhancement/

// NewProofEnhancementManager creates a new proof enhancement manager
func NewProofEnhancementManager() *ProofEnhancementManager {
return &ProofEnhancementManager{
enhancers: make(map[ProofMarker]ProofEnhancer),
Copy link
Contributor

Choose a reason for hiding this comment

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

why not register both enhancers directly here?
That'll allow you to keep the separate enhancers private, and if I understand correctly - these exact two enhancers are used by our "static" node

Copy link
Member Author

Choose a reason for hiding this comment

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

I made a convenience method to construct the enhancement manager directly with the customda enhancers, that Nitro can use.

I didn't make the enhancers private because system tests need to create them and wrap them to get the evil enhancer behavior.

@tsahee tsahee assigned Tristan-Wilson and unassigned tsahee Oct 16, 2025
@Tristan-Wilson Tristan-Wilson removed their assignment Oct 17, 2025
@Tristan-Wilson Tristan-Wilson requested a review from tsahee October 17, 2025 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants