about summary refs log tree commit diff
path: root/tvix/store/src/proto/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/store/src/proto/mod.rs')
-rw-r--r--tvix/store/src/proto/mod.rs364
1 files changed, 155 insertions, 209 deletions
diff --git a/tvix/store/src/proto/mod.rs b/tvix/store/src/proto/mod.rs
index f3ea4b196946..807f03854ddc 100644
--- a/tvix/store/src/proto/mod.rs
+++ b/tvix/store/src/proto/mod.rs
@@ -4,7 +4,7 @@ use bytes::Bytes;
 use data_encoding::BASE64;
 // https://github.com/hyperium/tonic/issues/1056
 use nix_compat::{
-    narinfo::Flags,
+    narinfo::{Signature, SignatureError},
     nixhash::{CAHash, NixHash},
     store_path::{self, StorePathRef},
 };
@@ -17,6 +17,8 @@ pub use grpc_pathinfoservice_wrapper::GRPCPathInfoServiceWrapper;
 
 tonic::include_proto!("tvix.store.v1");
 
+use tvix_castore::proto as castorepb;
+
 #[cfg(feature = "tonic-reflection")]
 /// Compiled file descriptors for implementing [gRPC
 /// reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) with e.g.
@@ -70,183 +72,18 @@ pub enum ValidatePathInfoError {
     /// The deriver field is invalid.
     #[error("deriver field is invalid: {0}")]
     InvalidDeriverField(store_path::Error),
-}
 
-/// Parses a root node name.
-///
-/// On success, this returns the parsed [store_path::StorePathRef].
-/// On error, it returns an error generated from the supplied constructor.
-fn parse_node_name_root<E>(
-    name: &[u8],
-    err: fn(Vec<u8>, store_path::Error) -> E,
-) -> Result<store_path::StorePathRef<'_>, E> {
-    store_path::StorePathRef::from_bytes(name).map_err(|e| err(name.to_vec(), e))
-}
+    /// The narinfo field is missing
+    #[error("The narinfo field is missing")]
+    NarInfoFieldMissing,
 
-impl PathInfo {
-    /// validate performs some checks on the PathInfo struct,
-    /// Returning either a [store_path::StorePath] of the root node, or a
-    /// [ValidatePathInfoError].
-    pub fn validate(&self) -> Result<store_path::StorePath<String>, ValidatePathInfoError> {
-        // ensure the references have the right number of bytes.
-        for (i, reference) in self.references.iter().enumerate() {
-            if reference.len() != store_path::DIGEST_SIZE {
-                return Err(ValidatePathInfoError::InvalidReferenceDigestLen(
-                    i,
-                    reference.len(),
-                ));
-            }
-        }
+    /// The ca field is invalid
+    #[error("The ca field is invalid: {0}")]
+    InvalidCaField(ConvertCAError),
 
-        // If there is a narinfo field populated…
-        if let Some(narinfo) = &self.narinfo {
-            // ensure the nar_sha256 digest has the correct length.
-            if narinfo.nar_sha256.len() != 32 {
-                return Err(ValidatePathInfoError::InvalidNarSha256DigestLen(
-                    narinfo.nar_sha256.len(),
-                ));
-            }
-
-            // ensure the number of references there matches PathInfo.references count.
-            if narinfo.reference_names.len() != self.references.len() {
-                return Err(ValidatePathInfoError::InconsistentNumberOfReferences(
-                    self.references.len(),
-                    narinfo.reference_names.len(),
-                ));
-            }
-
-            // parse references in reference_names.
-            for (i, reference_name_str) in narinfo.reference_names.iter().enumerate() {
-                // ensure thy parse as (non-absolute) store path
-                let reference_names_store_path = store_path::StorePathRef::from_bytes(
-                    reference_name_str.as_bytes(),
-                )
-                .map_err(|_| {
-                    ValidatePathInfoError::InvalidNarinfoReferenceName(
-                        i,
-                        reference_name_str.to_owned(),
-                    )
-                })?;
-
-                // ensure their digest matches the one at self.references[i].
-                {
-                    // This is safe, because we ensured the proper length earlier already.
-                    let reference_digest = self.references[i].to_vec().try_into().unwrap();
-
-                    if reference_names_store_path.digest() != &reference_digest {
-                        return Err(
-                            ValidatePathInfoError::InconsistentNarinfoReferenceNameDigest(
-                                i,
-                                reference_digest,
-                                *reference_names_store_path.digest(),
-                            ),
-                        );
-                    }
-                }
-
-                // If the Deriver field is populated, ensure it parses to a
-                // [store_path::StorePath].
-                // We can't check for it to *not* end with .drv, as the .drv files produced by
-                // recursive Nix end with multiple .drv suffixes, and only one is popped when
-                // converting to this field.
-                if let Some(deriver) = &narinfo.deriver {
-                    store_path::StorePathRef::from_name_and_digest(&deriver.name, &deriver.digest)
-                        .map_err(ValidatePathInfoError::InvalidDeriverField)?;
-                }
-            }
-        }
-
-        // Ensure there is a (root) node present, and it properly parses to a [store_path::StorePath].
-        let root_nix_path = match &self.node {
-            None => Err(ValidatePathInfoError::NoNodePresent)?,
-            Some(node) => {
-                // NOTE: We could have some PathComponent not allocating here,
-                // so this can return StorePathRef.
-                // However, as this will get refactored away to stricter types
-                // soon anyways, there's no point.
-                let (name, _node) = node
-                    .clone()
-                    .into_name_and_node()
-                    .map_err(ValidatePathInfoError::InvalidRootNode)?;
-
-                // parse the name of the node itself and return
-                parse_node_name_root(name.as_ref(), ValidatePathInfoError::InvalidNodeName)?
-                    .to_owned()
-            }
-        };
-
-        // return the root nix path
-        Ok(root_nix_path)
-    }
-
-    /// With self and its store path name, this reconstructs a
-    /// [nix_compat::narinfo::NarInfo<'_>].
-    /// It can be used to validate Signatures, or get back a (sparse) NarInfo
-    /// struct to prepare writing it out.
-    ///
-    /// It assumes self to be validated first, and will only return None if the
-    /// `narinfo` field is unpopulated.
-    ///
-    /// It does very little allocation (a Vec each for `signatures` and
-    /// `references`), the rest points to data owned elsewhere.
-    ///
-    /// Keep in mind this is not able to reconstruct all data present in the
-    /// NarInfo<'_>, as some of it is not stored at all:
-    /// - the `system`, `file_hash` and `file_size` fields are set to `None`.
-    /// - the URL is set to an empty string.
-    /// - Compression is set to "none"
-    ///
-    /// If you want to render it out to a string and be able to parse it back
-    /// in, at least URL *must* be set again.
-    pub fn to_narinfo<'a>(
-        &'a self,
-        store_path: store_path::StorePathRef<'a>,
-    ) -> Option<nix_compat::narinfo::NarInfo<'_>> {
-        let narinfo = &self.narinfo.as_ref()?;
-
-        Some(nix_compat::narinfo::NarInfo {
-            flags: Flags::empty(),
-            store_path,
-            nar_hash: narinfo
-                .nar_sha256
-                .as_ref()
-                .try_into()
-                .expect("invalid narhash"),
-            nar_size: narinfo.nar_size,
-            references: narinfo
-                .reference_names
-                .iter()
-                .map(|ref_name| {
-                    // This shouldn't pass validation
-                    StorePathRef::from_bytes(ref_name.as_bytes()).expect("invalid reference")
-                })
-                .collect(),
-            signatures: narinfo
-                .signatures
-                .iter()
-                .map(|sig| {
-                    nix_compat::narinfo::SignatureRef::new(
-                        &sig.name,
-                        // This shouldn't pass validation
-                        sig.data[..].try_into().expect("invalid signature len"),
-                    )
-                })
-                .collect(),
-            ca: narinfo
-                .ca
-                .as_ref()
-                .map(|ca| ca.try_into().expect("invalid ca")),
-            system: None,
-            deriver: narinfo.deriver.as_ref().map(|deriver| {
-                StorePathRef::from_name_and_digest(&deriver.name, &deriver.digest)
-                    .expect("invalid deriver")
-            }),
-            url: "",
-            compression: Some("none"),
-            file_hash: None,
-            file_size: None,
-        })
-    }
+    /// The signature at position is invalid
+    #[error("The signature at position {0} is invalid: {1}")]
+    InvalidSignature(usize, SignatureError),
 }
 
 /// Errors that can occur when converting from a [nar_info::Ca] to a (stricter)
@@ -341,45 +178,154 @@ impl From<&nix_compat::nixhash::CAHash> for nar_info::Ca {
     }
 }
 
-impl From<&nix_compat::narinfo::NarInfo<'_>> for NarInfo {
-    /// Converts from a NarInfo (returned from the NARInfo parser) to the proto-
-    /// level NarInfo struct.
-    fn from(value: &nix_compat::narinfo::NarInfo<'_>) -> Self {
-        let signatures = value
-            .signatures
-            .iter()
-            .map(|sig| nar_info::Signature {
-                name: sig.name().to_string(),
-                data: Bytes::copy_from_slice(sig.bytes()),
-            })
-            .collect();
-
-        NarInfo {
-            nar_size: value.nar_size,
-            nar_sha256: Bytes::copy_from_slice(&value.nar_hash),
-            signatures,
-            reference_names: value.references.iter().map(|r| r.to_string()).collect(),
-            deriver: value.deriver.as_ref().map(|sp| StorePath {
-                name: (*sp.name()).to_owned(),
-                digest: Bytes::copy_from_slice(sp.digest()),
-            }),
-            ca: value.ca.as_ref().map(|ca| ca.into()),
-        }
-    }
-}
-
-impl From<&nix_compat::narinfo::NarInfo<'_>> for PathInfo {
-    /// Converts from a NarInfo (returned from the NARInfo parser) to a PathInfo
-    /// struct with the node set to None.
-    fn from(value: &nix_compat::narinfo::NarInfo<'_>) -> Self {
+impl From<crate::pathinfoservice::PathInfo> for PathInfo {
+    fn from(value: crate::pathinfoservice::PathInfo) -> Self {
         Self {
-            node: None,
+            node: Some(castorepb::Node::from_name_and_node(
+                value.store_path.to_string().into_bytes().into(),
+                value.node,
+            )),
             references: value
                 .references
                 .iter()
-                .map(|x| Bytes::copy_from_slice(x.digest()))
+                .map(|reference| Bytes::copy_from_slice(reference.digest()))
                 .collect(),
-            narinfo: Some(value.into()),
+            narinfo: Some(NarInfo {
+                nar_size: value.nar_size,
+                nar_sha256: Bytes::copy_from_slice(&value.nar_sha256),
+                signatures: value
+                    .signatures
+                    .iter()
+                    .map(|sig| nar_info::Signature {
+                        name: sig.name().to_string(),
+                        data: Bytes::copy_from_slice(sig.bytes()),
+                    })
+                    .collect(),
+                reference_names: value.references.iter().map(|r| r.to_string()).collect(),
+                deriver: value.deriver.as_ref().map(|sp| StorePath {
+                    name: (*sp.name()).to_owned(),
+                    digest: Bytes::copy_from_slice(sp.digest()),
+                }),
+                ca: value.ca.as_ref().map(|ca| ca.into()),
+            }),
+        }
+    }
+}
+
+impl TryFrom<PathInfo> for crate::pathinfoservice::PathInfo {
+    type Error = ValidatePathInfoError;
+    fn try_from(value: PathInfo) -> Result<Self, Self::Error> {
+        let narinfo = value
+            .narinfo
+            .ok_or_else(|| ValidatePathInfoError::NarInfoFieldMissing)?;
+
+        // ensure the references have the right number of bytes.
+        for (i, reference) in value.references.iter().enumerate() {
+            if reference.len() != store_path::DIGEST_SIZE {
+                return Err(ValidatePathInfoError::InvalidReferenceDigestLen(
+                    i,
+                    reference.len(),
+                ));
+            }
+        }
+
+        // ensure the number of references there matches PathInfo.references count.
+        if narinfo.reference_names.len() != value.references.len() {
+            return Err(ValidatePathInfoError::InconsistentNumberOfReferences(
+                value.references.len(),
+                narinfo.reference_names.len(),
+            ));
+        }
+
+        // parse references in reference_names.
+        let mut references = vec![];
+        for (i, reference_name_str) in narinfo.reference_names.iter().enumerate() {
+            // ensure thy parse as (non-absolute) store path
+            let reference_names_store_path =
+                StorePathRef::from_bytes(reference_name_str.as_bytes()).map_err(|_| {
+                    ValidatePathInfoError::InvalidNarinfoReferenceName(
+                        i,
+                        reference_name_str.to_owned(),
+                    )
+                })?;
+
+            // ensure their digest matches the one at self.references[i].
+            {
+                // This is safe, because we ensured the proper length earlier already.
+                let reference_digest = value.references[i].to_vec().try_into().unwrap();
+
+                if reference_names_store_path.digest() != &reference_digest {
+                    return Err(
+                        ValidatePathInfoError::InconsistentNarinfoReferenceNameDigest(
+                            i,
+                            reference_digest,
+                            *reference_names_store_path.digest(),
+                        ),
+                    );
+                } else {
+                    references.push(reference_names_store_path.to_owned());
+                }
+            }
         }
+
+        let nar_sha256_length = narinfo.nar_sha256.len();
+
+        // split value.node into the name and node components
+        let (name, node) = value
+            .node
+            .ok_or_else(|| ValidatePathInfoError::NoNodePresent)?
+            .into_name_and_node()
+            .map_err(ValidatePathInfoError::InvalidRootNode)?;
+
+        Ok(Self {
+            // value.node has a valid name according to the castore model but might not parse to a
+            // [StorePath]
+            store_path: nix_compat::store_path::StorePath::from_bytes(name.as_ref()).map_err(
+                |err| ValidatePathInfoError::InvalidNodeName(name.as_ref().to_vec(), err),
+            )?,
+            node,
+            references,
+            nar_size: narinfo.nar_size,
+            nar_sha256: narinfo.nar_sha256.to_vec()[..]
+                .try_into()
+                .map_err(|_| ValidatePathInfoError::InvalidNarSha256DigestLen(nar_sha256_length))?,
+            // If the Deriver field is populated, ensure it parses to a
+            // [StorePath].
+            // We can't check for it to *not* end with .drv, as the .drv files produced by
+            // recursive Nix end with multiple .drv suffixes, and only one is popped when
+            // converting to this field.
+            deriver: narinfo
+                .deriver
+                .map(|deriver| {
+                    nix_compat::store_path::StorePath::from_name_and_digest(
+                        &deriver.name,
+                        &deriver.digest,
+                    )
+                    .map_err(ValidatePathInfoError::InvalidDeriverField)
+                })
+                .transpose()?,
+            signatures: narinfo
+                .signatures
+                .into_iter()
+                .enumerate()
+                .map(|(i, signature)| {
+                    signature.data.to_vec()[..]
+                        .try_into()
+                        .map_err(|_| {
+                            ValidatePathInfoError::InvalidSignature(
+                                i,
+                                SignatureError::InvalidSignatureLen(signature.data.len()),
+                            )
+                        })
+                        .map(|signature_data| Signature::new(signature.name, signature_data))
+                })
+                .collect::<Result<Vec<_>, ValidatePathInfoError>>()?,
+            ca: narinfo
+                .ca
+                .as_ref()
+                .map(TryFrom::try_from)
+                .transpose()
+                .map_err(ValidatePathInfoError::InvalidCaField)?,
+        })
     }
 }