Skip to content
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
628 changes: 604 additions & 24 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[workspace]
default-members = ["crates/cli"]
members = ["crates/*"]
resolver = "3"

Expand Down Expand Up @@ -59,7 +60,9 @@ itoa = "1.0"
jiff = "0.2.20"
once_cell = { version = "1.21", default-features = false }
paste = "1.0"
rand = "0.10"
regex = "1.12"
reqwest = { version = "0.13", default-features = false }
rocksdb = "0.24"
serde = "1.0"
serde-wasm-bindgen = "0.6"
Expand All @@ -72,6 +75,7 @@ toml = "0.9"
tower-http = "0.6"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
url = "2.5"
wasm-bindgen = "0.2"

crypto-common = "0.2.0-rc.5"
Expand Down
7 changes: 4 additions & 3 deletions crates/bmt/benches/tree_construction.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Benchmark for Merkle tree construction.
use bytemuck::Pod;
use criterion::{
BatchSize, BenchmarkGroup, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main,
measurement::WallTime,
Expand All @@ -7,7 +8,7 @@ use digest::{Digest, FixedOutputReset, Output};
use sha2::Sha256;
use sha3::Keccak256;
use std::hint::black_box;
use uts_bmt::FlatMerkleTree;
use uts_bmt::UnorderdMerkleTree;

const INPUT_SIZES: &[usize] = &[8, 1024, 65536, 1_048_576];

Expand All @@ -25,15 +26,15 @@ fn benchmark(c: &mut Criterion) {
fn bench_digest<D>(group: &mut BenchmarkGroup<'_, WallTime>, id: &str)
where
D: Digest + FixedOutputReset,
Output<D>: Copy,
Output<D>: Pod + Copy,
{
for &size in INPUT_SIZES {
let leaves = generate_leaves::<D>(size);
group.throughput(Throughput::Elements(size as u64));
group.bench_function(BenchmarkId::new(id, size), move |b| {
// Tree construction is the operation under test.
b.iter(|| {
let tree = FlatMerkleTree::<D>::new(black_box(leaves.as_slice()));
let tree = UnorderdMerkleTree::<D>::new(black_box(leaves.as_slice()));
black_box(tree);
});
});
Expand Down
12 changes: 6 additions & 6 deletions crates/bmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub const INNER_NODE_PREFIX: u8 = 0x01;
///
/// Leaves are **sorted** starting at index `len`.
#[derive(Debug, Clone, Default)]
pub struct FlatMerkleTree<D: Digest> {
pub struct UnorderdMerkleTree<D: Digest> {
/// Index 0 is not used, leaves start at index `len`.
nodes: Box<[Output<D>]>,
len: usize,
Expand All @@ -28,7 +28,7 @@ pub struct UnhashedFlatMerkleTree<D: Digest> {
len: usize,
}

impl<D: Digest + FixedOutputReset> FlatMerkleTree<D>
impl<D: Digest + FixedOutputReset> UnorderdMerkleTree<D>
where
Output<D>: Pod + Copy,
{
Expand Down Expand Up @@ -128,7 +128,7 @@ where
Output<D>: Pod + Copy,
{
/// Finalizes the Merkle tree by hashing internal nodes
pub fn finalize(self) -> FlatMerkleTree<D> {
pub fn finalize(self) -> UnorderdMerkleTree<D> {
let mut nodes = self.buffer;
let len = self.len;
unsafe {
Expand All @@ -152,7 +152,7 @@ where
// SAFETY: initialized all elements.
nodes.set_len(2 * len);
}
FlatMerkleTree {
UnorderdMerkleTree {
nodes: nodes.into_boxed_slice(),
len,
}
Expand Down Expand Up @@ -234,7 +234,7 @@ mod tests {
];
leaves.sort_unstable();

let tree = FlatMerkleTree::<D>::new(&leaves);
let tree = UnorderdMerkleTree::<D>::new(&leaves);

// Manually compute the expected root
let mut hasher = D::new();
Expand Down Expand Up @@ -265,7 +265,7 @@ mod tests {
];
leaves.sort_unstable();

let tree = FlatMerkleTree::<D>::new(&leaves);
let tree = UnorderdMerkleTree::<D>::new(&leaves);

for leaf in &leaves {
let mut iter = tree
Expand Down
23 changes: 7 additions & 16 deletions crates/calendar/src/routes/ots.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{AppState, time::current_time_sec};
use alloy_primitives::{B256, hex};
use alloy_primitives::B256;
use alloy_signer::SignerSync;
use axum::{
body::Bytes,
Expand All @@ -12,7 +12,7 @@ use bytes::BytesMut;
use digest::Digest;
use sha3::Keccak256;
use std::{cell::RefCell, sync::Arc};
use uts_bmt::{FlatMerkleTree, NodePosition};
use uts_bmt::UnorderdMerkleTree;
use uts_core::{
codec::{
Encode,
Expand Down Expand Up @@ -107,7 +107,8 @@ pub fn submit_digest_inner(digest: Bytes, signer: impl SignerSync) -> (Bytes, [u
let mut bump = bump.borrow_mut();
bump.reset();

let builder = Timestamp::builder_in(&*bump)
let mut builder = Timestamp::builder_in(&*bump);
builder
.prepend(recv_timestamp.to_vec_in(&bump))
.append(undeniable_sig.to_vec_in(&bump))
.keccak256();
Expand Down Expand Up @@ -153,28 +154,18 @@ pub async fn get_timestamp(
.load_entry(root)
.expect("DB error")
.expect("bug: entry not found");
let trie: FlatMerkleTree<Keccak256> = entry.trie();
let trie: UnorderdMerkleTree<Keccak256> = entry.trie();

let mut proof_iter = trie
let proof = trie
.get_proof_iter(bytemuck::cast_ref(&*commitment))
.expect("bug: proof not found");
let output = BUMP.with(|bump| {
let mut bump = bump.borrow_mut();
bump.reset();

let mut builder = Timestamp::builder_in(&*bump);
builder.merkle_proof(proof);

while let Some((side, sibling_hash)) = proof_iter.next() {
builder = match side {
NodePosition::Left => builder
.prepend([uts_bmt::INNER_NODE_PREFIX].to_vec_in(&bump))
.append(sibling_hash.to_vec_in(&bump)),
NodePosition::Right => builder
.prepend(sibling_hash.to_vec_in(&bump))
.prepend([uts_bmt::INNER_NODE_PREFIX].to_vec_in(&bump)),
}
.keccak256();
}
let timestamp = builder
.attest(EthereumUTSAttestation::new(
entry.chain_id,
Expand Down
46 changes: 46 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[package]
authors.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
name = "uts-cli"
repository.workspace = true
version.workspace = true

[lints]
workspace = true

[[bin]]
name = "uts"
path = "src/main.rs"

[dependencies]
alloy-provider = { workspace = true }
bytemuck = { workspace = true }
clap = { workspace = true, features = ["derive"] }
color-eyre = { workspace = true }
digest = { workspace = true }
eyre = { workspace = true }
futures = { workspace = true }
jiff = { workspace = true }
rand = { workspace = true }
reqwest = { workspace = true, default-features = false, features = ["http2"] }
ripemd = { workspace = true }
sha1 = { workspace = true }
sha2 = { workspace = true }
sha3 = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tracing = { workspace = true }
url = { workspace = true }
uts-bmt = { workspace = true }
uts-core = { workspace = true, features = ["std", "ethereum-uts-verifier", "io-utils"] }

[features]
default = ["reqwest-default-tls"]
reqwest-default-tls = ["reqwest/default-tls"]
reqwest-native-tls = ["reqwest/native-tls"]
reqwest-native-tls-no-alpn = ["reqwest/native-tls-no-alpn"]
reqwest-native-tls-vendored = ["reqwest/native-tls-vendored"]
reqwest-native-tls-vendored-no-alpn = ["reqwest/native-tls-vendored-no-alpn"]
reqwest-rustls = ["reqwest/rustls"]
9 changes: 9 additions & 0 deletions crates/cli/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use reqwest::Client;
use std::sync::LazyLock;

pub static CLIENT: LazyLock<Client> = LazyLock::new(|| {
Client::builder()
.user_agent(concat!("uts/", env!("CARGO_PKG_VERSION")))
.build()
.expect("Failed to build HTTP client")
});
29 changes: 29 additions & 0 deletions crates/cli/src/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use clap::Subcommand;

mod inspect;
mod stamp;
mod upgrade;
mod verify;

#[derive(Debug, Subcommand)]
pub enum Commands {
/// Inspect an ots file
Inspect(inspect::Inspect),
/// Verify an ots file against a file
Verify(verify::Verify),
/// Create timestamp
Stamp(stamp::Stamp),
/// Upgrade timestamp
Upgrade(upgrade::Upgrade),
}

impl Commands {
pub async fn run(self) -> eyre::Result<()> {
match self {
Commands::Inspect(cmd) => cmd.run(),
Commands::Verify(cmd) => cmd.run().await,
Commands::Stamp(cmd) => cmd.run().await,
Commands::Upgrade(cmd) => cmd.run().await,
}
}
}
34 changes: 34 additions & 0 deletions crates/cli/src/commands/inspect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use clap::Args;
use std::{fs, io, io::Seek, path::PathBuf};
use uts_core::codec::{
Decode, Reader, VersionedProof,
v1::{DetachedTimestamp, Timestamp},
};

#[derive(Debug, Args)]
pub struct Inspect {
/// Path to the OTS file to inspect
ots_file: PathBuf,
}

impl Inspect {
pub fn run(self) -> eyre::Result<()> {
let mut fh = fs::File::open(&self.ots_file)?;

match VersionedProof::<DetachedTimestamp>::decode(&mut Reader(&mut fh)) {
Ok(ots) => {
eprintln!("OTS Detached Timestamp found:\n{ots}");
return Ok(());
}
Err(e) => {
eprintln!("Not a valid Detached Timestamp OTS file (trying raw timestamp): {e}\n");
}
};

fh.seek(io::SeekFrom::Start(0))?;

let raw = Timestamp::decode(&mut Reader(&mut fh))?;
eprintln!("Raw Timestamp found:\n{raw}");
Ok(())
}
}
Loading