haskell-nix/hnix-store

Please, put Truncated into a separate type

Anton-Latukha opened this issue · 5 comments

A subtask of #156 to meet #142

#93 (comment) mentioned that truncation should be split from hashing types. The more I work on it - the more I am aware it is a right idea.

Inclusion of the truncation as general hash type in the first place disconnected the project from hashing libraries while also depending on their interface & as a result of it imposed requirement to roll own crypto (because of creation of data HashAlgorithm overload).

And in the end the Truncation with xor'ing the tail to the beginning is not a widespread non-standard (seems may be even questionable) practice, all current search shows it is not any hashing standard (does not have a name) but merely a Nix-specific decision, so it can not be recommended generally.

Moreover, type system leverages type application of data kinds to allow any truncation length type, while in reality, truncation happens only for 20 char case. (it also was discussed, but can not find a link to it).

-- | The hash algorithm used for store path hashes.
type StorePathHashAlgo = 'Truncated 20 'SHA256

In short - we probably do not need the Truncated at all. It seems that StorePathHashAlgo just needs to be constructed in this way, it just has a specific type & builder - that is all.

At the very least to put Truncated constructor into a separate type seems highly beneficial, simplifies the code greatly, since code starts to fully align with hashing libraries & we no longer need to roll custom type system.


This would introduce a breakage change in the code.

layus commented

I totally agree. 👍 from me :-)

It is not that easy to split them as it looks. But I am already at the end of it, hacking the tests to preserve them.

Closed by #157:

  • Breaking

    • (link) System.Nix.Hash: Base encoding/decoding function for hashes changed (due to changes in type system & separation of specially truncated Nix Store hasing):
      • encode(InBase -> DigestWith)
      • decode(Base -> DigestWith)
    • (link) System.Nix.StorePath:
      • rm type StorePathHashAlgo = 'Truncated 20 'SHA256 in favour of StorePathHashPart & mkStorePathHashPart.
  • Additional

    • (link) System.Nix.StorePath:
      • added newtype StorePathHashPart = StorePathHashPart ByteString.
        • added builder mkStorePathHashPart :: ByteString -> StorePathHashPart
    • (link) System.Nix.Hash:
      • Nix store (which are specially truncated) hashes are now handled separately from other hashes:
        • add mkStorePathHash - a function to create a content into Nix storepath-style hash:
          mkStorePathHash :: HashAlgorithm a => ByteString -> ByteString
          but recommend to at once use mkStorePathHashPart.

It looks as a mesh, since it is split into Breaking & Additional changes.

In short, truncation moved to the Internal.Truncation, instead of polymorphic truncation there is a StorePathHashPart with the builder for it mkStorePathHashPart :: ByteString -> StorePathHashPart that takes any content and gives out Nix store path hash for it. Due to Nix using only concrete types & parameters for store paths, the polymorphism on the hash algorithms & of the length - got hidden away inside of the implementation, if it ever would be needed it is there in the:

mkStorePathHash :: forall a . C.HashAlgorithm a => BS.ByteString -> BS.ByteString
mkStorePathHash bs =
truncateInNixWay 20 $ convert $ C.hash @BS.ByteString @a bs

As we see - now there is also a possible polymorphism on the incoming data carrier type, which is also hidden away, because so far HNix Store Core chosen the ByteString.

Working on C++ Nix, I am not sure truncated needs to exist at all; maybe it's just needed when doing name + hash -> store path.

Working on C++ Nix, I am not sure truncated needs to exist at all; maybe it's just needed when doing name + hash -> store path.

That is what it is. It is for StorePath type & that is why I put truncation into the internal module, there is no outs of it in API except for StorePath, StorePathHash & mkStorePathHash.