about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2023-01-23T13·18+0100
committerflokli <flokli@flokli.de>2023-01-23T13·40+0000
commitfc177af0c1dab6a4504e7e95c63507eb2a942e8f (patch)
tree28e07d30d8a1f220baaf833346ba2e81fe7c2ed2
parent5d856ac2e9b24f7b5a72346d8206cc366e1663ae (diff)
refactor(tvix/derivation): pass fingerprint to build_store_path r/5738
All of these occurences are actually sha256 digests of some fingerprint
- which means there's not a lot of need to do the hashing outside.
Instead of passing in a digest, keep the sha256 hasher in the function,
and pass in a fingerprint.

This makes it much easier to construct fingerprints using format!() in
all consumers, because we need don't need to juggle with the hasher
anymore.

Change-Id: I2dc3af2cab6cf06f55ae6cbd9a8be95faf2a07b6
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7907
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
-rw-r--r--tvix/derivation/src/derivation.rs114
-rw-r--r--tvix/derivation/src/write.rs3
2 files changed, 51 insertions, 66 deletions
diff --git a/tvix/derivation/src/derivation.rs b/tvix/derivation/src/derivation.rs
index 1f9ed6146b..d8870386e2 100644
--- a/tvix/derivation/src/derivation.rs
+++ b/tvix/derivation/src/derivation.rs
@@ -50,13 +50,19 @@ fn compress_hash(input: &[u8], output_size: usize) -> Vec<u8> {
 }
 
 /// This returns a store path, either of a derivation or a regular output.
-/// The path_hash is compressed to 20 bytes, and nixbase32-encoded (32 characters)
+/// The string is hashed with sha256, its digest is compressed to 20 bytes, and
+/// nixbase32-encoded (32 characters)
 fn build_store_path(
     is_derivation: bool,
-    path_hash: &[u8],
+    fingerprint: &str,
     name: &str,
 ) -> Result<StorePath, DerivationError> {
-    let compressed = compress_hash(path_hash, 20);
+    let digest = {
+        let mut hasher = Sha256::new();
+        hasher.update(fingerprint);
+        hasher.finalize()
+    };
+    let compressed = compress_hash(&digest, 20);
     if is_derivation {
         StorePath::from_string(
             format!(
@@ -82,23 +88,25 @@ pub fn path_with_references<'a, I: IntoIterator<Item = &'a str>, C: AsRef<[u8]>>
     content: C,
     references: I,
 ) -> Result<StorePath, DerivationError> {
-    let mut hasher = Sha256::new();
-    hasher.update(content);
-    let content_hash = hasher.finalize_reset();
+    let mut s = String::from("text");
 
-    hasher.update("text");
     for reference in references {
-        hasher.update(":");
-        hasher.update(reference);
+        s.push(':');
+        s.push_str(reference);
     }
 
-    hasher.update(&format!(":sha256:{:x}:", content_hash));
-    hasher.update(STORE_DIR);
-    hasher.update(&format!(":{}", name));
+    let content_digest = {
+        let mut hasher = Sha256::new();
+        hasher.update(content);
+        hasher.finalize()
+    };
 
-    let digest = hasher.finalize();
+    s.push_str(&format!(
+        ":sha256:{:x}:{}:{}",
+        content_digest, STORE_DIR, name
+    ));
 
-    build_store_path(false, &digest, name)
+    build_store_path(false, &s, name)
 }
 
 impl Derivation {
@@ -157,12 +165,10 @@ impl Derivation {
     ///   - Encode it with nixbase32
     ///   - Use it (and the name) to construct a [StorePath].
     pub fn calculate_derivation_path(&self, name: &str) -> Result<StorePath, DerivationError> {
-        let mut hasher = Sha256::new();
+        let mut s = String::from("text:");
 
         // collect the list of paths from input_sources and input_derivations
         // into a (sorted, guaranteed by BTreeSet) list, and join them by :
-        hasher.update(write::TEXT_COLON);
-
         let concat_inputs: BTreeSet<String> = {
             let mut inputs = self.input_sources.clone();
             let input_derivation_keys: Vec<String> =
@@ -172,29 +178,24 @@ impl Derivation {
         };
 
         for input in concat_inputs {
-            hasher.update(input);
-            hasher.update(write::COLON);
+            s.push_str(&input);
+            s.push(':');
         }
 
         // calculate the sha256 hash of the ATerm representation, and represent
         // it as a hex-encoded string (prefixed with sha256:).
-        hasher.update(write::SHA256_COLON);
-
-        let digest = {
+        let aterm_digest = {
             let mut derivation_hasher = Sha256::new();
             derivation_hasher.update(self.to_string());
             derivation_hasher.finalize()
         };
 
-        hasher.update(format!("{:x}", digest));
-        hasher.update(write::COLON);
-        hasher.update(STORE_DIR);
-        hasher.update(write::COLON);
-
-        hasher.update(name);
-        hasher.update(write::DOT_FILE_EXT);
+        s.push_str(&format!(
+            "sha256:{:x}:{}:{}.drv",
+            aterm_digest, STORE_DIR, name,
+        ));
 
-        build_store_path(true, &hasher.finalize(), name)
+        build_store_path(true, &s, name)
     }
 
     /// Calculate the drv replacement string for a given derivation.
@@ -213,12 +214,10 @@ impl Derivation {
         let mut hasher = Sha256::new();
         let digest = match self.get_fixed_output() {
             Some((fixed_output_path, fixed_output_hash)) => {
-                hasher.update("fixed:out:");
-                hasher.update(&fixed_output_hash.algo);
-                hasher.update(":");
-                hasher.update(&fixed_output_hash.digest);
-                hasher.update(":");
-                hasher.update(fixed_output_path);
+                hasher.update(format!(
+                    "fixed:out:{}:{}:{}",
+                    &fixed_output_hash.algo, &fixed_output_hash.digest, fixed_output_path,
+                ));
                 hasher.finalize()
             }
             None => {
@@ -274,8 +273,6 @@ impl Derivation {
         name: &str,
         drv_replacement_str: &str,
     ) -> Result<(), DerivationError> {
-        let mut hasher = Sha256::new();
-
         // Check if the Derivation is fixed output, because they cause
         // different fingerprints to be hashed.
         match self.get_fixed_output() {
@@ -287,14 +284,6 @@ impl Derivation {
                     // footgun prevention mechanism.
                     assert!(output.path.is_empty());
 
-                    hasher.update("output:");
-                    hasher.update(output_name);
-                    hasher.update(":sha256:");
-                    hasher.update(drv_replacement_str);
-                    hasher.update(":");
-                    hasher.update(STORE_DIR);
-                    hasher.update(":");
-
                     // calculate the output_name_path, which is the part of the NixPath after the digest.
                     let mut output_path_name = name.to_string();
                     if output_name != "out" {
@@ -302,12 +291,13 @@ impl Derivation {
                         output_path_name.push_str(output_name);
                     }
 
-                    hasher.update(output_path_name.as_str());
-
-                    let digest = hasher.finalize_reset();
+                    let s = &format!(
+                        "output:{}:sha256:{}:{}:{}",
+                        output_name, drv_replacement_str, STORE_DIR, output_path_name,
+                    );
 
                     let abs_store_path =
-                        build_store_path(false, &digest, &output_path_name)?.to_absolute_path();
+                        build_store_path(false, s, &output_path_name)?.to_absolute_path();
 
                     output.path = abs_store_path.clone();
                     self.environment
@@ -320,29 +310,27 @@ impl Derivation {
                 // footgun prevention mechanism.
                 assert!(fixed_output_path.is_empty());
 
-                let digest = {
+                let s = {
+                    let mut s = String::new();
                     // Fixed-output derivation.
                     // There's two different hashing strategies in place, depending on the value of hash.algo.
                     // This code is _weird_ but it is what Nix is doing. See:
                     // https://github.com/NixOS/nix/blob/1385b2007804c8a0370f2a6555045a00e34b07c7/src/libstore/store-api.cc#L178-L196
                     if fixed_output_hash.algo == "r:sha256" {
-                        hasher.update("source:");
-                        hasher.update("sha256");
-                        hasher.update(":");
-                        hasher.update(fixed_output_hash.digest.clone()); // nixbase32
+                        s.push_str(&format!(
+                            "source:sha256:{}",
+                            fixed_output_hash.digest, // nixbase32
+                        ));
                     } else {
-                        hasher.update("output:out:sha256:");
+                        s.push_str("output:out:sha256:");
                         // This is drv_replacement for FOD, with an empty fixed_output_path.
-                        hasher.update(drv_replacement_str);
+                        s.push_str(drv_replacement_str);
                     }
-                    hasher.update(":");
-                    hasher.update(STORE_DIR);
-                    hasher.update(":");
-                    hasher.update(name);
-                    hasher.finalize()
+                    s.push_str(&format!(":{}:{}", STORE_DIR, name));
+                    s
                 };
 
-                let abs_store_path = build_store_path(false, &digest, name)?.to_absolute_path();
+                let abs_store_path = build_store_path(false, &s, name)?.to_absolute_path();
 
                 self.outputs.insert(
                     "out".to_string(),
diff --git a/tvix/derivation/src/write.rs b/tvix/derivation/src/write.rs
index 8874b5d35a..0f53281567 100644
--- a/tvix/derivation/src/write.rs
+++ b/tvix/derivation/src/write.rs
@@ -16,9 +16,6 @@ pub const BRACKET_CLOSE: char = ']';
 pub const COMMA: char = ',';
 pub const QUOTE: char = '"';
 
-pub const COLON: &str = ":";
-pub const TEXT_COLON: &str = "text:";
-pub const SHA256_COLON: &str = "sha256:";
 pub const DOT_FILE_EXT: &str = ".drv";
 
 fn write_array_elements(