From 971080c9120725c0bec21ea2e5e14847b0e996e6 Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Thu, 30 Mar 2023 13:27:23 +0200 Subject: refactor(tvix/nix-compat): add text_hash_string function Use it to calculate the text_hash_string, which is then used in the calculate_derivation_path and path_with_references functions. Relates to b/263. Change-Id: I7478825e2a23a11224212fea5e3fd06daa97d5e5 Reviewed-on: https://cl.tvl.fyi/c/depot/+/8364 Autosubmit: flokli Reviewed-by: tazjin Tested-by: BuildkiteCI --- tvix/nix-compat/src/derivation/mod.rs | 46 ++++++++------------------------- tvix/nix-compat/src/derivation/utils.rs | 29 +++------------------ tvix/nix-compat/src/lib.rs | 1 + tvix/nix-compat/src/texthash.rs | 42 ++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 60 deletions(-) create mode 100644 tvix/nix-compat/src/texthash.rs diff --git a/tvix/nix-compat/src/derivation/mod.rs b/tvix/nix-compat/src/derivation/mod.rs index 281fe3a3c8..53bdccef6d 100644 --- a/tvix/nix-compat/src/derivation/mod.rs +++ b/tvix/nix-compat/src/derivation/mod.rs @@ -1,6 +1,7 @@ use crate::{ nixhash::HashAlgo, store_path::{self, StorePath}, + texthash::text_hash_string, }; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -78,26 +79,17 @@ impl Derivation { /// Returns the drv path of a Derivation struct. /// - /// The drv path is calculated like this: - /// - Write the fingerprint of the Derivation to the sha256 hash function. - /// This is: `text:`, - /// all d.InputDerivations and d.InputSources (sorted, separated by a `:`), - /// a `:`, - /// the nix string representation of the sha256 sum of the ATerm representation - /// a `:`, - /// the storeDir, followed by a `:`, - /// the name of a derivation, - /// a `.drv`. - /// - Write the .drv A-Term contents to a hash function - /// - Take the digest, run hash.CompressHash(digest, 20) on it. - /// - Encode it with nixbase32 - /// - Use it (and the name) to construct a [StorePath]. + /// The drv path is calculated by calculating the [text_hash_string], using + /// the `name` with a `.drv` suffix as name, all d.InputDerivations and d.InputSources as references, + /// and the ATerm representation of the Derivation as contents. + /// The text_hash_string is then passed to the build_store_path function. pub fn calculate_derivation_path(&self, name: &str) -> Result { - let mut s = String::from("text:"); + // append .drv to the name + let name_with_suffix = &format!("{}.drv", name); // collect the list of paths from input_sources and input_derivations - // into a (sorted, guaranteed by BTreeSet) list, and join them by : - let concat_inputs: BTreeSet = { + // into a (sorted, guaranteed by BTreeSet) list of references + let references: BTreeSet = { let mut inputs = self.input_sources.clone(); let input_derivation_keys: Vec = self.input_derivations.keys().cloned().collect(); @@ -105,25 +97,9 @@ impl Derivation { inputs }; - for input in concat_inputs { - 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:). - let aterm_digest = Sha256::new_with_prefix(self.to_aterm_string()) - .finalize() - .to_vec(); - - s.push_str(&format!( - "{}:{}:{}.drv", - NixHash::new(HashAlgo::Sha256, aterm_digest).to_nix_hash_string(), - store_path::STORE_DIR, - name, - )); + let text_hash_str = &text_hash_string(name_with_suffix, self.to_aterm_string(), references); - utils::build_store_path(true, &s, name) + utils::build_store_path(true, text_hash_str, name) } /// Returns the FOD digest, if the derivation is fixed-output, or None if diff --git a/tvix/nix-compat/src/derivation/utils.rs b/tvix/nix-compat/src/derivation/utils.rs index cbb5d02a21..68ab3e23c9 100644 --- a/tvix/nix-compat/src/derivation/utils.rs +++ b/tvix/nix-compat/src/derivation/utils.rs @@ -1,7 +1,7 @@ use crate::derivation::DerivationError; use crate::nixbase32; -use crate::nixhash::NixHash; -use crate::store_path::{self, StorePath}; +use crate::store_path::StorePath; +use crate::texthash::text_hash_string; use sha2::{Digest, Sha256}; /// compress_hash takes an arbitrarily long sequence of bytes (usually @@ -55,27 +55,6 @@ pub fn path_with_references, I: IntoIterator, C: AsRef<[ content: C, references: I, ) -> Result { - let mut s = String::from("text"); - - for reference in references { - s.push(':'); - s.push_str(reference.as_ref()); - } - - let content_digest = { - let mut hasher = Sha256::new(); - hasher.update(content); - hasher.finalize() - }; - - let h = NixHash::new(crate::nixhash::HashAlgo::Sha256, content_digest.to_vec()); - - s.push_str(&format!( - ":{}:{}:{}", - h.to_nix_hash_string(), - store_path::STORE_DIR, - name - )); - - build_store_path(false, &s, name) + let text_hash_str = text_hash_string(name, content, references); + build_store_path(false, &text_hash_str, name) } diff --git a/tvix/nix-compat/src/lib.rs b/tvix/nix-compat/src/lib.rs index 52b3ea65b9..ec9ddd1ffa 100644 --- a/tvix/nix-compat/src/lib.rs +++ b/tvix/nix-compat/src/lib.rs @@ -7,6 +7,7 @@ pub mod nixhash; mod nixhash_algos; mod nixhash_with_mode; pub mod store_path; +mod texthash; /// Nix placeholders (i.e. values returned by `builtins.placeholder`) /// are used to populate outputs with paths that must be diff --git a/tvix/nix-compat/src/texthash.rs b/tvix/nix-compat/src/texthash.rs new file mode 100644 index 0000000000..959a4dc3dc --- /dev/null +++ b/tvix/nix-compat/src/texthash.rs @@ -0,0 +1,42 @@ +use sha2::{Digest, Sha256}; + +use crate::{nixhash::NixHash, store_path}; + +/// This contains the Nix logic to create "text hash strings", which are used +/// in `builtins.toFile`, as well as in Derivation Path calculation. +/// +/// A text hash is calculated by concatenating the following fields, separated by a `:`: +/// +/// - text +/// - references, individually joined by `:` +/// - the nix_hash_string representation of the sha256 digest of some contents +/// - the value of `storeDir` +/// - the name +pub fn text_hash_string, I: IntoIterator, C: AsRef<[u8]>>( + name: &str, + content: C, + references: I, +) -> String { + let mut s = String::from("text:"); + + for reference in references { + s.push_str(reference.as_ref()); + s.push(':'); + } + + // the nix_hash_string representation of the sha256 digest of some contents + s.push_str( + &{ + let content_digest = { + let hasher = Sha256::new_with_prefix(content); + hasher.finalize() + }; + NixHash::new(crate::nixhash::HashAlgo::Sha256, content_digest.to_vec()) + } + .to_nix_hash_string(), + ); + + s.push_str(&format!(":{}:{}", store_path::STORE_DIR, name)); + + s +} -- cgit 1.4.1