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