-
Notifications
You must be signed in to change notification settings - Fork 246
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
fix: fix get_storage_proof #2463
base: main
Are you sure you want to change the base?
Conversation
bb6c6e7
to
fba3ea6
Compare
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.
Can I ask you to please do a few things:
- Can you add a test for the changed functionality?
- In a few places it seems to me like you're using manual loops to collect items into a
Vec
. The old code was the same, but it used iterators instead and collected them into aHashSet
. Can you still use iterators? It's sufficient to change fromHashSet<_>
toVec<_>
in the collect call (I think, again let me know if I'm missing something). I'll elaborate on this in my other comment below.
let nodes: Vec<NodeHashToNodeMapping> = | ||
ClassCommitmentTree::get_proofs(tx, block_number, class_hashes, class_root_idx)? | ||
.into_iter() | ||
.flatten() | ||
.map(|(node, node_hash)| NodeHashToNodeMapping { | ||
let nodes = ClassCommitmentTree::get_proofs(tx, block_number, class_hashes, class_root_idx)?; | ||
|
||
let mut proof: Vec<NodeHashToNodeMapping> = Vec::new(); | ||
for node in nodes { | ||
for (merkle, node_hash) in node { | ||
let node_map = NodeHashToNodeMapping { | ||
node_hash, | ||
node: ProofNode(node), | ||
}) | ||
.collect::<HashSet<_>>() | ||
.into_iter() | ||
.collect(); | ||
let classes_proof = NodeHashToNodeMappings(nodes); | ||
node: ProofNode(merkle), | ||
}; | ||
proof.push(node_map); | ||
} | ||
} | ||
let classes_proof = NodeHashToNodeMappings(proof); |
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.
This is what I'm referring to in point 2. above. You changed a bunch of code here, but the only change in functionality as far as I can see is that now it's a Vec
instead of a HashSet
. Could you please just do it like this?
let nodes: Vec<NodeHashToNodeMapping> =
ClassCommitmentTree::get_proofs(tx, block_number, class_hashes, class_root_idx)?
.into_iter()
.flatten()
.map(|(node, node_hash)| NodeHashToNodeMapping {
node_hash,
node: ProofNode(node),
})
.collect::<Vec<_>>()
.into_iter()
.collect();
let classes_proof = NodeHashToNodeMappings(nodes);
(Note the .collect
call now uses Vec<_>
instead of HashSet<_>
. This will keep the order. The order was not being kept before due to us using a HashSet
for the nodes.)
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.
+1
let contract_root = tx | ||
.contract_root(block_number, csk.contract_address) | ||
.context("Querying contract's root")? | ||
.unwrap_or_default(); |
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.
Why are you using unwrap_or_default
here? What will happen now is if the contract root is missing from the DB, this will return a proof for some invalid 0x0
node hash. I'm pretty sure that if it's missing we should just return []
? Let me know if I'm missing something.
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.
I don't think you're missing anything, I just copied
pathfinder/crates/rpc/src/pathfinder/methods/get_proof.rs
Lines 316 to 319 in cab975c
let contract_root = tx | |
.contract_root(header.number, input.contract_address) | |
.context("Querying contract's root")? | |
.unwrap_or_default(); |
root, | ||
)?; | ||
|
||
for node in nodes { |
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.
Same here, no need to loop yourself, please use iterators like we used to.
StorageCommitmentTree::get_proofs(tx, block_number, contract_addresses, storage_root_idx)?; | ||
|
||
let mut proof: Vec<NodeHashToNodeMapping> = Vec::new(); | ||
for node in nodes { |
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.
Same here.
I can see 2 issues here:
This case actually works fine and you can test it, with for example this old contract: For
gives
ℹ️
gives:
Which is still fine in this very case. The problem arises when a contract does have storage updates and you still need just the root of that contract's trie, which does exist. The easiest solution would be to extend the existing API and add a proper field here, and then you'd have the |
let ContractRoot(node_hash) = contract_root; | ||
|
||
let merkle = TrieNode::Binary { | ||
left: Felt::default(), | ||
right: Felt::default(), | ||
}; | ||
|
||
let node_map = NodeHashToNodeMapping { | ||
node_hash, | ||
node: ProofNode(node), | ||
}) | ||
.collect::<HashSet<_>>() | ||
.into_iter() | ||
.collect(); | ||
node: ProofNode(merkle), | ||
}; | ||
|
||
proofs.push(NodeHashToNodeMappings(nodes)); | ||
proofs.push(NodeHashToNodeMappings(vec![node_map])); |
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.
I think this is incorrect. A root of the storage trie for a particular contract is not a proof. As per my main comment - we should extend the API so that when you query for a proof of a contract you get it's class
, nonce
and storage_root
in return. So the fix would be in get_contract_proofs
instead of get_contract_storage_proofs
.
fba3ea6
to
082856e
Compare
@sistemd Sorry about the over correction on the loops. I've returned them to using iterators. There's another issue when dealing with contract storage proof, that the @CHr15F0x I've opened an issue for starkware explaining this problem with the api definition and we're currently talking about a potential solution. I wholeheartedly agree that what I proposed is patchwork at best, and not an issue with pathfinder itself, just the constraints imposed on it. However it's a away for snos to receive all information it requires, and we sorely needed them to properly prepare for v0_8 release. Whatever solution is decided on the api, we'll surely need to update this in the future to properly return the data required. Cheers. |
082856e
to
01ae205
Compare
@GMKrieger |
a568faf
to
36a1656
Compare
The storage proofs were being returned out of order, and certain use cases of the endpoint where there are no storage keys but the root was still needed were not implemented.
36a1656
to
f73935b
Compare
The storage proofs were being returned out of order, and certain use cases of the endpoint where there are no storage keys but the root was still needed were not implemented.
Fixes #2458
Unable to Request Contract Storage State Root Without Storage Values in RPC v0.8
Problem Statement
In RPC v0.8, it is not possible to request a contract's storage state root without also requesting storage values. This is a regression from previous functionality and breaks certain use cases where only the root hash is needed.
Previous Behavior (pathfinder_getStorageProof)
The ContractData struct previously provided both pieces of information:
Importantly, the endpoint accepted an empty array of keys, making it possible to retrieve just the root for contracts without storage keys (e.g., newly deployed but unused contracts).
Current Implementation (v0.8)
The new modular approach separates proofs into distinct components:
Issue Details
In the current implementation there's no way to retrieve the root when there are no storage keys to request
This limitation breaks functionality for cases where: