1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under both the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! This module contains all of the structs which need to be constructed
//! to verify any of the following AKD proofs
//!
//! 1. Lookup
//!
//! Append-only and history proofs to come
use crate::ARITY;
#[cfg(feature = "nostd")]
use alloc::vec::Vec;
use core::convert::TryInto;
// ============================================
// Typedefs and constants
// ============================================
/// This type is used to indicate a direction for a
/// particular node relative to its parent.
pub type Direction = Option<usize>;
/// The label of a particular entry in the AKD
pub type AkdLabel = Vec<u8>;
/// The value of a particular entry in the AKD
pub type AkdValue = Vec<u8>;
/// A hash digest (size will depend on hashing algorithm specified
/// at compilation time)
pub type Digest = [u8; crate::hash::DIGEST_BYTES];
/// The value to be hashed every time an empty node's hash is to be considered
pub const EMPTY_VALUE: [u8; 1] = [0u8];
/// The label used for an empty node
pub const EMPTY_LABEL: NodeLabel = NodeLabel {
label_val: [1u8; 32],
label_len: 0,
};
/// A "tombstone" is a false value in an AKD ValueState denoting that a real value has been removed (e.g. data rentention policies).
/// Should a tombstone be encountered, we have to assume that the hash of the value is correct, and we move forward without being able to
/// verify the raw value. We utilize an empty array to save space in the storage layer
///
/// See [GitHub issue #130](https://github.com/novifinancial/akd/issues/130) for more context
pub const TOMBSTONE: &[u8] = &[];
// ============================================
// Structs
// ============================================
/// Represents the label of a AKD node
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct NodeLabel {
/// val stores a binary string as a u64
pub label_val: [u8; 32],
/// len keeps track of how long the binary string is
pub label_len: u32,
}
impl NodeLabel {
pub(crate) fn hash(&self) -> Digest {
let hash_input = [&self.label_len.to_be_bytes()[..], &self.label_val].concat();
crate::hash::hash(&hash_input)
}
/// Takes as input a pointer to the caller and another NodeLabel,
/// returns a NodeLabel that is the longest common prefix of the two.
pub(crate) fn get_longest_common_prefix(&self, other: NodeLabel) -> Self {
let shorter_len = if self.label_len < other.label_len {
self.label_len
} else {
other.label_len
};
let mut prefix_len = 0;
while prefix_len <= shorter_len
&& self.get_bit_at(prefix_len) == other.get_bit_at(prefix_len)
{
prefix_len += 1;
}
if *self == EMPTY_LABEL || other == EMPTY_LABEL {
return EMPTY_LABEL;
}
self.get_prefix(prefix_len)
}
/// Returns the bit at a specified index, and a 0 on an out of range index
fn get_bit_at(&self, index: u32) -> u8 {
if index >= self.label_len {
return 0;
}
let usize_index: usize = index.try_into().unwrap();
let index_full_blocks = usize_index / 8;
let index_remainder = usize_index % 8;
(self.label_val[index_full_blocks] >> (7 - index_remainder)) & 1
}
/// Returns the prefix of a specified length, and the entire value on an out of range length
pub(crate) fn get_prefix(&self, len: u32) -> Self {
if len >= self.label_len {
return *self;
}
if len == 0 {
return Self {
label_val: [0u8; 32],
label_len: 0,
};
}
let usize_len: usize = (len - 1).try_into().unwrap();
let len_remainder = usize_len % 8;
let len_div = usize_len / 8;
let mut out_val = [0u8; 32];
out_val[..len_div].clone_from_slice(&self.label_val[..len_div]);
out_val[len_div] = (self.label_val[len_div] >> (7 - len_remainder)) << (7 - len_remainder);
Self {
label_val: out_val,
label_len: len,
}
}
}
/// Represents a node (label + hash) in the AKD
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct Node {
/// The label of the node
pub label: NodeLabel,
/// The associated hash of the node
pub hash: Digest,
}
/// Represents a specific level of the tree with the parental sibling and the direction
/// of the parent for use in tree hash calculations
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct LayerProof {
/// The parent's label
pub label: NodeLabel,
/// Siblings of the parent
pub siblings: [Node; ARITY - 1],
/// The direction
pub direction: Direction,
}
/// Merkle proof of membership of a [`NodeLabel`] with a particular hash
/// value in the tree at a given epoch
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct MembershipProof {
/// The node label
pub label: NodeLabel,
/// The hash of the value
pub hash_val: Digest,
/// The parents of the node in question
pub layer_proofs: Vec<LayerProof>,
}
/// Merkle Patricia proof of non-membership for a [`NodeLabel`] in the tree
/// at a given epoch.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct NonMembershipProof {
/// The label in question
pub label: NodeLabel,
/// The longest prefix in the tree
pub longest_prefix: NodeLabel,
/// The children of the longest prefix
pub longest_prefix_children: [Node; ARITY],
/// The membership proof of the longest prefix
pub longest_prefix_membership_proof: MembershipProof,
}
/// Proof that a given label was at a particular state at the given epoch.
/// This means we need to show that the state and version we are claiming for this node must have been:
/// * committed in the tree,
/// * not too far ahead of the most recent marker version,
/// * not stale when served.
/// This proof is sent in response to a lookup query for a particular key.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct LookupProof {
/// The epoch of this record
pub epoch: u64,
/// The plaintext value in question
pub plaintext_value: AkdValue,
/// The version of the record
pub version: u64,
/// VRF proof for the label corresponding to this version
pub existence_vrf_proof: Vec<u8>,
/// Record existence proof
pub existence_proof: MembershipProof,
/// VRF proof for the marker preceding (less than or equal to) this version
pub marker_vrf_proof: Vec<u8>,
/// Existence at specific marker
pub marker_proof: MembershipProof,
/// VRF proof for the label corresponding to this version being stale
pub freshness_vrf_proof: Vec<u8>,
/// Freshness proof (non member at previous epoch)
pub freshness_proof: NonMembershipProof,
/// Proof for commitment value derived from raw AkdLabel and AkdValue
pub commitment_proof: Vec<u8>,
}
/// A vector of UpdateProofs are sent as the proof to a history query for a particular key.
/// For each version of the value associated with the key, the verifier must check that:
/// * the version was included in the claimed epoch,
/// * the previous version was retired at this epoch,
/// * the version did not exist prior to this epoch,
/// * the next few versions (up until the next marker), did not exist at this epoch,
/// * the future marker versions did not exist at this epoch.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct UpdateProof {
/// Epoch of this update
pub epoch: u64,
/// Value at this update
pub plaintext_value: AkdValue,
/// Version at this update
pub version: u64,
/// VRF proof for the label for the current version
pub existence_vrf_proof: Vec<u8>,
/// Membership proof to show that the key was included in this epoch
pub existence_at_ep: MembershipProof,
/// VRF proof for the label for the previous version which became stale
pub previous_version_vrf_proof: Option<Vec<u8>>,
/// Proof that previous value was set to old at this epoch
pub previous_version_stale_at_ep: Option<MembershipProof>,
/// Proof for commitment value derived from raw AkdLabel and AkdValue
pub commitment_proof: Vec<u8>,
}
/// This proof is just an array of [`UpdateProof`]s.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct HistoryProof {
/// The update proofs in the key history
pub update_proofs: Vec<UpdateProof>,
/// VRF Proofs for the labels of the next few values
pub next_few_vrf_proofs: Vec<Vec<u8>>,
/// Proof that the next few values did not exist at this time
pub non_existence_of_next_few: Vec<NonMembershipProof>,
/// VRF proofs for the labels of future marker entries
pub future_marker_vrf_proofs: Vec<Vec<u8>>,
/// Proof that future markers did not exist
pub non_existence_of_future_markers: Vec<NonMembershipProof>,
}
/// The payload that is outputted as a result of successful verification of
/// a [LookupProof] or [HistoryProof]. This includes the fields containing the
/// epoch that the leaf was published in, the version corresponding to the value,
/// and the value itself.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde_serialization",
derive(serde::Deserialize, serde::Serialize)
)]
pub struct VerifyResult {
/// The epoch of this record
pub epoch: u64,
/// Version at this update
pub version: u64,
/// The plaintext value associated with the record
pub value: AkdValue,
}