Skip to content
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

feat(rpc): implement debug_executionWitness API #30613

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

0x00101010
Copy link

@0x00101010 0x00101010 commented Oct 16, 2024

Implement a new debug_executionWitness API to return execution witness for desired blocks.

This is helpful for

  1. fault proofs and ZK applications, instead of trying to retrieve MPT tries and contract codes by hashes, they can now use this API to retrieve relevant information.
  2. cross client validation

This PR upstreams changes to op-geth, and the API has already been implemented in reth as well

Codes: transformMap(w.Codes),
State: transformMap(w.State),
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This should be moved to the API part. It's the encoding specific to that API endpoint, not something generic for the witness + we don't want a JSON encoding in general because it's very expensive.

Copy link
Author

Choose a reason for hiding this comment

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

Done, understood that JSON encoding can be very expensive. How do you feel about merge it for now given

  1. currently performance is not a big concern
  2. with JSON encoding, it helps with readability, troubleshooting, ease of parsing and cross client compatibility until we have an agreed upon format (maybe through an EIP)?

@0x00101010 0x00101010 requested a review from karalabe October 18, 2024 15:26
chain, _ := core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil)

blockNum := 10
_, bs, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), blockNum, nil)
Copy link
Member

Choose a reason for hiding this comment

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

Could you add some transactions to the test chain so that we have some more data in the witnesses other than the coinbase payment?

e.g.

tx, err := types.SignTx(types.NewTransaction(0, common.Address{0x00}, new(big.Int), params.TxGas, block.BaseFee(), nil), signer, key)
			if err != nil {
				panic(err)
			}
			block.AddTx(tx)

Copy link
Author

Choose a reason for hiding this comment

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

done

@karalabe
Copy link
Member

karalabe commented Nov 1, 2024

Been thinking about this but would like to push back on the map format.

The original witness format I used sent the codes and the trie nodes as an array of bytes. This PR changes that to return a map, keyed by hash.

This is IMO a problem, because if I send a malicious witness (keys do not match the content), and the receiving side blindly trusts it, then you can get arbitrary results. You could say that the receiver must verify the hashes, but that means hashing the content, so the effort to verify is the same as if the hashes weren't transmitted in the first place.

So: transmitting the hash keys bloats the witness up; and it potentially permits clients to be less secure. IMO, this should be reverted to my format.

The other quirk my format has and this doesn't is the transmission of the root hash. The reasoning is the same, by omitting the root hash, a verified has to 100% work correctly, otherwise it cannot arrive to the correct result. In this case however, some bug could still "verify" the correct root, since you have it anyway.

@fjl
Copy link
Contributor

fjl commented Dec 10, 2024

@0x00101010 we need to make a decision here about the output format of this method. The specific thing we want addressed is: state tree nodes should be provided as a list, without their hashes. We want the client side of this method to verify the result, by first hashing all nodes, and then building the tree themselves.

@0x00101010
Copy link
Author

@0x00101010 we need to make a decision here about the output format of this method. The specific thing we want addressed is: state tree nodes should be provided as a list, without their hashes. We want the client side of this method to verify the result, by first hashing all nodes, and then building the tree themselves.

This makes sense, generally in favor of @karalabe's proposal. We're actually queueing up a task to submit a execution-apis PR with more defined information about this API.

@meyer9
Copy link

meyer9 commented Dec 10, 2024

we need to make a decision here about the output format of this method. The specific thing we want addressed is: state tree nodes should be provided as a list, without their hashes. We want the client side of this method to verify the result, by first hashing all nodes, and then building the tree themselves.

@fjl @0x00101010 I think we're leaning towards just an array of hex preimages as the output format:

["0xf00f00...", "0xdeadbeef...", ...]

We can eliminate client disagreement in the response by not segmenting into codes/keys/state, and I don't see any reason we need to segment the preimages by purpose (there may be cases where there are two purposes).

I'm working on some test cases to be able to submit a PR to execution-apis once there's agreement on a response schema.

Would love to hear what people think!

@holiman
Copy link
Contributor

holiman commented Dec 11, 2024

We can eliminate client disagreement in the response by not segmenting into codes/keys/state, and I don't see any reason we need to segment the preimages by purpose (there may be cases where there are two purposes).

I have a feeling that they should be segmented. Due to non-separation, we had some disambiguation problems in fastsync, long ago, since it was possible to set code arbitrarily. Thus, users/attackers could create code which looked state, and presence-checking while traversing the missing tree would hit the code and mistakenly think "I already have this subtree".

I cannot think of an analoguous attack in this case, since the roots are ostensibly checked against the parent header root, but all the same, I have an aversion towards mixing code and state.

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.

6 participants