diff --git a/Cargo.lock b/Cargo.lock index 0e84089b..ac51c753 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1005,11 +1005,12 @@ dependencies = [ [[package]] name = "ic-certification" version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1120544357d4d2f7540dd5290b952c6305afe24c9620d423e2239560adca2535" dependencies = [ "hex", "serde", "serde_bytes", - "serde_cbor", "sha2 0.10.7", ] diff --git a/Cargo.toml b/Cargo.toml index 7779a556..1d75b92e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ members = [ "icx-cert", "ic-identity-hsm", "ic-utils", - "ic-certification", "icx", "ref-tests", ] @@ -23,8 +22,8 @@ license = "Apache-2.0" [workspace.dependencies] ic-agent = { path = "ic-agent", version = "0.27.0", default-features = false } ic-utils = { path = "ic-utils", version = "0.27.0" } -ic-certification = { path = "ic-certification", version = "0.27.0" } +ic-certification = "0.27.0" candid = "0.9.5" hex = "0.4.3" ring = "0.16.20" diff --git a/ic-certification/Cargo.toml b/ic-certification/Cargo.toml deleted file mode 100644 index 0f9ad453..00000000 --- a/ic-certification/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "ic-certification" -version.workspace = true -authors.workspace = true -edition.workspace = true -repository.workspace = true -license.workspace = true -rust-version.workspace = true -description = "Types related to the Internet Computer Public Specification." -documentation = "https://docs.rs/ic-certification" -readme = "README.md" -categories = ["api-bindings", "data-structures", "no-std"] -keywords = ["internet-computer", "agent", "utility", "icp", "dfinity"] -include = ["src", "Cargo.toml", "../LICENSE", "README.md"] - -[dependencies] -hex = { workspace = true } -sha2 = { workspace = true } - -[dev-dependencies] -serde = { workspace = true, features = ["derive"] } -serde_cbor = { workspace = true } - -[dependencies.serde] -workspace = true -features = ["derive"] -optional = true - -[dependencies.serde_bytes] -workspace = true -optional = true - -[features] -# Default features include serde support. -default = ['serde', 'serde_bytes'] - -[package.metadata.docs.rs] -rustdoc-args = ["--cfg=docsrs"] diff --git a/ic-certification/README.md b/ic-certification/README.md index 970f011a..49e0618a 100644 --- a/ic-certification/README.md +++ b/ic-certification/README.md @@ -1,7 +1,3 @@ # IC Certification -## Goal -This library contains typings and utility functions dealing with Certification on the Internet Computer. - -## References -See https://internetcomputer.org/docs/current/references/ic-interface-spec/#certification +This library has been moved to https://github.com/dfinity/response-verification. diff --git a/ic-certification/src/certificate.rs b/ic-certification/src/certificate.rs deleted file mode 100644 index f3aeceb8..00000000 --- a/ic-certification/src/certificate.rs +++ /dev/null @@ -1,334 +0,0 @@ -use crate::hash_tree::HashTree; - -/// A `Certificate` as defined in -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -#[cfg_attr( - feature = "serde", - serde(bound(serialize = "Storage: serde_bytes::Serialize")) -)] -pub struct Certificate> { - /// The hash tree. - pub tree: HashTree, - - /// The signature of the root hash in `tree`. - #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] - pub signature: Storage, - - /// A delegation from the root key to the key used to sign `signature`, if one exists. - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub delegation: Option>, -} - -/// A `Delegation` as defined in -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -#[cfg_attr( - feature = "serde", - serde(bound(serialize = "Storage: serde_bytes::Serialize")) -)] -pub struct Delegation> { - #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] - pub subnet_id: Storage, - - #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))] - pub certificate: Storage, -} - -#[cfg(feature = "serde")] -mod serde_impl { - use super::{Certificate, Delegation}; - use crate::{hash_tree::HashTreeNode, serde_impl::*}; - - use std::{borrow::Cow, fmt, marker::PhantomData}; - - use serde::{ - de::{self, MapAccess, SeqAccess, Visitor}, - Deserialize, Deserializer, - }; - - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "snake_case")] - enum CertificateField { - Tree, - Signature, - Delegation, - } - struct CertificateVisitor(PhantomData); - - impl<'de, S: Storage> Visitor<'de> for CertificateVisitor - where - Delegation>: Deserialize<'de>, - HashTreeNode>: Deserialize<'de>, - { - type Value = Certificate>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct Delegation") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: SeqAccess<'de>, - { - let tree = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(0, &self))?; - let signature = S::convert( - seq.next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?, - ); - let delegation = seq.next_element()?; - - Ok(Certificate { - tree, - signature, - delegation, - }) - } - - fn visit_map(self, mut map: V) -> Result - where - V: MapAccess<'de>, - { - let mut tree = None; - let mut signature = None; - let mut delegation = None; - while let Some(key) = map.next_key()? { - match key { - CertificateField::Tree => { - if tree.is_some() { - return Err(de::Error::duplicate_field("tree")); - } - tree = Some(map.next_value()?); - } - CertificateField::Signature => { - if signature.is_some() { - return Err(de::Error::duplicate_field("signature")); - } - signature = Some(map.next_value()?); - } - CertificateField::Delegation => { - if delegation.is_some() { - return Err(de::Error::duplicate_field("signature")); - } - delegation = Some(map.next_value()?); - } - } - } - let tree = tree.ok_or_else(|| de::Error::missing_field("tree"))?; - let signature = - S::convert(signature.ok_or_else(|| de::Error::missing_field("signature"))?); - Ok(Certificate { - tree, - signature, - delegation, - }) - } - } - - fn deserialize_certificate<'de, S: Storage, D>( - deserializer: D, - ) -> Result>, D::Error> - where - Delegation>: Deserialize<'de>, - HashTreeNode>: Deserialize<'de>, - D: Deserializer<'de>, - { - const FIELDS: &[&str] = &["tree", "signature", "delegation"]; - deserializer.deserialize_struct("Certificate", FIELDS, CertificateVisitor::(PhantomData)) - } - - impl<'de> Deserialize<'de> for Certificate> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_certificate::(deserializer) - } - } - - impl<'de> Deserialize<'de> for Certificate<&'de [u8]> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_certificate::(deserializer) - } - } - - impl<'de> Deserialize<'de> for Certificate> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_certificate::(deserializer) - } - } - - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "snake_case")] - enum DelegationField { - SubnetId, - Certificate, - } - struct DelegationVisitor(PhantomData); - - impl<'de, S: Storage> Visitor<'de> for DelegationVisitor { - type Value = Delegation>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct Delegation") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: SeqAccess<'de>, - { - let subnet_id = S::convert( - seq.next_element()? - .ok_or_else(|| de::Error::invalid_length(0, &self))?, - ); - let certificate = S::convert( - seq.next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?, - ); - Ok(Delegation { - subnet_id, - certificate, - }) - } - - fn visit_map(self, mut map: V) -> Result - where - V: MapAccess<'de>, - { - let mut subnet_id = None; - let mut certificate = None; - while let Some(key) = map.next_key()? { - match key { - DelegationField::SubnetId => { - if subnet_id.is_some() { - return Err(de::Error::duplicate_field("subnet_id")); - } - subnet_id = Some(map.next_value()?); - } - DelegationField::Certificate => { - if certificate.is_some() { - return Err(de::Error::duplicate_field("certificate")); - } - certificate = Some(map.next_value()?); - } - } - } - let subnet_id = - S::convert(subnet_id.ok_or_else(|| de::Error::missing_field("subnet_id"))?); - let certificate = - S::convert(certificate.ok_or_else(|| de::Error::missing_field("certificate"))?); - Ok(Delegation { - subnet_id, - certificate, - }) - } - } - - fn deserialize_delegation<'de, S: Storage, D>( - deserializer: D, - ) -> Result>, D::Error> - where - D: Deserializer<'de>, - { - const FIELDS: &[&str] = &["subnet_id", "certificate"]; - deserializer.deserialize_struct("Delegation", FIELDS, DelegationVisitor::(PhantomData)) - } - - impl<'de> Deserialize<'de> for Delegation> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_delegation::(deserializer) - } - } - - impl<'de> Deserialize<'de> for Delegation<&'de [u8]> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_delegation::(deserializer) - } - } - - impl<'de> Deserialize<'de> for Delegation> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserialize_delegation::(deserializer) - } - } -} - -#[cfg(test)] -#[cfg(feature = "serde")] -mod tests { - use super::*; - use crate::hash_tree::{empty, fork, label, leaf}; - - fn create_tree() -> HashTree> { - fork( - fork( - label( - "a", - fork( - fork(label("x", leaf(*b"hello")), empty()), - label("y", leaf(*b"world")), - ), - ), - label("b", leaf(*b"good")), - ), - fork(label("c", empty()), label("d", leaf(*b"morning"))), - ) - } - - #[test] - fn serialize_to_cbor() { - let tree = create_tree(); - let signature = vec![1, 2, 3, 4, 5, 6]; - - let certificate = Certificate { - tree, - signature, - delegation: None, - }; - - let cbor_bytes = - serde_cbor::to_vec(&certificate).expect("Failed to encode certificate to cbor"); - let cbor_hex = hex::encode(cbor_bytes); - - assert_eq!(cbor_hex, "a264747265658301830183024161830183018302417882034568656c6c6f810083024179820345776f726c6483024162820344676f6f648301830241638100830241648203476d6f726e696e67697369676e617475726546010203040506"); - } - - #[test] - fn serialize_to_cbor_with_delegation() { - let tree = create_tree(); - let signature = vec![1, 2, 3, 4, 5, 6]; - let delegation = Delegation { - subnet_id: vec![7, 8, 9, 10, 11, 12], - certificate: vec![13, 14, 15, 16, 17, 18], - }; - - let certificate = Certificate { - tree, - signature, - delegation: Some(delegation), - }; - - let cbor_bytes = - serde_cbor::to_vec(&certificate).expect("Failed to encode certificate to cbor"); - let cbor_hex = hex::encode(cbor_bytes); - - assert_eq!(cbor_hex, "a364747265658301830183024161830183018302417882034568656c6c6f810083024179820345776f726c6483024162820344676f6f648301830241638100830241648203476d6f726e696e67697369676e6174757265460102030405066a64656c65676174696f6ea2697375626e65745f6964460708090a0b0c6b6365727469666963617465460d0e0f101112"); - } -} diff --git a/ic-certification/src/hash_tree/mod.rs b/ic-certification/src/hash_tree/mod.rs deleted file mode 100644 index abf8d2e7..00000000 --- a/ic-certification/src/hash_tree/mod.rs +++ /dev/null @@ -1,785 +0,0 @@ -//! cf - -use hex::FromHexError; -use sha2::Digest; -use std::{ - borrow::{Borrow, Cow}, - fmt, -}; - -/// Sha256 Digest: 32 bytes -pub type Sha256Digest = [u8; 32]; - -#[derive(Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -/// For labeled [HashTreeNode] -pub struct Label>(Storage); - -impl> Label { - /// Create a label from bytes. - pub fn from_bytes<'a>(v: &'a [u8]) -> Self - where - &'a [u8]: Into, - { - Self(v.into()) - } - - /// Convert labels - pub fn from_label + Into>(s: Label) -> Self { - Self(s.0.into()) - } - - /// Returns this label as bytes. - pub fn as_bytes(&self) -> &[u8] { - self.0.as_ref() - } - - /// The length of the output of [`Self::write_hex`] - fn hex_len(&self) -> usize { - self.as_bytes().len() * 2 - } - - /// Write out the hex - fn write_hex(&self, f: &mut impl fmt::Write) -> fmt::Result { - self.as_bytes() - .iter() - .try_for_each(|b| write!(f, "{:02X}", b)) - } -} - -impl> From for Label { - fn from(s: Storage) -> Self { - Self(s) - } -} - -impl From<[u8; N]> for Label> { - fn from(s: [u8; N]) -> Self { - Self(s.into()) - } -} -impl<'a, const N: usize> From<&'a [u8; N]> for Label> { - fn from(s: &'a [u8; N]) -> Self { - Self(s.as_slice().into()) - } -} -impl<'a> From<&'a [u8]> for Label> { - fn from(s: &'a [u8]) -> Self { - Self(s.into()) - } -} -impl<'a> From<&'a str> for Label> { - fn from(s: &'a str) -> Self { - Self(s.as_bytes().into()) - } -} -impl From for Label> { - fn from(s: String) -> Self { - Self(s.into()) - } -} - -impl<'a, const N: usize> From<&'a [u8; N]> for Label<&'a [u8]> { - fn from(s: &'a [u8; N]) -> Self { - Self(s.as_slice()) - } -} -impl<'a> From<&'a str> for Label<&'a [u8]> { - fn from(s: &'a str) -> Self { - Self(s.as_bytes()) - } -} - -impl<'a, const N: usize> From<&'a [u8; N]> for Label> { - fn from(s: &'a [u8; N]) -> Self { - Self(s.as_slice().into()) - } -} -impl<'a> From<&'a [u8]> for Label> { - fn from(s: &'a [u8]) -> Self { - Self(s.into()) - } -} -impl<'a> From<&'a str> for Label> { - fn from(s: &'a str) -> Self { - Self(s.as_bytes().into()) - } -} - -impl> fmt::Display for Label { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use fmt::Write; - - // Try to print it as an UTF-8 string. If an error happens, print the bytes - // as hexadecimal. - match std::str::from_utf8(self.as_bytes()) { - Ok(s) if s.chars().all(|c| c.is_ascii_graphic()) => { - f.write_char('"')?; - f.write_str(s)?; - f.write_char('"') - } - _ => { - write!(f, "0x")?; - fmt::Debug::fmt(self, f) - } - } - } -} - -impl> fmt::Debug for Label { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.write_hex(f) - } -} - -impl> AsRef<[u8]> for Label { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -/// A result of looking up for a certificate. -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum LookupResult<'tree> { - /// The value is guaranteed to be absent in the original state tree. - Absent, - - /// This partial view does not include information about this path, and the original - /// tree may or may note include this value. - Unknown, - - /// The value was found at the referenced node. - Found(&'tree [u8]), - - /// The path does not make sense for this certificate. - Error, -} - -/// A result of looking up for a subtree. -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum SubtreeLookupResult> { - /// The subtree at the provided path is guaranteed to be absent in the original state tree. - Absent, - - /// This partial view does not include information about this path, and the original - /// tree may or may note include a subtree at this path. - Unknown, - - /// The subtree was found at the provided path. - Found(HashTree), -} - -/// A HashTree representing a full tree. -#[derive(Clone, PartialEq, Eq)] -pub struct HashTree> { - root: HashTreeNode, -} - -impl> fmt::Debug for HashTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("HashTree") - .field("root", &self.root) - .finish() - } -} - -#[allow(dead_code)] -impl> HashTree { - /// Recomputes root hash of the full tree that this hash tree was constructed from. - #[inline] - pub fn digest(&self) -> Sha256Digest { - self.root.digest() - } - - /// Given a (verified) tree, the client can fetch the value at a given path, which is a - /// sequence of labels (blobs). - pub fn lookup_path

(&self, path: P) -> LookupResult<'_> - where - P: IntoIterator, - P::Item: AsRef<[u8]>, - { - self.root.lookup_path(&mut path.into_iter()) - } -} - -impl> HashTree { - /// Given a (verified) tree, the client can fetch the subtree at a given path, which is a - /// sequence of labels (blobs). - pub fn lookup_subtree<'p, P, I>(&self, path: P) -> SubtreeLookupResult - where - P: IntoIterator, - I: ?Sized + AsRef<[u8]> + 'p, - { - self.root - .lookup_subtree(&mut path.into_iter().map(|v| v.borrow())) - } - - /// List all paths in the [HashTree] - pub fn list_paths(&self) -> Vec>> { - self.root.list_paths(&vec![]) - } -} - -impl> AsRef> for HashTree { - fn as_ref(&self) -> &HashTreeNode { - &self.root - } -} - -impl> From> for HashTreeNode { - fn from(tree: HashTree) -> HashTreeNode { - tree.root - } -} - -/// Create an empty hash tree. -#[inline] -pub fn empty>() -> HashTree { - HashTree { - root: HashTreeNode::Empty(), - } -} - -/// Create a forked tree from two trees or node. -#[inline] -pub fn fork>( - left: HashTree, - right: HashTree, -) -> HashTree { - HashTree { - root: HashTreeNode::Fork(Box::new((left.root, right.root))), - } -} - -/// Create a labeled hash tree. -#[inline] -pub fn label, L: Into>, N: Into>>( - label: L, - node: N, -) -> HashTree { - HashTree { - root: HashTreeNode::Labeled(label.into(), Box::new(node.into().root)), - } -} - -/// Create a leaf in the tree. -#[inline] -pub fn leaf, L: Into>(leaf: L) -> HashTree { - HashTree { - root: HashTreeNode::Leaf(leaf.into()), - } -} - -/// Create a pruned tree node. -#[inline] -pub fn pruned, C: Into>(content: C) -> HashTree { - HashTree { - root: HashTreeNode::Pruned(content.into()), - } -} - -/// Create a pruned tree node, from a hex representation of the data. Useful for -/// testing or hard coded values. -#[inline] -pub fn pruned_from_hex, C: AsRef>( - content: C, -) -> Result, FromHexError> { - let mut decode: Sha256Digest = [0; 32]; - hex::decode_to_slice(content.as_ref(), &mut decode)?; - - Ok(pruned(decode)) -} - -/// Private type for label lookup result. -#[derive(Debug)] -enum LookupLabelResult<'node, Storage: AsRef<[u8]>> { - /// The label is not part of this node's tree. - Absent, - - /// Same as absent, but some leaves were pruned and so it's impossible to know. - Unknown, - - /// The label was not found, but could still be to the left. - Less, - - /// The label was not found, but could still be to the right. - Greater, - - /// The label was found. Contains a reference to the [HashTreeNode]. - Found(&'node HashTreeNode), -} - -/// A Node in the HashTree. -#[derive(Clone, PartialEq, Eq)] -pub enum HashTreeNode> { - Empty(), - Fork(Box<(Self, Self)>), - Labeled(Label, Box), - Leaf(Storage), - Pruned(Sha256Digest), -} - -impl> fmt::Debug for HashTreeNode { - // Shows a nicer view to debug than the default debugger. - // Example: - // - // ``` - // HashTree { - // root: Fork( - // Fork( - // Label("a", Fork( - // Pruned(1b4feff9bef8131788b0c9dc6dbad6e81e524249c879e9f10f71ce3749f5a638), - // Label("y", Leaf("world")), - // )), - // Label("b", Pruned(7b32ac0c6ba8ce35ac82c255fc7906f7fc130dab2a090f80fe12f9c2cae83ba6)), - // ), - // Fork( - // Pruned(ec8324b8a1f1ac16bd2e806edba78006479c9877fed4eb464a25485465af601d), - // Label("d", Leaf("morning")), - // ), - // ), - // } - // ``` - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn readable_print(f: &mut fmt::Formatter<'_>, v: &[u8]) -> fmt::Result { - // If it's UTF-8 and all the characters are graphic ASCII, then show as a string. - // If it's short, show hex. - // Otherwise, show length. - match std::str::from_utf8(v) { - Ok(s) if s.chars().all(|c| c.is_ascii_graphic()) => { - f.write_str("\"")?; - f.write_str(s)?; - f.write_str("\"") - } - _ if v.len() <= 32 => { - f.write_str("0x")?; - f.write_str(&hex::encode(v)) - } - _ => { - write!(f, "{} bytes", v.len()) - } - } - } - - match self { - HashTreeNode::Empty() => f.write_str("Empty"), - HashTreeNode::Fork(nodes) => f - .debug_tuple("Fork") - .field(&nodes.0) - .field(&nodes.1) - .finish(), - HashTreeNode::Leaf(v) => { - f.write_str("Leaf(")?; - readable_print(f, v.as_ref())?; - f.write_str(")") - } - HashTreeNode::Labeled(l, node) => { - f.write_str("Label(")?; - readable_print(f, l.as_bytes())?; - f.write_str(", ")?; - node.fmt(f)?; - f.write_str(")") - } - HashTreeNode::Pruned(digest) => write!(f, "Pruned({})", hex::encode(digest.as_ref())), - } - } -} - -impl> HashTreeNode { - /// Update a hasher with the domain separator (byte(|s|) . s). - #[inline] - fn domain_sep(&self, hasher: &mut sha2::Sha256) { - let domain_sep = match self { - HashTreeNode::Empty() => "ic-hashtree-empty", - HashTreeNode::Fork(_) => "ic-hashtree-fork", - HashTreeNode::Labeled(_, _) => "ic-hashtree-labeled", - HashTreeNode::Leaf(_) => "ic-hashtree-leaf", - HashTreeNode::Pruned(_) => return, - }; - hasher.update([domain_sep.len() as u8]); - hasher.update(domain_sep.as_bytes()); - } - - /// Calculate the digest of this node only. - #[inline] - pub fn digest(&self) -> Sha256Digest { - let mut hasher = sha2::Sha256::new(); - self.domain_sep(&mut hasher); - - match self { - HashTreeNode::Empty() => {} - HashTreeNode::Fork(nodes) => { - hasher.update(nodes.0.digest()); - hasher.update(nodes.1.digest()); - } - HashTreeNode::Labeled(label, node) => { - hasher.update(label.as_bytes()); - hasher.update(node.digest()); - } - HashTreeNode::Leaf(bytes) => { - hasher.update(bytes.as_ref()); - } - HashTreeNode::Pruned(digest) => { - return *digest; - } - } - - hasher.finalize().into() - } - - /// Lookup a single label, returning a reference to the labeled [HashTreeNode] node if found. - /// - /// This assumes a sorted hash tree, which is what the spec says the system should - /// return. It will stop when it finds a label that's greater than the one being looked - /// for. - /// - /// This function is implemented with flattening in mind, ie. flattening the forks - /// is not necessary. - fn lookup_label(&self, label: &[u8]) -> LookupLabelResult { - match self { - // If this node is a labeled node, check for the name. - HashTreeNode::Labeled(l, node) => match label.cmp(l.as_bytes()) { - std::cmp::Ordering::Greater => LookupLabelResult::Greater, - std::cmp::Ordering::Equal => LookupLabelResult::Found(node.as_ref()), - // If this node has a smaller label than the one we're looking for, shortcut - // out of this search (sorted tree), we looked too far. - std::cmp::Ordering::Less => LookupLabelResult::Less, - }, - HashTreeNode::Fork(nodes) => { - let left_label = nodes.0.lookup_label(label); - match left_label { - // On greater or unknown, look on the right side of the fork. - LookupLabelResult::Greater => { - let right_label = nodes.1.lookup_label(label); - match right_label { - LookupLabelResult::Less => LookupLabelResult::Absent, - result => result, - } - } - LookupLabelResult::Unknown => { - let right_label = nodes.1.lookup_label(label); - match right_label { - LookupLabelResult::Less => LookupLabelResult::Unknown, - result => result, - } - } - result => result, - } - } - HashTreeNode::Pruned(_) => LookupLabelResult::Unknown, - // Any other type of node and we need to look for more forks. - _ => LookupLabelResult::Absent, - } - } - - /// Lookup the path for the current node only. If the node does not contain the label, - /// this will return [None], signifying that whatever process is recursively walking the - /// tree should continue with siblings of this node (if possible). If it returns - /// [Some] value, then it found an actual result and this may be propagated to the - /// original process doing the lookup. - /// - /// This assumes a sorted hash tree, which is what the spec says the system should return. - /// It will stop when it finds a label that's greater than the one being looked for. - fn lookup_path(&self, path: &mut dyn Iterator>) -> LookupResult<'_> { - use HashTreeNode::*; - use LookupLabelResult as LLR; - use LookupResult::*; - - match ( - path.next() - .map(|segment| self.lookup_label(segment.as_ref())), - self, - ) { - (Some(LLR::Found(node)), _) => node.lookup_path(path), - (None, Leaf(v)) => Found(v.as_ref()), - - (None, Empty()) => Absent, - (None, Pruned(_)) => Unknown, - (None, Labeled(_, _) | Fork(_)) => Error, - - (Some(LLR::Unknown), _) => Unknown, - (Some(LLR::Absent | LLR::Greater | LLR::Less), _) => Absent, - } - } -} - -impl> HashTreeNode { - /// Lookup a subtree at the provided path. - /// If the tree definitely does not contain the label, this will return [SubtreeLookupResult::Absent]. - /// If the tree has pruned sections that might contain the path, this will return [SubtreeLookupResult::Unknown]. - /// If the provided path is found, this will return [SubtreeLookupResult::Found] with the node that was found at that path. - /// - /// This assumes a sorted hash tree, which is what the spec says the system should return. - /// It will stop when it finds a label that's greater than the one being looked for. - fn lookup_subtree( - &self, - path: &mut dyn Iterator>, - ) -> SubtreeLookupResult { - use LookupLabelResult as LLR; - use SubtreeLookupResult::*; - - match path - .next() - .map(|segment| self.lookup_label(segment.as_ref())) - { - Some(LLR::Found(node)) => node.lookup_subtree(path), - Some(LLR::Unknown) => Unknown, - Some(LLR::Absent | LLR::Greater | LLR::Less) => Absent, - None => Found(HashTree { - root: self.to_owned(), - }), - } - } - - fn list_paths(&self, path: &Vec>) -> Vec>> { - match self { - HashTreeNode::Empty() => vec![], - HashTreeNode::Fork(nodes) => { - [nodes.0.list_paths(path), nodes.1.list_paths(path)].concat() - } - HashTreeNode::Leaf(_) => vec![path.clone()], - HashTreeNode::Labeled(l, node) => { - let mut path = path.clone(); - path.push(l.clone()); - node.list_paths(&path) - } - HashTreeNode::Pruned(_) => vec![], - } - } -} -#[cfg(feature = "serde")] -mod serde_impl { - use std::{borrow::Cow, fmt, marker::PhantomData}; - - use crate::serde_impl::{CowStorage, SliceStorage, Storage, VecStorage}; - - use super::{HashTree, HashTreeNode, Label}; - - use serde::{ - de::{self, SeqAccess, Visitor}, - ser::SerializeSeq, - Deserialize, Deserializer, Serialize, Serializer, - }; - use serde_bytes::Bytes; - - impl> Serialize for Label { - fn serialize(&self, serializer: S) -> Result { - if serializer.is_human_readable() { - let mut s = String::with_capacity(self.hex_len()); - self.write_hex(&mut s).unwrap(); - s.serialize(serializer) - } else { - serializer.serialize_bytes(self.0.as_ref()) - } - } - } - impl<'de, Storage: AsRef<[u8]>> Deserialize<'de> for Label - where - Storage: serde_bytes::Deserialize<'de>, - { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - serde_bytes::deserialize(deserializer).map(Self) - } - } - - impl> Serialize for HashTreeNode { - // Serialize a `MixedHashTree` per the CDDL of the public spec. - // See https://docs.dfinity.systems/public/certificates.cddl - fn serialize( - &self, - serializer: S, - ) -> Result<::Ok, ::Error> - where - S: Serializer, - { - match self { - HashTreeNode::Empty() => { - let mut seq = serializer.serialize_seq(Some(1))?; - seq.serialize_element(&0u8)?; - seq.end() - } - HashTreeNode::Fork(tree) => { - let mut seq = serializer.serialize_seq(Some(3))?; - seq.serialize_element(&1u8)?; - seq.serialize_element(&tree.0)?; - seq.serialize_element(&tree.1)?; - seq.end() - } - HashTreeNode::Labeled(label, tree) => { - let mut seq = serializer.serialize_seq(Some(3))?; - seq.serialize_element(&2u8)?; - seq.serialize_element(Bytes::new(label.as_bytes()))?; - seq.serialize_element(&tree)?; - seq.end() - } - HashTreeNode::Leaf(leaf_bytes) => { - let mut seq = serializer.serialize_seq(Some(2))?; - seq.serialize_element(&3u8)?; - seq.serialize_element(Bytes::new(leaf_bytes.as_ref()))?; - seq.end() - } - HashTreeNode::Pruned(digest) => { - let mut seq = serializer.serialize_seq(Some(2))?; - seq.serialize_element(&4u8)?; - seq.serialize_element(Bytes::new(digest))?; - seq.end() - } - } - } - } - - struct HashTreeNodeVisitor(PhantomData); - - impl<'de, S: Storage> Visitor<'de> for HashTreeNodeVisitor - where - HashTreeNode>: Deserialize<'de>, - { - type Value = HashTreeNode>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str( - "HashTree encoded as a sequence of the form \ - hash-tree ::= [0] | [1 hash-tree hash-tree] | [2 bytes hash-tree] | [3 bytes] | [4 hash]", - ) - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: SeqAccess<'de>, - { - let tag: u8 = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(0, &self))?; - - match tag { - 0 => { - if let Some(de::IgnoredAny) = seq.next_element()? { - return Err(de::Error::invalid_length(2, &self)); - } - - Ok(HashTreeNode::Empty()) - } - 1 => { - let left = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?; - let right = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(2, &self))?; - - if let Some(de::IgnoredAny) = seq.next_element()? { - return Err(de::Error::invalid_length(4, &self)); - } - - Ok(HashTreeNode::Fork(Box::new((left, right)))) - } - 2 => { - let label = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?; - let subtree = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(2, &self))?; - - if let Some(de::IgnoredAny) = seq.next_element()? { - return Err(de::Error::invalid_length(4, &self)); - } - - Ok(HashTreeNode::Labeled( - Label(S::convert(label)), - Box::new(subtree), - )) - } - 3 => { - let bytes = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?; - - if let Some(de::IgnoredAny) = seq.next_element()? { - return Err(de::Error::invalid_length(3, &self)); - } - - Ok(HashTreeNode::Leaf(S::convert(bytes))) - } - 4 => { - let digest_bytes: &serde_bytes::Bytes = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?; - - if let Some(de::IgnoredAny) = seq.next_element()? { - return Err(de::Error::invalid_length(3, &self)); - } - - let digest = - std::convert::TryFrom::try_from(digest_bytes.as_ref()).map_err(|_| { - de::Error::invalid_length(digest_bytes.len(), &"Expected digest blob") - })?; - - Ok(HashTreeNode::Pruned(digest)) - } - _ => Err(de::Error::custom(format!( - "Unknown tag: {}, expected the tag to be one of {{0, 1, 2, 3, 4}}", - tag - ))), - } - } - } - - impl<'de> Deserialize<'de> for HashTreeNode> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_seq(HashTreeNodeVisitor::(PhantomData)) - } - } - - impl<'de> Deserialize<'de> for HashTreeNode<&'de [u8]> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_seq(HashTreeNodeVisitor::(PhantomData)) - } - } - - impl<'de> Deserialize<'de> for HashTreeNode> { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_seq(HashTreeNodeVisitor::(PhantomData)) - } - } - - impl> serde::Serialize for HashTree { - fn serialize( - &self, - serializer: S, - ) -> Result<::Ok, ::Error> - where - S: serde::Serializer, - { - self.root.serialize(serializer) - } - } - - impl<'de, Storage: AsRef<[u8]>> serde::Deserialize<'de> for HashTree - where - HashTreeNode: Deserialize<'de>, - { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - Ok(HashTree { - root: HashTreeNode::deserialize(deserializer)?, - }) - } - } -} - -#[cfg(test)] -mod tests; diff --git a/ic-certification/src/hash_tree/tests.rs b/ic-certification/src/hash_tree/tests.rs deleted file mode 100644 index 5cece74d..00000000 --- a/ic-certification/src/hash_tree/tests.rs +++ /dev/null @@ -1,508 +0,0 @@ -#![cfg(test)] - -use crate::hash_tree::{ - empty, fork, label, leaf, pruned, pruned_from_hex, HashTree, LookupResult, SubtreeLookupResult, -}; - -fn lookup_path>(tree: &HashTree>, path: P) -> LookupResult { - tree.lookup_path(path.as_ref().iter().map(|s| s.as_bytes())) -} - -fn lookup_subtree>( - tree: &HashTree>, - path: P, -) -> SubtreeLookupResult> { - tree.lookup_subtree(path.as_ref().iter().map(|s| s.as_bytes())) -} - -#[test] -fn works_with_simple_tree() { - let tree: HashTree> = fork( - label("label 1", empty()), - fork( - pruned(*b"\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"), - leaf([1u8, 2, 3, 4, 5, 6]), - ), - ); - - assert_eq!( - hex::encode(tree.digest()), - "69cf325d0f20505b261821a7e77ff72fb9a8753a7964f0b587553bfb44e72532" - ); -} - -#[test] -fn spec_example() { - // This is the example straight from the spec. - let tree: HashTree> = fork( - fork( - label( - "a", - fork( - fork(label("x", leaf(*b"hello")), empty()), - label("y", leaf(*b"world")), - ), - ), - label("b", leaf(*b"good")), - ), - fork(label("c", empty()), label("d", leaf(*b"morning"))), - ); - - // Check CBOR serialization. - #[cfg(feature = "serde")] - assert_eq!( - hex::encode(serde_cbor::to_vec(&tree).unwrap()), - "8301830183024161830183018302417882034568656c6c6f810083024179820345776f726c6483024162820344676f6f648301830241638100830241648203476d6f726e696e67" - ); - - assert_eq!( - hex::encode(tree.digest()), - "eb5c5b2195e62d996b84c9bcc8259d19a83786a2f59e0878cec84c811f669aa0" - ); -} - -#[test] -fn spec_example_pruned() { - // This is the example straight from the spec. - let tree: HashTree> = fork( - fork( - label( - "a", - fork( - pruned_from_hex( - "1b4feff9bef8131788b0c9dc6dbad6e81e524249c879e9f10f71ce3749f5a638", - ) - .unwrap(), - label("y", leaf(*b"world")), - ), - ), - label( - "b", - pruned_from_hex("7b32ac0c6ba8ce35ac82c255fc7906f7fc130dab2a090f80fe12f9c2cae83ba6") - .unwrap(), - ), - ), - fork( - pruned_from_hex("ec8324b8a1f1ac16bd2e806edba78006479c9877fed4eb464a25485465af601d") - .unwrap(), - label("d", leaf(*b"morning")), - ), - ); - - assert_eq!( - hex::encode(tree.digest()), - "eb5c5b2195e62d996b84c9bcc8259d19a83786a2f59e0878cec84c811f669aa0" - ); - - assert_eq!(lookup_path(&tree, ["a", "a"]), LookupResult::Unknown); - assert_eq!( - lookup_path(&tree, ["a", "y"]), - LookupResult::Found(b"world") - ); - assert_eq!(lookup_path(&tree, ["aa"]), LookupResult::Absent); - assert_eq!(lookup_path(&tree, ["ax"]), LookupResult::Absent); - assert_eq!(lookup_path(&tree, ["b"]), LookupResult::Unknown); - assert_eq!(lookup_path(&tree, ["bb"]), LookupResult::Unknown); - assert_eq!(lookup_path(&tree, ["d"]), LookupResult::Found(b"morning")); - assert_eq!(lookup_path(&tree, ["e"]), LookupResult::Absent); -} - -#[test] -fn can_lookup_paths_1() { - let tree: HashTree> = fork( - label("label 1", empty()), - fork( - pruned([1; 32]), - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - ), - ); - - assert_eq!(tree.lookup_path([b"label 0"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 1"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 2"]), LookupResult::Unknown); - assert_eq!( - tree.lookup_path([b"label 3"]), - LookupResult::Found(&[1, 2, 3, 4, 5, 6]) - ); - assert_eq!(tree.lookup_path([b"label 4"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 5"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 6"]), LookupResult::Absent); -} - -#[test] -fn can_lookup_paths_2() { - let tree: HashTree> = fork( - label("label 1", empty()), - fork( - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - pruned([1; 32]), - ), - ); - - assert_eq!(tree.lookup_path([b"label 0"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 1"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 2"]), LookupResult::Absent); - assert_eq!( - tree.lookup_path([b"label 3"]), - LookupResult::Found(&[1, 2, 3, 4, 5, 6]) - ); - assert_eq!(tree.lookup_path([b"label 4"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 5"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 6"]), LookupResult::Unknown); -} - -#[test] -fn can_lookup_paths_3() { - let tree: HashTree> = fork( - pruned([0; 32]), - fork( - pruned([1; 32]), - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - ), - ); - - assert_eq!(tree.lookup_path([b"label 2"]), LookupResult::Unknown); - assert_eq!( - tree.lookup_path([b"label 3"]), - LookupResult::Found(&[1, 2, 3, 4, 5, 6]) - ); - assert_eq!(tree.lookup_path([b"label 4"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 5"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 6"]), LookupResult::Absent); -} - -#[test] -fn can_lookup_paths_4() { - let tree: HashTree> = fork( - pruned([0; 32]), - fork( - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - pruned([1; 32]), - ), - ); - - assert_eq!(tree.lookup_path([b"label 2"]), LookupResult::Unknown); - assert_eq!( - tree.lookup_path([b"label 3"]), - LookupResult::Found(&[1, 2, 3, 4, 5, 6]) - ); - assert_eq!(tree.lookup_path([b"label 4"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 5"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 6"]), LookupResult::Unknown); -} - -#[test] -fn can_lookup_paths_5() { - let tree: HashTree> = fork( - fork( - pruned([1; 32]), - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - ), - label("label 7", empty()), - ); - - assert_eq!(tree.lookup_path([b"label 2"]), LookupResult::Unknown); - assert_eq!( - tree.lookup_path([b"label 3"]), - LookupResult::Found(&[1, 2, 3, 4, 5, 6]) - ); - assert_eq!(tree.lookup_path([b"label 4"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 5"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 6"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 7"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 8"]), LookupResult::Absent); -} - -#[test] -fn can_lookup_paths_6() { - let tree: HashTree> = fork( - fork( - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - pruned([1; 32]), - ), - label("label 7", empty()), - ); - - assert_eq!(tree.lookup_path([b"label 2"]), LookupResult::Absent); - assert_eq!( - tree.lookup_path([b"label 3"]), - LookupResult::Found(&[1, 2, 3, 4, 5, 6]) - ); - assert_eq!(tree.lookup_path([b"label 4"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 5"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 6"]), LookupResult::Unknown); - assert_eq!(tree.lookup_path([b"label 7"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 8"]), LookupResult::Absent); -} - -#[test] -fn can_lookup_paths_7() { - let tree: HashTree> = fork( - fork( - pruned([1; 32]), - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - ), - pruned([0; 32]), - ); - - assert_eq!(tree.lookup_path([b"label 2"]), LookupResult::Unknown); - assert_eq!( - tree.lookup_path([b"label 3"]), - LookupResult::Found(&[1, 2, 3, 4, 5, 6]) - ); - assert_eq!(tree.lookup_path([b"label 4"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 5"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 6"]), LookupResult::Unknown); -} - -#[test] -fn can_lookup_paths_8() { - let tree: HashTree> = fork( - fork( - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - pruned([1; 32]), - ), - pruned([0; 32]), - ); - - assert_eq!(tree.lookup_path([b"label 2"]), LookupResult::Absent); - assert_eq!( - tree.lookup_path([b"label 3"]), - LookupResult::Found(&[1, 2, 3, 4, 5, 6]) - ); - assert_eq!(tree.lookup_path([b"label 4"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 5"]), LookupResult::Absent); - assert_eq!(tree.lookup_path([b"label 6"]), LookupResult::Unknown); -} - -#[test] -fn can_lookup_subtrees_1() { - use SubtreeLookupResult::*; - - let tree: HashTree> = fork( - label("label 1", empty()), - fork( - pruned([1; 32]), - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - ), - ); - - assert_eq!(lookup_subtree(&tree, ["label 0"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 1"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 2"]), Unknown); - assert_eq!( - lookup_subtree(&tree, ["label 3"]), - Found(leaf(vec![1, 2, 3, 4, 5, 6])) - ); - assert_eq!(lookup_subtree(&tree, ["label 4"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 5"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 6"]), Absent); -} - -#[test] -fn can_lookup_subtrees_2() { - use SubtreeLookupResult::*; - - let tree: HashTree> = fork( - label("label 1", empty()), - fork( - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - pruned([1; 32]), - ), - ); - - assert_eq!(lookup_subtree(&tree, ["label 0"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 1"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 2"]), Absent); - assert_eq!( - lookup_subtree(&tree, ["label 3"]), - Found(leaf(vec![1, 2, 3, 4, 5, 6])) - ); - assert_eq!(lookup_subtree(&tree, ["label 4"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 5"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 6"]), Unknown); -} - -#[test] -fn can_lookup_subtrees_3() { - use SubtreeLookupResult::*; - - let tree: HashTree> = fork( - pruned([0; 32]), - fork( - pruned([1; 32]), - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - ), - ); - - assert_eq!(lookup_subtree(&tree, ["label 2"]), Unknown); - assert_eq!( - lookup_subtree(&tree, ["label 3"]), - Found(leaf(vec![1, 2, 3, 4, 5, 6])) - ); - assert_eq!(lookup_subtree(&tree, ["label 4"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 5"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 6"]), Absent); -} - -#[test] -fn can_lookup_subtrees_4() { - use SubtreeLookupResult::*; - - let tree: HashTree> = fork( - pruned([0; 32]), - fork( - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - pruned([1; 32]), - ), - ); - - assert_eq!(lookup_subtree(&tree, ["label 2"]), Unknown); - assert_eq!( - lookup_subtree(&tree, ["label 3"]), - Found(leaf(vec![1, 2, 3, 4, 5, 6])) - ); - assert_eq!(lookup_subtree(&tree, ["label 4"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 5"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 6"]), Unknown); -} - -#[test] -fn can_lookup_subtrees_5() { - use SubtreeLookupResult::*; - - let tree: HashTree> = fork( - fork( - pruned([1; 32]), - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - ), - label("label 7", empty()), - ); - - assert_eq!(lookup_subtree(&tree, ["label 2"]), Unknown); - assert_eq!( - lookup_subtree(&tree, ["label 3"]), - Found(leaf(vec![1, 2, 3, 4, 5, 6])) - ); - assert_eq!(lookup_subtree(&tree, ["label 4"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 5"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 6"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 7"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 8"]), Absent); -} - -#[test] -fn can_lookup_subtrees_6() { - use SubtreeLookupResult::*; - - let tree: HashTree> = fork( - fork( - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - pruned([1; 32]), - ), - label("label 7", empty()), - ); - - assert_eq!(lookup_subtree(&tree, ["label 2"]), Absent); - assert_eq!( - lookup_subtree(&tree, ["label 3"]), - Found(leaf(vec![1, 2, 3, 4, 5, 6])) - ); - assert_eq!(lookup_subtree(&tree, ["label 4"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 5"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 6"]), Unknown); - assert_eq!(lookup_subtree(&tree, ["label 7"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 8"]), Absent); -} - -#[test] -fn can_lookup_subtrees_7() { - use SubtreeLookupResult::*; - - let tree: HashTree> = fork( - fork( - pruned([1; 32]), - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - ), - pruned([0; 32]), - ); - - assert_eq!(lookup_subtree(&tree, ["label 2"]), Unknown); - assert_eq!( - lookup_subtree(&tree, ["label 3"]), - Found(leaf(vec![1, 2, 3, 4, 5, 6])) - ); - assert_eq!(lookup_subtree(&tree, ["label 4"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 5"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 6"]), Unknown); -} - -#[test] -fn can_lookup_subtrees_8() { - use SubtreeLookupResult::*; - - let tree: HashTree> = fork( - fork( - fork( - label("label 3", leaf(vec![1, 2, 3, 4, 5, 6])), - label("label 5", empty()), - ), - pruned([1; 32]), - ), - pruned([0; 32]), - ); - - assert_eq!(lookup_subtree(&tree, ["label 2"]), Absent); - assert_eq!( - lookup_subtree(&tree, ["label 3"]), - Found(leaf(vec![1, 2, 3, 4, 5, 6])) - ); - assert_eq!(lookup_subtree(&tree, ["label 4"]), Absent); - assert_eq!(lookup_subtree(&tree, ["label 5"]), Found(empty())); - assert_eq!(lookup_subtree(&tree, ["label 6"]), Unknown); -} diff --git a/ic-certification/src/lib.rs b/ic-certification/src/lib.rs deleted file mode 100644 index 22d09023..00000000 --- a/ic-certification/src/lib.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! A collection of types related to the Internet Computer Protocol. -//! -//! If you need support for the serde library, you will need to use the `serde` feature -//! (available by default). -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] - -use hash_tree::Sha256Digest; -use hex::FromHexError; - -pub mod certificate; -pub mod hash_tree; - -#[doc(inline)] -pub use hash_tree::LookupResult; - -/// A HashTree representing a full tree. -pub type HashTree = hash_tree::HashTree>; -/// For labeled [`HashTreeNode`](hash_tree::HashTreeNode) -pub type Label = hash_tree::Label>; -/// A result of looking up for a subtree. -pub type SubtreeLookupResult = hash_tree::SubtreeLookupResult>; - -/// A `Delegation` as defined in -pub type Delegation = certificate::Delegation>; -/// A `Certificate` as defined in -pub type Certificate = certificate::Certificate>; - -/// Create an empty hash tree. -#[inline] -pub fn empty() -> HashTree { - hash_tree::empty() -} - -/// Create a forked tree from two trees or node. -#[inline] -pub fn fork(left: HashTree, right: HashTree) -> HashTree { - hash_tree::fork(left, right) -} - -/// Create a labeled hash tree. -#[inline] -pub fn label, N: Into>(label: L, node: N) -> HashTree { - hash_tree::label(label, node) -} - -/// Create a leaf in the tree. -#[inline] -pub fn leaf>>(leaf: L) -> HashTree { - hash_tree::leaf(leaf) -} - -/// Create a pruned tree node. -#[inline] -pub fn pruned>(content: C) -> HashTree { - hash_tree::pruned(content) -} - -/// Create a pruned tree node, from a hex representation of the data. Useful for -/// testing or hard coded values. -#[inline] -pub fn pruned_from_hex>(content: C) -> Result { - hash_tree::pruned_from_hex(content) -} - -#[cfg(feature = "serde")] -mod serde_impl { - use std::borrow::Cow; - - use serde::Deserialize; - use serde_bytes::{ByteBuf, Bytes}; - - /// A trait to genericize deserializing owned or borrowed bytes - pub trait Storage { - type Temp<'a>: Deserialize<'a>; - type Value<'a>: AsRef<[u8]>; - fn convert(t: Self::Temp<'_>) -> Self::Value<'_>; - } - - /// `Vec` - pub struct VecStorage; - /// `&[u8]` - pub struct SliceStorage; - /// `Cow<[u8]>` - pub struct CowStorage; - - impl Storage for VecStorage { - type Temp<'a> = ByteBuf; - type Value<'a> = Vec; - fn convert(t: Self::Temp<'_>) -> Self::Value<'_> { - t.into_vec() - } - } - - impl Storage for SliceStorage { - type Temp<'a> = &'a Bytes; - type Value<'a> = &'a [u8]; - fn convert(t: Self::Temp<'_>) -> Self::Value<'_> { - t.as_ref() - } - } - - impl Storage for CowStorage { - type Temp<'a> = &'a Bytes; - type Value<'a> = Cow<'a, [u8]>; - fn convert(t: Self::Temp<'_>) -> Self::Value<'_> { - Cow::Borrowed(t.as_ref()) - } - } -}