Skip to content

Commit

Permalink
Merge pull request #2491 from eqlabs/chris/add-class-commitment
Browse files Browse the repository at this point in the history
feat: re-add class_commitment to pathfinder_getProof output
  • Loading branch information
CHr15F0x authored Jan 15, 2025
2 parents 8a2d331 + e7bc5cd commit 95f5966
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 8 deletions.
96 changes: 88 additions & 8 deletions crates/rpc/src/pathfinder/methods/get_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,12 @@ impl crate::dto::SerializeForVersion for ProofNodes {
let mut s = serializer.serialize_struct()?;
match self.0 {
TrieNode::Binary { left, right } => {
s.serialize_field("type", &"binary")?;
s.serialize_field("left", left)?;
s.serialize_field("right", right)?;
let mut inner = serializer.serialize_struct()?;
inner.serialize_field("left", left)?;
inner.serialize_field("right", right)?;
let inner = inner.end()?;

s.serialize_field("binary", &inner)?;
}
TrieNode::Edge { child, path } => {
let value = Felt::from_bits(path).unwrap();
Expand All @@ -143,9 +146,12 @@ impl crate::dto::SerializeForVersion for ProofNodes {
len: path.len(),
};

s.serialize_field("type", &"edge")?;
s.serialize_field("path", &path)?;
s.serialize_field("child", child)?;
let mut inner = serializer.serialize_struct()?;
inner.serialize_field("path", &path)?;
inner.serialize_field("child", child)?;
let inner = inner.end()?;

s.serialize_field("edge", &inner)?;
}
}
s.end()
Expand Down Expand Up @@ -208,6 +214,11 @@ pub struct GetProofOutput {
/// [contract_proof](GetProofOutput#contract_proof) is the global state
/// commitment.
state_commitment: Option<StateCommitment>,
/// Required to verify that the hash of the class commitment and the root of
/// the [contract_proof](GetProofOutput::contract_proof) matches the
/// [state_commitment](Self#state_commitment). Present only for Starknet
/// blocks 0.11.0 onwards.
class_commitment: Option<ClassCommitment>,

/// Membership / Non-membership proof for the queried contract
contract_proof: ProofNodes,
Expand All @@ -222,7 +233,8 @@ impl crate::dto::SerializeForVersion for GetProofOutput {
serializer: crate::dto::Serializer,
) -> Result<crate::dto::Ok, crate::dto::Error> {
let mut serializer = serializer.serialize_struct()?;
serializer.serialize_optional("state_commitment", self.state_commitment)?;
serializer.serialize_optional_with_null("state_commitment", self.state_commitment)?;
serializer.serialize_optional_with_null("class_commitment", self.class_commitment)?;
serializer.serialize_field("contract_proof", &self.contract_proof)?;
serializer.serialize_optional("contract_data", self.contract_data.clone())?;
serializer.end()
Expand All @@ -231,6 +243,11 @@ impl crate::dto::SerializeForVersion for GetProofOutput {

#[derive(Debug, PartialEq)]
pub struct GetClassProofOutput {
/// Required to verify that the hash of the class commitment and the root of
/// the [contract_proof](GetProofOutput::contract_proof) matches the
/// [state_commitment](Self#state_commitment). Present only for Starknet
/// blocks 0.11.0 onwards.
class_commitment: Option<ClassCommitment>,
/// Membership / Non-membership proof for the queried contract classes
class_proof: ProofNodes,
}
Expand All @@ -241,6 +258,7 @@ impl crate::dto::SerializeForVersion for GetClassProofOutput {
serializer: crate::dto::Serializer,
) -> Result<crate::dto::Ok, crate::dto::Error> {
let mut serializer = serializer.serialize_struct()?;
serializer.serialize_optional_with_null("class_commitment", self.class_commitment)?;
serializer.serialize_field("class_proof", &self.class_proof)?;
serializer.end()
}
Expand Down Expand Up @@ -295,6 +313,9 @@ pub async fn get_proof(
let storage_root_idx = tx
.storage_root_index(header.number)
.context("Querying storage root index")?;
let class_commitment = tx
.class_root(header.number)
.context("Querying class commitment")?;

let Some(storage_root_idx) = storage_root_idx else {
if tx.trie_pruning_enabled() {
Expand All @@ -306,6 +327,7 @@ pub async fn get_proof(
// An empty proof is then a proof of non-membership in an empty block.
return Ok(GetProofOutput {
state_commitment,
class_commitment,
contract_proof: ProofNodes(vec![]),
contract_data: None,
});
Expand Down Expand Up @@ -333,6 +355,7 @@ pub async fn get_proof(
if contract_state_hash.is_none() {
return Ok(GetProofOutput {
state_commitment,
class_commitment,
contract_proof,
contract_data: None,
});
Expand Down Expand Up @@ -388,6 +411,7 @@ pub async fn get_proof(

Ok(GetProofOutput {
state_commitment,
class_commitment,
contract_proof,
contract_data: Some(contract_data),
})
Expand Down Expand Up @@ -442,11 +466,17 @@ pub async fn get_class_proof(
// - or all leaves were removed resulting in an empty trie
// An empty proof is then a proof of non-membership in an empty block.
return Ok(GetClassProofOutput {
class_commitment: None,
class_proof: ProofNodes(vec![]),
});
}
};

let class_commitment = tx
.class_trie_node_hash(class_root_idx)
.context("Querying class trie root")?
.map(ClassCommitment);

// Generate a proof for this class. If the class does not exist, this will
// be a "non membership" proof.
let class_proof =
Expand All @@ -457,7 +487,10 @@ pub async fn get_class_proof(

let class_proof = ProofNodes(class_proof);

Ok(GetClassProofOutput { class_proof })
Ok(GetClassProofOutput {
class_commitment,
class_proof,
})
});

jh.await.context("Database read panic or shutting down")?
Expand All @@ -472,6 +505,52 @@ mod tests {

use super::*;

mod serialization {
use bitvec::prelude::*;

use super::*;
use crate::dto::SerializeForVersion;

#[test]
fn serialize_proof_nodes() {
let nodes = ProofNodes(vec![
TrieNode::Binary {
left: Felt::from_u64(0),
right: Felt::from_u64(1),
},
TrieNode::Edge {
child: Felt::from_u64(2),
path: bitvec::bitvec![u8, Msb0; 1, 1],
},
]);
let actual = nodes
.serialize(crate::dto::Serializer {
version: crate::RpcVersion::default(),
})
.unwrap();
let expected = serde_json::json!(
[
{
"binary": {
"left": "0x0",
"right": "0x1",
}
},
{
"edge": {
"path": {
"value": "0x3",
"len": 2,
},
"child": "0x2",
}
},
]
);
assert_eq!(actual, expected);
}
}

mod get_proof {
use super::*;

Expand Down Expand Up @@ -678,6 +757,7 @@ mod tests {
assert_eq!(
output,
GetClassProofOutput {
class_commitment: None,
class_proof: ProofNodes(vec![])
}
);
Expand Down
14 changes: 14 additions & 0 deletions crates/storage/src/connection/trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ impl Transaction<'_> {
.map_err(Into::into)
}

pub fn class_root(&self, block_number: BlockNumber) -> anyhow::Result<Option<ClassCommitment>> {
self.inner()
.query_row(
r"SELECT hash FROM trie_class WHERE idx = (
SELECT root_index FROM class_roots WHERE block_number <= ? ORDER BY block_number DESC LIMIT 1
)",
params![&block_number],
|row| row.get_optional_class_commitment(0),
)
.optional()
.map(|x| x.flatten())
.map_err(Into::into)
}

pub fn class_root_exists(&self, block_number: BlockNumber) -> anyhow::Result<bool> {
self.inner()
.query_row(
Expand Down
7 changes: 7 additions & 0 deletions crates/storage/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,13 @@ pub trait RowExt {
Ok(self.get_optional_felt(index)?.map(StateCommitment))
}

fn get_optional_class_commitment<Index: RowIndex>(
&self,
index: Index,
) -> rusqlite::Result<Option<ClassCommitment>> {
Ok(self.get_optional_felt(index)?.map(ClassCommitment))
}

fn get_block_number<Index: RowIndex>(&self, index: Index) -> rusqlite::Result<BlockNumber> {
let num = self.get_i64(index)?;
// Always safe since we are fetching an i64
Expand Down

0 comments on commit 95f5966

Please sign in to comment.