Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added registry index for fetched records and updated proof endpoints to use the log length and registry index #187

Merged
merged 26 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e4ba1f3
added registry log index for operator and package records on the fetc…
calvinrp Aug 24, 2023
71f9ab5
refactored proof endpoints to use log_length and log index
calvinrp Aug 24, 2023
0cb1a12
updated openapi.yaml
calvinrp Aug 24, 2023
327e4b1
fixed bug assuming the Node(usize) was the registry log index
calvinrp Aug 25, 2023
eb8ffb9
fixed error
calvinrp Aug 25, 2023
8639915
added type alias for log_length and log_index and fixed issues
calvinrp Aug 25, 2023
e3f6fe5
renamed fields
calvinrp Aug 25, 2023
2c80fea
fixed debug endpoints
calvinrp Aug 25, 2023
bbfe6cc
cargo fmt
calvinrp Aug 25, 2023
fd7a132
fixed required field in openapi.yaml
calvinrp Aug 25, 2023
8ba0ea8
fix openapi.yaml
calvinrp Aug 25, 2023
5d72362
more fixes for openapi.yaml
calvinrp Aug 25, 2023
9f1e6ae
fixing type in postgres tests
calvinrp Aug 25, 2023
9d8bb33
fixed fmt again
calvinrp Aug 25, 2023
becad3d
set order on the sql more explicitly
calvinrp Aug 25, 2023
8e0fcbd
removed unused field
calvinrp Aug 25, 2023
31ea3e4
fixed map inclusion proof; the order of the requested logleafs must b…
calvinrp Aug 26, 2023
53ca289
added another alias type for RegistryLen as well as RegistryIndex
calvinrp Aug 26, 2023
8c253d5
uncommented out proof error enum type
calvinrp Aug 26, 2023
521277f
cargo fmt
calvinrp Aug 26, 2023
58905e4
changed the fetch logs endpoint to use log length instead of checkpoi…
calvinrp Aug 26, 2023
c914061
cargo fmt
calvinrp Aug 26, 2023
2c13f63
fixed debug endpoints
calvinrp Aug 26, 2023
163369c
removed unused import
calvinrp Aug 26, 2023
5bff826
clippy fixes
calvinrp Aug 28, 2023
6d5088e
simplified datastore service get_all_validated_records
calvinrp Aug 28, 2023
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/content
/target
*.swp
*.swo
6 changes: 3 additions & 3 deletions crates/api/src/v1/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use thiserror::Error;
use warg_crypto::hash::AnyHash;
use warg_protocol::{
registry::{LogId, RecordId},
ProtoEnvelopeBody,
PublishedProtoEnvelopeBody,
};

/// Represents a fetch logs request.
Expand Down Expand Up @@ -36,10 +36,10 @@ pub struct FetchLogsResponse {
pub more: bool,
/// The operator records appended since the last known operator record.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub operator: Vec<ProtoEnvelopeBody>,
pub operator: Vec<PublishedProtoEnvelopeBody>,
/// The package records appended since last known package record ids.
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub packages: HashMap<LogId, Vec<ProtoEnvelopeBody>>,
pub packages: HashMap<LogId, Vec<PublishedProtoEnvelopeBody>>,
}

/// Represents a fetch API error.
Expand Down
6 changes: 3 additions & 3 deletions crates/api/src/v1/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{borrow::Cow, collections::HashMap};
use thiserror::Error;
use warg_crypto::hash::AnyHash;
use warg_protocol::{
registry::{LogId, PackageId, RecordId},
registry::{LogId, PackageId, RecordId, RegistryIndex},
ProtoEnvelopeBody,
};

Expand Down Expand Up @@ -105,10 +105,10 @@ pub enum PackageRecordState {
Published {
/// The envelope of the package record.
record: ProtoEnvelopeBody,
/// The index of the record in the registry log.
registry_log_index: u32,
/// The content sources of the record.
content_sources: HashMap<AnyHash, Vec<ContentSource>>,
/// The published index of the record in the registry log.
registry_index: RegistryIndex,
},
}

Expand Down
101 changes: 30 additions & 71 deletions crates/api/src/v1/proof.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
//! Types relating to the proof API.

use crate::Status;
use serde::{de::Unexpected, Deserialize, Serialize, Serializer};
use serde::{Deserialize, Serialize, Serializer};
use serde_with::{base64::Base64, serde_as};
use std::borrow::Cow;
use thiserror::Error;
use warg_crypto::hash::AnyHash;
use warg_protocol::registry::{Checkpoint, LogId, LogLeaf};
use warg_protocol::registry::{LogId, RegistryIndex};

/// Represents a consistency proof request.
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConsistencyRequest {
/// The starting log length to check for consistency.
pub from: u32,
pub from: RegistryIndex,
/// The ending log length to check for consistency.
pub to: u32,
pub to: RegistryIndex,
}

/// Represents a consistency proof response.
Expand All @@ -31,11 +31,11 @@ pub struct ConsistencyResponse {
/// Represents an inclusion proof request.
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InclusionRequest<'a> {
/// The checkpoint to check for inclusion.
pub checkpoint: Cow<'a, Checkpoint>,
/// The log leafs to check for inclusion.
pub leafs: Cow<'a, [LogLeaf]>,
pub struct InclusionRequest {
/// The log length to check for inclusion.
pub log_length: RegistryIndex,
calvinrp marked this conversation as resolved.
Show resolved Hide resolved
/// The log leaf indexes in the registry log to check for inclusion.
pub leafs: Vec<RegistryIndex>,
}

/// Represents an inclusion proof response.
Expand All @@ -55,12 +55,12 @@ pub struct InclusionResponse {
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum ProofError {
/// The provided log root was not found.
#[error("log root `{0}` was not found")]
RootNotFound(AnyHash),
/// The checkpoint could not be found for the provided log length.
#[error("checkpoint not found for log length {0}")]
CheckpointNotFound(RegistryIndex),
/// The provided log leaf was not found.
#[error("log leaf `{}:{}` was not found", .0.log_id, .0.record_id)]
LeafNotFound(LogLeaf),
#[error("log leaf `{0}` exceeds the registry log length")]
LeafNotFound(RegistryIndex),
/// Failed to prove inclusion of a package.
#[error("failed to prove inclusion of package log `{0}`")]
PackageLogNotIncluded(LogId),
Expand Down Expand Up @@ -89,7 +89,7 @@ impl ProofError {
/// Returns the HTTP status code of the error.
pub fn status(&self) -> u16 {
match self {
Self::RootNotFound(_) | Self::LeafNotFound(_) => 404,
Self::CheckpointNotFound(_) | Self::LeafNotFound(_) => 404,
Self::BundleFailure(_)
| Self::PackageLogNotIncluded(_)
| Self::IncorrectProof { .. } => 422,
Expand All @@ -101,7 +101,7 @@ impl ProofError {
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
enum EntityType {
LogRoot,
LogLength,
Leaf,
}

Expand All @@ -122,16 +122,12 @@ enum BundleError<'a> {

#[derive(Serialize, Deserialize)]
#[serde(untagged, rename_all = "camelCase")]
enum RawError<'a, T>
where
T: Clone + ToOwned,
<T as ToOwned>::Owned: Serialize + for<'b> Deserialize<'b>,
{
enum RawError<'a> {
NotFound {
status: Status<404>,
#[serde(rename = "type")]
ty: EntityType,
id: Cow<'a, T>,
id: RegistryIndex,
},
BundleError {
status: Status<422>,
Expand All @@ -147,41 +143,41 @@ where
impl Serialize for ProofError {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::RootNotFound(root) => RawError::NotFound {
Self::CheckpointNotFound(log_length) => RawError::NotFound {
status: Status::<404>,
ty: EntityType::LogRoot,
id: Cow::Borrowed(root),
ty: EntityType::LogLength,
id: *log_length,
}
.serialize(serializer),
Self::LeafNotFound(leaf) => RawError::NotFound::<String> {
Self::LeafNotFound(leaf_index) => RawError::NotFound {
status: Status::<404>,
ty: EntityType::Leaf,
id: Cow::Owned(format!("{}|{}", leaf.log_id, leaf.record_id)),
id: *leaf_index,
}
.serialize(serializer),
Self::PackageLogNotIncluded(log_id) => RawError::BundleError::<()> {
Self::PackageLogNotIncluded(log_id) => RawError::BundleError {
status: Status::<422>,
error: BundleError::PackageNotIncluded {
log_id: Cow::Borrowed(log_id),
},
}
.serialize(serializer),
Self::IncorrectProof { root, found } => RawError::BundleError::<()> {
Self::IncorrectProof { root, found } => RawError::BundleError {
status: Status::<422>,
error: BundleError::IncorrectProof {
root: Cow::Borrowed(root),
found: Cow::Borrowed(found),
},
}
.serialize(serializer),
Self::BundleFailure(message) => RawError::BundleError::<()> {
Self::BundleFailure(message) => RawError::BundleError {
status: Status::<422>,
error: BundleError::Failure {
message: Cow::Borrowed(message),
},
}
.serialize(serializer),
Self::Message { status, message } => RawError::Message::<()> {
Self::Message { status, message } => RawError::Message {
status: *status,
message: Cow::Borrowed(message),
}
Expand All @@ -195,47 +191,10 @@ impl<'de> Deserialize<'de> for ProofError {
where
D: serde::Deserializer<'de>,
{
match RawError::<String>::deserialize(deserializer)? {
match RawError::deserialize(deserializer)? {
RawError::NotFound { status: _, ty, id } => match ty {
EntityType::LogRoot => {
Ok(Self::RootNotFound(id.parse::<AnyHash>().map_err(|_| {
serde::de::Error::invalid_value(
Unexpected::Str(&id),
&"a valid checkpoint id",
)
})?))
}
EntityType::Leaf => Ok(Self::LeafNotFound(
id.split_once('|')
.map(|(log_id, record_id)| {
Ok(LogLeaf {
log_id: log_id
.parse::<AnyHash>()
.map_err(|_| {
serde::de::Error::invalid_value(
Unexpected::Str(log_id),
&"a valid log id",
)
})?
.into(),
record_id: record_id
.parse::<AnyHash>()
.map_err(|_| {
serde::de::Error::invalid_value(
Unexpected::Str(record_id),
&"a valid record id",
)
})?
.into(),
})
})
.ok_or_else(|| {
serde::de::Error::invalid_value(
Unexpected::Str(&id),
&"a valid leaf id",
)
})??,
)),
EntityType::LogLength => Ok(Self::CheckpointNotFound(id)),
EntityType::Leaf => Ok(Self::LeafNotFound(id)),
},
RawError::BundleError { status: _, error } => match error {
BundleError::PackageNotIncluded { log_id } => {
Expand Down
13 changes: 7 additions & 6 deletions crates/client/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,12 @@ impl Client {
}

/// Proves the inclusion of the given package log heads in the registry.
pub async fn prove_inclusion(&self, request: InclusionRequest<'_>) -> Result<(), ClientError> {
pub async fn prove_inclusion(
&self,
request: InclusionRequest,
checkpoint: &Checkpoint,
leafs: &[LogLeaf],
) -> Result<(), ClientError> {
let url = self.url.join(paths::prove_inclusion());
tracing::debug!("proving checkpoint inclusion at `{url}`");

Expand All @@ -246,11 +251,7 @@ impl Client {
)
.await?;

Self::validate_inclusion_response(
response,
request.checkpoint.as_ref(),
request.leafs.as_ref(),
)
Self::validate_inclusion_response(response, checkpoint, leafs)
}

/// Proves consistency between two log roots.
Expand Down
38 changes: 24 additions & 14 deletions crates/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use warg_crypto::{
use warg_protocol::{
operator, package,
registry::{LogId, LogLeaf, PackageId, RecordId, TimestampedCheckpoint},
ProtoEnvelope, SerdeEnvelope, Version, VersionReq,
PublishedProtoEnvelope, SerdeEnvelope, Version, VersionReq,
};

pub mod api;
Expand Down Expand Up @@ -404,11 +404,12 @@ impl<R: RegistryStorage, C: ContentStorage> Client<R, C> {
})?;

for record in response.operator {
let record: ProtoEnvelope<operator::OperatorRecord> = record.try_into()?;
let record: PublishedProtoEnvelope<operator::OperatorRecord> = record.try_into()?;
operator
.state
.validate(&record)
.validate(&record.envelope)
.map_err(|inner| ClientError::OperatorValidationFailed { inner })?;
operator.head_registry_index = Some(record.registry_index);
}

for (log_id, records) in response.packages {
Expand All @@ -417,13 +418,15 @@ impl<R: RegistryStorage, C: ContentStorage> Client<R, C> {
})?;

for record in records {
let record: ProtoEnvelope<package::PackageRecord> = record.try_into()?;
package.state.validate(&record).map_err(|inner| {
let record: PublishedProtoEnvelope<package::PackageRecord> =
record.try_into()?;
package.state.validate(&record.envelope).map_err(|inner| {
ClientError::PackageValidationFailed {
id: package.id.clone(),
inner,
}
})?;
package.head_registry_index = Some(record.registry_index);
}

// At this point, the package log should not be empty
Expand All @@ -445,29 +448,36 @@ impl<R: RegistryStorage, C: ContentStorage> Client<R, C> {
}

// Prove inclusion for the current log heads
let mut leafs = Vec::with_capacity(packages.len() + 1 /* for operator */);
if let Some(head) = operator.state.head() {
let mut leaf_indices = Vec::with_capacity(packages.len() + 1 /* for operator */);
let mut leafs = Vec::with_capacity(leaf_indices.len());
if let Some(index) = operator.head_registry_index {
leaf_indices.push(index);
leafs.push(LogLeaf {
log_id: LogId::operator_log::<Sha256>(),
record_id: head.digest.clone(),
record_id: operator.state.head().as_ref().unwrap().digest.clone(),
});
}

for (log_id, package) in &packages {
if let Some(head) = package.state.head() {
if let Some(index) = package.head_registry_index {
leaf_indices.push(index);
leafs.push(LogLeaf {
log_id: log_id.clone(),
record_id: head.digest.clone(),
record_id: package.state.head().as_ref().unwrap().digest.clone(),
});
}
}

if !leafs.is_empty() {
self.api
.prove_inclusion(InclusionRequest {
checkpoint: Cow::Borrowed(checkpoint),
leafs: Cow::Borrowed(&leafs),
})
.prove_inclusion(
InclusionRequest {
log_length: checkpoint.log_length,
leafs: leaf_indices,
},
&checkpoint,
&leafs,
)
.await?;
}

Expand Down
Loading
Loading