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 <flokli@flokli.de>
Reviewed-by: tazjin <tazjin@tvl.su>
Tested-by: BuildkiteCI
This commit is contained in:
Florian Klink 2023-03-30 13:27:23 +02:00 committed by clbot
parent 14c5781389
commit 971080c912
4 changed files with 58 additions and 60 deletions

View file

@ -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<StorePath, DerivationError> {
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<String> = {
// into a (sorted, guaranteed by BTreeSet) list of references
let references: BTreeSet<String> = {
let mut inputs = self.input_sources.clone();
let input_derivation_keys: Vec<String> =
self.input_derivations.keys().cloned().collect();
@ -105,25 +97,9 @@ impl Derivation {
inputs
};
for input in concat_inputs {
s.push_str(&input);
s.push(':');
}
let text_hash_str = &text_hash_string(name_with_suffix, self.to_aterm_string(), references);
// 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,
));
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

View file

@ -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<S: AsRef<str>, I: IntoIterator<Item = S>, C: AsRef<[
content: C,
references: I,
) -> Result<StorePath, DerivationError> {
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)
}

View file

@ -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

View file

@ -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<S: AsRef<str>, I: IntoIterator<Item = S>, 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
}