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

adds support for host functions to ics23 #90

Merged
merged 3 commits into from
Jun 20, 2022
Merged
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

# 0.8.0
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks

The following functions have been made generic over a new trait `HostFunctionProvider`:

- [x] `calculate_existence_root`
- [x] `verify_batch_membership`
- [x] `verify_batch_non_membership`
- [x] `verify_membership`
- [x] `verify_non_membership`

For `wasm32-unknown-unknown` environments this trait allows you to delegate hashing functions to a native implementation through host functions. For `std` you can simply use `ics23::HostFunctionManager` as this provides a default implementation of this trait.

# v0.7.0

This handles non-existence tests for empty branches properly. This
Expand Down
20 changes: 13 additions & 7 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ members = ["codegen"]
prost = { version = "0.10", default-features = false, features = ["prost-derive"] }
bytes = { version = "1.0.1", default-features = false }
hex = { version = "0.4.3", default-features = false, features = [ "alloc" ] }
sha2 = { version = "0.9.3", default-features = false }
sha3 = { version = "0.9.1", default-features = false }
ripemd160 = { version = "0.9.1", default-features = false }
anyhow = { version = "1.0.40", default-features = false }
sp-std = {version = "3.0.0", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22", default-features = false }
Copy link
Contributor

Choose a reason for hiding this comment

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

I really don't like tagging to branches here.
Can we just update to a newer release if that is what you need?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

unfortunately, this won't work. Substrate doesn't publish new releases to crates.io so parachain teams need to track git branches for new releases.

Copy link
Contributor

Choose a reason for hiding this comment

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

why do they do that? do you know?

Copy link
Contributor Author

@seunlanlege seunlanlege Jun 20, 2022

Choose a reason for hiding this comment

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

from the versioning (currently we're at 0.9.24) I'd say they don't want to publish until they hit v1.0.0, which would be considered stable

Copy link

Choose a reason for hiding this comment

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

@ethanfrey will you be able to tag a version & publish on crates.io with the dependency specified as git/branch?

sha2 = { version = "0.9.3", optional = true }
sha3 = { version = "0.9.1", optional = true }
ripemd160 = { version = "0.9.1", optional = true }
sp-core = { version = "6.0.0", optional = true }

[dev-dependencies]
sha2 = { version = "0.9.3" }
sha3 = { version = "0.9.1" }
ripemd160 = { version = "0.9.1" }
sp-core = { version = "6.0.0" }
serde = { version = "1.0.125", features = ["derive"] }
serde_json = "1.0.64"

Expand All @@ -32,9 +37,10 @@ std = [
"prost/std",
"bytes/std",
"hex/std",
"sha2/std",
"sha3/std",
"ripemd160/std",
"anyhow/std",
"sp-std/std",
"sha2",
"sha3",
"ripemd160",
"sp-core",
]
55 changes: 31 additions & 24 deletions rust/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use std::collections::btree_map::BTreeMap;

#[cfg(not(feature = "std"))]
use std::prelude::*;
use sp_std::collections::btree_map::BTreeMap;
use sp_std::vec;

use crate::compress::{decompress, is_compressed};
use crate::host_functions::HostFunctionsProvider;
use crate::ics23;
use crate::verify::{verify_existence, verify_non_existence, CommitmentRoot};

// Use CommitmentRoot vs &[u8] to stick with ics naming
#[allow(clippy::ptr_arg)]
pub fn verify_membership(
pub fn verify_membership<H: HostFunctionsProvider>(
proof: &ics23::CommitmentProof,
spec: &ics23::ProofSpec,
root: &CommitmentRoot,
Expand All @@ -30,7 +29,7 @@ pub fn verify_membership(

// if let Some(ics23::commitment_proof::Proof::Exist(ex)) = &proof.proof {
if let Some(ex) = get_exist_proof(proof, key) {
let valid = verify_existence(ex, spec, root, key, value);
let valid = verify_existence::<H>(ex, spec, root, key, value);
valid.is_ok()
} else {
false
Expand All @@ -39,7 +38,7 @@ pub fn verify_membership(

// Use CommitmentRoot vs &[u8] to stick with ics naming
#[allow(clippy::ptr_arg)]
pub fn verify_non_membership(
pub fn verify_non_membership<H: HostFunctionsProvider>(
proof: &ics23::CommitmentProof,
spec: &ics23::ProofSpec,
root: &CommitmentRoot,
Expand All @@ -58,15 +57,15 @@ pub fn verify_non_membership(
}

if let Some(non) = get_nonexist_proof(proof, key) {
let valid = verify_non_existence(non, spec, root, key);
let valid = verify_non_existence::<H>(non, spec, root, key);
valid.is_ok()
} else {
false
}
}

#[allow(clippy::ptr_arg)]
pub fn verify_batch_membership(
pub fn verify_batch_membership<H: HostFunctionsProvider>(
proof: &ics23::CommitmentProof,
spec: &ics23::ProofSpec,
root: &CommitmentRoot,
Expand All @@ -86,11 +85,11 @@ pub fn verify_batch_membership(

items
.iter()
.all(|(key, value)| verify_membership(proof, spec, root, key, value))
.all(|(key, value)| verify_membership::<H>(proof, spec, root, key, value))
}

#[allow(clippy::ptr_arg)]
pub fn verify_batch_non_membership(
pub fn verify_batch_non_membership<H: HostFunctionsProvider>(
proof: &ics23::CommitmentProof,
spec: &ics23::ProofSpec,
root: &CommitmentRoot,
Expand All @@ -109,7 +108,7 @@ pub fn verify_batch_non_membership(
}

keys.iter()
.all(|key| verify_non_membership(proof, spec, root, key))
.all(|key| verify_non_membership::<H>(proof, spec, root, key))
}

fn get_exist_proof<'a>(
Expand Down Expand Up @@ -231,21 +230,21 @@ pub fn smt_spec() -> ics23::ProofSpec {
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
extern crate std as _std;
use super::*;

#[cfg(feature = "std")]
use _std::fs::File;
#[cfg(feature = "std")]
use _std::io::prelude::*;
use alloc::string::String;
use anyhow::{bail, ensure};
use prost::Message;
use serde::Deserialize;
use std::vec::Vec;
use sp_std::vec::Vec;
#[cfg(feature = "std")]
use std::fs::File;
#[cfg(feature = "std")]
use std::io::prelude::*;

use crate::compress::compress;
use crate::helpers::Result;
use crate::host_functions::host_functions_impl::HostFunctionsManager;

#[derive(Deserialize, Debug)]
struct TestVector {
Expand Down Expand Up @@ -289,11 +288,14 @@ mod tests {
let (proof, data) = load_file(filename)?;

if let Some(value) = data.value {
let valid = super::verify_membership(&proof, spec, &data.root, &data.key, &value);
let valid = verify_membership::<HostFunctionsManager>(
&proof, spec, &data.root, &data.key, &value,
);
ensure!(valid, "invalid test vector");
Ok(())
} else {
let valid = super::verify_non_membership(&proof, spec, &data.root, &data.key);
let valid =
verify_non_membership::<HostFunctionsManager>(&proof, spec, &data.root, &data.key);
ensure!(valid, "invalid test vector");
Ok(())
}
Expand Down Expand Up @@ -463,18 +465,23 @@ mod tests {
data: &RefData,
) -> Result<()> {
if let Some(value) = &data.value {
let valid = super::verify_membership(proof, spec, &data.root, &data.key, value);
let valid = verify_membership::<HostFunctionsManager>(
proof, spec, &data.root, &data.key, value,
);
ensure!(valid, "invalid test vector");
let mut items = BTreeMap::new();
items.insert(data.key.as_slice(), value.as_slice());
let valid = super::verify_batch_membership(proof, spec, &data.root, items);
let valid =
verify_batch_membership::<HostFunctionsManager>(proof, spec, &data.root, items);
ensure!(valid, "invalid test vector");
Ok(())
} else {
let valid = super::verify_non_membership(proof, spec, &data.root, &data.key);
let valid =
verify_non_membership::<HostFunctionsManager>(proof, spec, &data.root, &data.key);
ensure!(valid, "invalid test vector");
let keys = &[data.key.as_slice()];
let valid = super::verify_batch_non_membership(proof, spec, &data.root, keys);
let valid =
verify_batch_non_membership::<HostFunctionsManager>(proof, spec, &data.root, keys);
ensure!(valid, "invalid test vector");
Ok(())
}
Expand Down
6 changes: 3 additions & 3 deletions rust/src/compress.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use prost::Message;
use std::borrow::ToOwned;
use std::collections::btree_map::BTreeMap as HashMap;
use std::vec::Vec;
use sp_std::borrow::ToOwned;
use sp_std::collections::btree_map::BTreeMap as HashMap;
use sp_std::vec::Vec;

use crate::helpers::Result;
use crate::ics23;
Expand Down
2 changes: 1 addition & 1 deletion rust/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub type Result<T> = anyhow::Result<T>;
pub type Hash = std::vec::Vec<u8>;
pub type Hash = sp_std::vec::Vec<u8>;
62 changes: 62 additions & 0 deletions rust/src/host_functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/// If this is to be executed in a blockchain context, then we need to delegate these hashing
/// functions to a native implementation through host function calls.
/// This trait provides that interface.
pub trait HostFunctionsProvider {
/// The SHA-256 hash algorithm
fn sha2_256(message: &[u8]) -> [u8; 32];

/// The SHA-512 hash algorithm
fn sha2_512(message: &[u8]) -> [u8; 64];

/// The SHA-512 hash algorithm with its output truncated to 256 bits.
fn sha2_512_truncated(message: &[u8]) -> [u8; 32];

/// SHA-3-512 hash function.
fn sha3_512(message: &[u8]) -> [u8; 64];

/// Ripemd160 hash function.
fn ripemd160(message: &[u8]) -> [u8; 20];
}

#[cfg(any(feature = "std", test))]
pub mod host_functions_impl {
use crate::host_functions::HostFunctionsProvider;
use ripemd160::Ripemd160;
use sha2::{Digest, Sha512, Sha512Trunc256};
use sha3::Sha3_512;

pub struct HostFunctionsManager;
Copy link
Contributor

Choose a reason for hiding this comment

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

In general, it makes sense to move this.

However, substrate is not the only production user and this code will be needed outside of tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Its available in std now

impl HostFunctionsProvider for HostFunctionsManager {
fn sha2_256(message: &[u8]) -> [u8; 32] {
sp_core::hashing::sha2_256(message)
}

fn sha2_512(message: &[u8]) -> [u8; 64] {
let digest = Sha512::digest(message);
let mut buf = [0u8; 64];
buf.copy_from_slice(&digest);
buf
}

fn sha2_512_truncated(message: &[u8]) -> [u8; 32] {
let digest = Sha512Trunc256::digest(message);
let mut buf = [0u8; 32];
buf.copy_from_slice(&digest);
buf
}

fn sha3_512(message: &[u8]) -> [u8; 64] {
let digest = Sha3_512::digest(message);
let mut buf = [0u8; 64];
buf.copy_from_slice(&digest);
buf
}

fn ripemd160(message: &[u8]) -> [u8; 20] {
let digest = Ripemd160::digest(message);
let mut buf = [0u8; 20];
buf.copy_from_slice(&digest);
buf
}
}
}
7 changes: 5 additions & 2 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@

extern crate alloc;
extern crate core;
#[cfg(not(feature = "std"))]
extern crate sp_std as std;

mod api;
mod compress;
mod helpers;
mod host_functions;
mod ics23;
mod ops;
mod verify;
Expand All @@ -21,3 +20,7 @@ pub use api::{
pub use compress::{compress, decompress, is_compressed};
pub use helpers::{Hash, Result};
pub use verify::calculate_existence_root;
pub use host_functions::HostFunctionsProvider;
seunlanlege marked this conversation as resolved.
Show resolved Hide resolved

#[cfg(feature ="std")]
pub use host_functions::host_functions_impl::HostFunctionsManager;
Loading