From 39602580959c3ffa9ceb77c036b06563260cca96 Mon Sep 17 00:00:00 2001 From: Kevin Lewi Date: Wed, 4 Mar 2026 16:55:44 -0800 Subject: [PATCH] Add VerifiableKeyDirectory trait to akd_core Introduces an abstract trait for verifiable key directories, enabling consumers to program against a common interface and swap backends (e.g. AKD's Merkle-tree vs polynomial-commitment-based implementations). Moves EpochHash from akd to akd_core since the trait requires it and it has no akd-specific dependencies. --- akd/src/directory.rs | 61 ++++++++++++++++++++++++++ akd/src/helper_structs.rs | 16 ------- akd/src/lib.rs | 3 +- akd_core/src/directory_trait.rs | 76 +++++++++++++++++++++++++++++++++ akd_core/src/lib.rs | 5 +++ akd_core/src/types/mod.rs | 15 +++++++ 6 files changed, 158 insertions(+), 18 deletions(-) create mode 100644 akd_core/src/directory_trait.rs diff --git a/akd/src/directory.rs b/akd/src/directory.rs index f72c8201..bb541751 100644 --- a/akd/src/directory.rs +++ b/akd/src/directory.rs @@ -24,6 +24,8 @@ use crate::VersionFreshness; use akd_core::configuration::Configuration; use akd_core::utils::get_marker_versions; use akd_core::verify::history::HistoryParams; +use akd_core::VerifiableKeyDirectory; +use async_trait::async_trait; use std::collections::{HashMap, HashSet}; use std::marker::PhantomData; use std::sync::Arc; @@ -847,6 +849,65 @@ where } } +#[async_trait] +impl VerifiableKeyDirectory for Directory +where + TC: Configuration, + S: Database + 'static, + V: VRFKeyStorage, +{ + type LookupProof = LookupProof; + type HistoryProof = HistoryProof; + type AuditProof = AppendOnlyProof; + type PublicKey = VRFPublicKey; + type Error = AkdError; + + async fn publish( + &self, + updates: Vec<(AkdLabel, AkdValue)>, + ) -> Result { + Directory::publish(self, updates).await + } + + async fn lookup( + &self, + label: AkdLabel, + ) -> Result<(LookupProof, EpochHash), AkdError> { + Directory::lookup(self, label).await + } + + async fn batch_lookup( + &self, + labels: &[AkdLabel], + ) -> Result<(Vec, EpochHash), AkdError> { + Directory::batch_lookup(self, labels).await + } + + async fn key_history( + &self, + label: &AkdLabel, + params: HistoryParams, + ) -> Result<(HistoryProof, EpochHash), AkdError> { + Directory::key_history(self, label, params).await + } + + async fn audit( + &self, + start: u64, + end: u64, + ) -> Result { + Directory::audit(self, start, end).await + } + + async fn get_public_key(&self) -> Result { + Directory::get_public_key(self).await + } + + async fn get_epoch_hash(&self) -> Result { + Directory::get_epoch_hash(self).await + } +} + /// A thin newtype which offers read-only interactivity with a [Directory]. #[derive(Clone)] pub struct ReadOnlyDirectory(Directory) diff --git a/akd/src/helper_structs.rs b/akd/src/helper_structs.rs index 707e5329..54c9a417 100644 --- a/akd/src/helper_structs.rs +++ b/akd/src/helper_structs.rs @@ -8,24 +8,8 @@ //! Helper structs that are used for various data structures, //! to make it easier to pass arguments around. -use crate::Digest; use crate::{storage::types::ValueState, NodeLabel}; -/// Root hash of the tree and its associated epoch -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct EpochHash(pub u64, pub Digest); - -impl EpochHash { - /// Get the contained epoch - pub fn epoch(&self) -> u64 { - self.0 - } - /// Get the contained hash - pub fn hash(&self) -> Digest { - self.1 - } -} - #[derive(Clone, Debug)] /// Info needed for a lookup of a user for an epoch pub struct LookupInfo { diff --git a/akd/src/lib.rs b/akd/src/lib.rs index 1cbb7016..c1cb1610 100644 --- a/akd/src/lib.rs +++ b/akd/src/lib.rs @@ -558,7 +558,7 @@ pub mod local_auditing; pub use akd_core::{ configuration, configuration::*, ecvrf, hash, hash::Digest, proto, types::*, verify, - verify::history::HistoryParams, ARITY, + verify::history::HistoryParams, VerifiableKeyDirectory, ARITY, }; #[macro_use] @@ -568,7 +568,6 @@ mod utils; pub use append_only_zks::{Azks, AzksParallelismConfig, AzksParallelismOption}; pub use client::HistoryVerificationParams; pub use directory::Directory; -pub use helper_structs::EpochHash; // ========== Constants and type aliases ========== // #[cfg(any(test, feature = "public_tests"))] diff --git a/akd_core/src/directory_trait.rs b/akd_core/src/directory_trait.rs new file mode 100644 index 00000000..d504ca08 --- /dev/null +++ b/akd_core/src/directory_trait.rs @@ -0,0 +1,76 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// +// This source code is dual-licensed under either the MIT license found in the +// LICENSE-MIT file in the root directory of this source tree or the Apache +// License, Version 2.0 found in the LICENSE-APACHE file in the root directory +// of this source tree. You may select, at your option, one of the above-listed licenses. + +//! Abstract trait for a verifiable key directory. +//! +//! Both AKD (Merkle-tree-based) and other implementations (e.g. polynomial-commitment-based) +//! can implement this trait, enabling consumers to swap backends. + +use async_trait::async_trait; + +use crate::types::{AkdLabel, AkdValue, EpochHash}; +use crate::verify::history::HistoryParams; + +/// Abstract Verifiable Key Directory interface. +/// +/// This trait defines the core operations that any verifiable key directory +/// must support: publishing updates, looking up entries, querying key history, +/// and generating audit proofs. Proof types and errors are backend-specific +/// associated types. +#[cfg_attr(docsrs, doc(cfg(not(feature = "nostd"))))] +#[async_trait] +pub trait VerifiableKeyDirectory: Send + Sync { + /// The proof type returned by a single-key lookup. + type LookupProof: Send + Sync; + /// The proof type returned by a key history query. + type HistoryProof: Send + Sync; + /// The proof type returned by an audit between two epochs. + type AuditProof: Send + Sync; + /// The public key type for this directory. + type PublicKey: Send + Sync; + /// The error type for this directory. + type Error: std::error::Error + Send + Sync; + + /// Publish a batch of label-value updates to the directory, returning the + /// new epoch and root hash. + async fn publish( + &self, + updates: Vec<(AkdLabel, AkdValue)>, + ) -> Result; + + /// Generate a lookup proof for a single label at the latest epoch. + async fn lookup( + &self, + label: AkdLabel, + ) -> Result<(Self::LookupProof, EpochHash), Self::Error>; + + /// Generate lookup proofs for multiple labels at the latest epoch. + async fn batch_lookup( + &self, + labels: &[AkdLabel], + ) -> Result<(Vec, EpochHash), Self::Error>; + + /// Generate a key history proof for a label. + async fn key_history( + &self, + label: &AkdLabel, + params: HistoryParams, + ) -> Result<(Self::HistoryProof, EpochHash), Self::Error>; + + /// Generate an audit proof between two epochs. + async fn audit( + &self, + start: u64, + end: u64, + ) -> Result; + + /// Retrieve the public key for this directory. + async fn get_public_key(&self) -> Result; + + /// Retrieve the current epoch and root hash. + async fn get_epoch_hash(&self) -> Result; +} diff --git a/akd_core/src/lib.rs b/akd_core/src/lib.rs index 4a5f14d6..9fe76204 100644 --- a/akd_core/src/lib.rs +++ b/akd_core/src/lib.rs @@ -207,6 +207,11 @@ extern crate alloc; #[cfg(all(feature = "protobuf", not(feature = "nostd")))] pub mod proto; +#[cfg(not(feature = "nostd"))] +pub mod directory_trait; +#[cfg(not(feature = "nostd"))] +pub use directory_trait::VerifiableKeyDirectory; + pub mod ecvrf; pub mod hash; pub mod utils; diff --git a/akd_core/src/types/mod.rs b/akd_core/src/types/mod.rs index 5c8fdefa..ece97382 100644 --- a/akd_core/src/types/mod.rs +++ b/akd_core/src/types/mod.rs @@ -273,6 +273,21 @@ impl AkdValue { } } +/// Root hash of the tree and its associated epoch +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct EpochHash(pub u64, pub Digest); + +impl EpochHash { + /// Get the contained epoch + pub fn epoch(&self) -> u64 { + self.0 + } + /// Get the contained hash + pub fn hash(&self) -> Digest { + self.1 + } +} + /// The value to be hashed every time an empty node's hash is to be considered pub const EMPTY_VALUE: [u8; 1] = [0u8];