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

fix(rust): Interpret max_depth in proof specs as 128 if left to 0 #371

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions rust/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

# Unreleased

- fix(rust): Interpret `max_depth` in proof specs as 128 if left to 0 [#371](https://github.com/cosmos/ics23/pull/371).

# v0.12.0

- chore(rust): Update `prost` to v0.13 ([#335](https://github.com/cosmos/ics23/pull/335), [#336](https://github.com/cosmos/ics23/pull/336))
Expand Down
63 changes: 63 additions & 0 deletions rust/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,69 @@ mod tests {
verify_test_vector("../testdata/smt/nonexist_middle.json", &spec)
}

#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct ExistenceProofTest {
proof: crate::ExistenceProof,
is_err: bool,
expected: Option<Vec<u8>>,
}

#[test]
#[cfg(feature = "std")]
fn test_existence_proof() -> Result<()> {
use crate::calculate_existence_root;

let data = std::fs::read_to_string("../testdata/TestExistenceProofData.json")?;
let tests: BTreeMap<String, ExistenceProofTest> = serde_json::from_str(&data)?;

for (name, test) in tests {
println!("Test: {name}");
let result = calculate_existence_root::<HostFunctionsManager>(&test.proof);
if test.is_err {
assert!(result.is_err());
} else {
assert!(result.is_ok());
assert_eq!(
result.unwrap().as_slice(),
test.expected.unwrap().as_slice()
);
}
}

Ok(())
}

#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct CheckAgainstSpecTest {
proof: crate::ExistenceProof,
spec: crate::ProofSpec,
err: String,
}

#[test]
#[cfg(feature = "std")]
fn test_check_against_spec() -> Result<()> {
use crate::verify::check_existence_spec;

let data = std::fs::read_to_string("../testdata/TestCheckAgainstSpecData.json")?;
let tests: BTreeMap<String, CheckAgainstSpecTest> = serde_json::from_str(&data)?;

for (name, test) in tests {
println!("Test: {name}");
let result = check_existence_spec(&test.proof, &test.spec);
if test.err.is_empty() {
assert!(result.is_ok());
} else {
assert!(result.is_err());
// assert_eq!(result.unwrap_err().to_string(), test.err);
}
}

Ok(())
}

#[cfg(feature = "std")]
fn load_batch(files: &[&str]) -> Result<(ics23::CommitmentProof, Vec<RefData>)> {
let (data, entries) = files
Expand Down
4 changes: 4 additions & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ mod verify;
mod ics23 {
include!("cosmos.ics23.v1.rs");

impl ProofSpec {
pub const DEFAULT_MAX_DEPTH: i32 = 128;
}

#[cfg(feature = "serde")]
include!("cosmos.ics23.v1.serde.rs");
}
Expand Down
62 changes: 39 additions & 23 deletions rust/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,31 +114,47 @@ fn calculate_existence_root_for_spec<H: HostFunctionsProvider>(
}
}

fn check_existence_spec(proof: &ics23::ExistenceProof, spec: &ics23::ProofSpec) -> Result<()> {
if let (Some(leaf), Some(leaf_spec)) = (&proof.leaf, &spec.leaf_spec) {
ensure_leaf_prefix(&leaf.prefix, spec)?;
ensure_leaf(leaf, leaf_spec)?;
// ensure min/max depths
if spec.min_depth != 0 {
ensure!(
proof.path.len() >= spec.min_depth as usize,
"Too few InnerOps: {}",
proof.path.len(),
);
ensure!(
proof.path.len() <= spec.max_depth as usize,
"Too many InnerOps: {}",
proof.path.len(),
);
}
for (idx, step) in proof.path.iter().enumerate() {
ensure_inner_prefix(&step.prefix, spec, (idx as i64) + 1, step.hash)?;
ensure_inner(step, spec)?;
}
Ok(())
pub(crate) fn check_existence_spec(
proof: &ics23::ExistenceProof,
spec: &ics23::ProofSpec,
) -> Result<()> {
let Some(leaf) = &proof.leaf else {
bail!("existence Proof needs defined LeafOp");
};

let Some(leaf_spec) = &spec.leaf_spec else {
bail!("existence Proof needs defined LeafSpec");
};

ensure_leaf_prefix(&leaf.prefix, spec)?;
ensure_leaf(leaf, leaf_spec)?;

let max_depth = if spec.max_depth == 0 {
ics23::ProofSpec::DEFAULT_MAX_DEPTH
} else {
bail!("Leaf and Leaf Spec must be set")
spec.max_depth
};

// ensure min/max depths
if spec.min_depth != 0 {
ensure!(
proof.path.len() >= spec.min_depth as usize,
"innerOps depth too short: {}",
proof.path.len(),
);
ensure!(
proof.path.len() <= max_depth as usize,
"innerOps depth too long: {}",
proof.path.len(),
);
}

for (idx, step) in proof.path.iter().enumerate() {
ensure_inner_prefix(&step.prefix, spec, (idx as i64) + 1, step.hash)?;
ensure_inner(step, spec)?;
}

Ok(())
}

fn ensure_leaf(leaf: &ics23::LeafOp, leaf_spec: &ics23::LeafOp) -> Result<()> {
Expand Down
Loading