Skip to content

Commit

Permalink
feat(ic-certification): add ic-certification crate and move HashTree …
Browse files Browse the repository at this point in the history
…and Certificate types (#393)

* feat(ic-types): add ic-types crate and move HashTree and Certificate types

* Improve compatibility

* Rename to ic-certification

Co-authored-by: Linwei Shang <[email protected]>
  • Loading branch information
nathanosdev and lwshang authored Nov 21, 2022
1 parent 4968280 commit c9f9497
Show file tree
Hide file tree
Showing 19 changed files with 194 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.DS_Store

/.vscode/
/.idea/
.idea/

# will have compiled files and executables
/target/
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

* Remove `garcon` from API. Callers can remove the dependency and any usages of it; all waiting functions no longer take a waiter parameter.
* Create `ic-certification` crate and move HashTree and Certificate types.

## [0.22.0] - 2022-10-17

Expand Down
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"icx-cert",
"ic-identity-hsm",
"ic-utils",
"ic-certification",
"icx",
"ref-tests"
]
1 change: 1 addition & 0 deletions ic-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ tokio = { version = "1.21.2", features = ["time"] }
url = "2.1.0"
pkcs8 = { version = "0.9", features = ["std"] }
sec1 = { version = "0.3", features = ["pem"] }
ic-certification = { path = "../ic-certification", version = "0.22" }

[dependencies.hyper]
version = "0.14"
Expand Down
3 changes: 2 additions & 1 deletion ic-agent/src/agent/agent_error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Errors that can occur when using the replica agent.
use crate::{agent::status::Status, hash_tree::Label, RequestIdError};
use crate::{agent::status::Status, RequestIdError};
use ic_certification::Label;
use leb128::read;
use std::{
fmt::{Debug, Display, Formatter},
Expand Down
2 changes: 1 addition & 1 deletion ic-agent/src/agent/agent_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use crate::{
Status,
},
export::Principal,
hash_tree::Label,
Agent, AgentError,
};
use ic_certification::Label;
use mockito::mock;
use std::collections::BTreeMap;

Expand Down
5 changes: 2 additions & 3 deletions ic-agent/src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@ mod agent_test;

use crate::{
agent::replica_api::{
CallRequestContent, Certificate, Delegation, Envelope, QueryContent, ReadStateContent,
ReadStateResponse,
CallRequestContent, Envelope, QueryContent, ReadStateContent, ReadStateResponse,
},
export::Principal,
hash_tree::Label,
identity::Identity,
to_request_id, RequestId,
};
use ic_certification::{Certificate, Delegation, Label};
use serde::Serialize;
use status::Status;

Expand Down
31 changes: 4 additions & 27 deletions ic-agent/src/agent/replica_api.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{
export::Principal,
hash_tree::{HashTree, Label},
};
use crate::export::Principal;
use ic_certification::Label;
use serde::{Deserialize, Serialize};

pub use ic_certification::{Certificate, Delegation};

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct Envelope<T: Serialize> {
Expand Down Expand Up @@ -108,29 +108,6 @@ pub struct ReadStateResponse {
pub certificate: Vec<u8>,
}

/// A `Certificate` as defined in <https://smartcontracts.org/docs/interface-spec/index.html#_certificate>
#[derive(Debug, Deserialize, PartialEq, Eq)]
pub struct Certificate<'a> {
/// The hash tree.
pub tree: HashTree<'a>,

/// The signature of the root hash in `tree`.
#[serde(with = "serde_bytes")]
pub signature: Vec<u8>,

/// A delegation from the root key to the key used to sign `signature`, if one exists.
pub delegation: Option<Delegation>,
}

#[derive(Debug, Deserialize, PartialEq, Eq)]
pub struct Delegation {
#[serde(with = "serde_bytes")]
pub subnet_id: Vec<u8>,

#[serde(with = "serde_bytes")]
pub certificate: Vec<u8>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "status")]
pub enum Status {
Expand Down
7 changes: 2 additions & 5 deletions ic-agent/src/agent/response_authentication.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use crate::agent::{Replied, RequestStatusResponse};
use crate::{export::Principal, AgentError, RequestId};

use crate::{
agent::{replica_api::Certificate, Replied, RequestStatusResponse},
hash_tree::{Label, LookupResult},
};
use ic_certification::{Certificate, Label, LookupResult};
use std::str::from_utf8;

const DER_PREFIX: &[u8; 37] = b"\x30\x81\x82\x30\x1d\x06\x0d\x2b\x06\x01\x04\x01\x82\xdc\x7c\x05\x03\x01\x02\x01\x06\x0c\x2b\x06\x01\x04\x01\x82\xdc\x7c\x05\x03\x02\x01\x03\x61\x00";
Expand Down
9 changes: 6 additions & 3 deletions ic-agent/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,16 @@ mod macros;

pub mod agent;
pub mod export;
pub mod hash_tree;
pub mod identity;
pub mod request_id;

pub use agent::{
agent_error, agent_error::AgentError, replica_api::Certificate,
response_authentication::lookup_value, Agent, NonceFactory, NonceGenerator,
agent_error, agent_error::AgentError, response_authentication::lookup_value, Agent,
NonceFactory, NonceGenerator,
};
pub use identity::{Identity, Signature};
pub use request_id::{to_request_id, RequestId, RequestIdError};

// Re-export from ic_certification for backward compatibility.
pub use ic_certification::hash_tree;
pub use ic_certification::Certificate;
35 changes: 35 additions & 0 deletions ic-certification/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "ic-certification"
version = "0.22.0"
description = "Types related to the Internet Computer Public Specification."
homepage = "https://docs.rs/ic-certification"
documentation = "https://docs.rs/ic-certification"
repository = "https://github.com/dfinity/agent-rs"
edition = "2021"
license = "Apache-2.0"
readme = "README.md"
categories = ["api-bindings", "data-structures", "no-std"]
keywords = ["internet-computer", "agent", "utility", "icp", "dfinity"]
include = ["src", "Cargo.toml", "../LICENSE", "README.md"]
rust-version = "1.60.0"

[dependencies]
hex = "0.4.3"
sha2 = "0.10.1"

[dev-dependencies]
serde = { version = "1.0.133", features = ["derive"] }
serde_cbor = "0.11.2"

[dependencies.serde]
version = "1.0.115"
features = ["derive"]
optional = true

[dependencies.serde_bytes]
version = "0.11.5"
optional = true

[features]
# Default features include serde support.
default = ['serde', 'serde_bytes']
7 changes: 7 additions & 0 deletions ic-certification/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# 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
91 changes: 91 additions & 0 deletions ic-certification/src/certificate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use crate::HashTree;

/// A `Certificate` as defined in <https://internetcomputer.org/docs/current/references/ic-interface-spec/#certificate>
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Certificate<'a> {
/// The hash tree.
pub tree: HashTree<'a>,

/// The signature of the root hash in `tree`.
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
pub signature: Vec<u8>,

/// 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<Delegation>,
}

/// A `Delegation` as defined in <https://internetcomputer.org/docs/current/references/ic-interface-spec/#certification-delegation>
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Delegation {
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
pub subnet_id: Vec<u8>,

#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
pub certificate: Vec<u8>,
}

#[cfg(test)]
#[cfg(feature = "serde")]
mod tests {
use super::*;
use crate::hash_tree::{empty, fork, label, leaf};

fn create_tree<'a>() -> HashTree<'a> {
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");
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! cf https://sdk.dfinity.org/docs/interface-spec/index.html#certification-encoding
//! cf <https://internetcomputer.org/docs/current/references/ic-interface-spec/#certification-encoding>
use hex::FromHexError;
use sha2::Digest;
Expand All @@ -7,9 +7,10 @@ use std::borrow::Cow;
/// Sha256 Digest: 32 bytes
pub type Sha256Digest = [u8; 32];

#[derive(Clone, Hash, Ord, PartialOrd, Eq, PartialEq, serde::Deserialize)]
#[serde(from = "&serde_bytes::Bytes")]
#[serde(into = "&serde_bytes::ByteBuf")]
#[derive(Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(from = "&serde_bytes::Bytes"))]
#[cfg_attr(feature = "serde", serde(into = "&serde_bytes::ByteBuf"))]
/// For labeled [HashTreeNode]
pub struct Label(Vec<u8>);

Expand All @@ -20,6 +21,7 @@ impl Label {
}
}

#[cfg(feature = "serde")]
impl From<Label> for serde_bytes::ByteBuf {
fn from(label: Label) -> serde_bytes::ByteBuf {
serde_bytes::ByteBuf::from(label.as_bytes().to_vec())
Expand Down Expand Up @@ -63,6 +65,7 @@ impl std::fmt::Debug for Label {
}
}

#[cfg(feature = "serde")]
impl serde::Serialize for Label {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
Expand Down Expand Up @@ -131,6 +134,7 @@ impl<'a> From<HashTree<'a>> for HashTreeNode<'a> {
}
}

#[cfg(feature = "serde")]
impl serde::Serialize for HashTree<'_> {
fn serialize<S>(
&self,
Expand All @@ -143,6 +147,7 @@ impl serde::Serialize for HashTree<'_> {
}
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for HashTree<'_> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down Expand Up @@ -225,7 +230,7 @@ enum LookupLabelResult<'node> {

/// A Node in the HashTree.
#[derive(Clone, PartialEq, Eq)]
pub(crate) enum HashTreeNode<'a> {
pub enum HashTreeNode<'a> {
Empty(),
Fork(Box<(HashTreeNode<'a>, HashTreeNode<'a>)>),
Labeled(Cow<'a, Label>, Box<HashTreeNode<'a>>),
Expand Down Expand Up @@ -429,6 +434,7 @@ impl<'a> HashTreeNode<'a> {
}
}

#[cfg(feature = "serde")]
impl serde::Serialize for HashTreeNode<'_> {
// Serialize a `MixedHashTree` per the CDDL of the public spec.
// See https://docs.dfinity.systems/public/certificates.cddl
Expand Down Expand Up @@ -478,6 +484,7 @@ impl serde::Serialize for HashTreeNode<'_> {
}
}

#[cfg(feature = "serde")]
impl<'de, 'tree: 'de> serde::Deserialize<'de> for HashTreeNode<'tree> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down
Loading

0 comments on commit c9f9497

Please sign in to comment.