From 6f1626a0ef874cb5695ee736e4b23de0d7e32f48 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sun, 18 Nov 2018 12:05:44 -0500 Subject: [PATCH 01/13] Reflect type level hash algo to value --- hnix-store-core/src/System/Nix/Hash.hs | 4 +- .../src/System/Nix/Internal/Hash.hs | 27 ++++++++++-- hnix-store-core/src/System/Nix/Path.hs | 2 +- hnix-store-remote/app/Main.hs | 14 +++--- .../src/System/Nix/Store/Remote.hs | 43 ++++++++++++++++++- 5 files changed, 76 insertions(+), 14 deletions(-) diff --git a/hnix-store-core/src/System/Nix/Hash.hs b/hnix-store-core/src/System/Nix/Hash.hs index b1fece2..0f8d1ca 100644 --- a/hnix-store-core/src/System/Nix/Hash.hs +++ b/hnix-store-core/src/System/Nix/Hash.hs @@ -13,7 +13,9 @@ Maintainer : Shea Levy ; Greg Hale module System.Nix.Hash ( HNix.Digest - , HNix.HashAlgorithm(..) + , HNix.HashAlgorithm + , HNix.HashAlgorithm'(..) + , HNix.AlgoVal(..) , HNix.HasDigest(..) , HNix.hash , HNix.hashLazy diff --git a/hnix-store-core/src/System/Nix/Internal/Hash.hs b/hnix-store-core/src/System/Nix/Internal/Hash.hs index af80c3b..05cb363 100644 --- a/hnix-store-core/src/System/Nix/Internal/Hash.hs +++ b/hnix-store-core/src/System/Nix/Internal/Hash.hs @@ -11,6 +11,7 @@ Maintainer : Greg Hale {-# LANGUAGE KindSignatures #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeInType #-} module System.Nix.Internal.Hash where @@ -23,6 +24,7 @@ import Data.Bits (xor) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL import qualified Data.Hashable as DataHashable +import Data.Kind (Type) import Data.List (foldl') import Data.Proxy (Proxy(Proxy)) import qualified Data.Text as T @@ -34,12 +36,31 @@ import GHC.TypeLits -- | A tag for different hashing algorithms -- Also used as a type-level tag for hash digests -- (e.g. @Digest SHA256@ is the type for a sha256 hash) -data HashAlgorithm +-- +-- When used at the type level, `n` is `Nat` +data HashAlgorithm' n = MD5 | SHA1 | SHA256 - | Truncated Nat HashAlgorithm + | Truncated n (HashAlgorithm' n) + deriving (Eq, Show) +type HashAlgorithm = HashAlgorithm' Nat + +class AlgoVal (a :: HashAlgorithm) where + algoVal :: HashAlgorithm' Integer + +instance AlgoVal MD5 where + algoVal = MD5 + +instance AlgoVal SHA1 where + algoVal = SHA1 + +instance AlgoVal SHA256 where + algoVal = SHA256 + +instance forall a n.(AlgoVal a, KnownNat n) => AlgoVal (Truncated n a) where + algoVal = Truncated (natVal (Proxy @n)) (algoVal @a) -- | Types with kind @HashAlgorithm@ may be a @HasDigest@ instance -- if they are able to hash bytestrings via the init/update/finalize @@ -49,7 +70,7 @@ data HashAlgorithm -- monomorphic hashing libraries, such as `cryptohash-sha256`. class HasDigest (a :: HashAlgorithm) where - type AlgoCtx a :: * + type AlgoCtx a :: Type initialize :: AlgoCtx a update :: AlgoCtx a -> BS.ByteString -> AlgoCtx a diff --git a/hnix-store-core/src/System/Nix/Path.hs b/hnix-store-core/src/System/Nix/Path.hs index 18d805b..d92aa9e 100644 --- a/hnix-store-core/src/System/Nix/Path.hs +++ b/hnix-store-core/src/System/Nix/Path.hs @@ -18,7 +18,7 @@ module System.Nix.Path ) where import System.Nix.Hash (Digest(..), - HashAlgorithm(Truncated, SHA256)) + HashAlgorithm'(Truncated, SHA256)) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BSC import Data.Hashable (Hashable (..), hashPtrWithSalt) diff --git a/hnix-store-remote/app/Main.hs b/hnix-store-remote/app/Main.hs index 70d1be0..1a8dd7f 100644 --- a/hnix-store-remote/app/Main.hs +++ b/hnix-store-remote/app/Main.hs @@ -1,13 +1,13 @@ {-# LANGUAGE OverloadedStrings #-} import qualified Data.ByteString.Lazy as LBS -import qualified Data.HashSet as HS -import qualified System.Nix.GC as GC -import System.Nix.Store.Remote -import System.Nix.Store.Remote.Util -import Data.Maybe -import Control.Monad.Reader +import qualified Data.HashSet as HS +import Data.Maybe +import Control.Monad.Reader +import Text.Pretty.Simple -import Text.Pretty.Simple +import qualified System.Nix.GC as GC +import System.Nix.Store.Remote +import System.Nix.Store.Remote.Util noSuchPath = fromJust $ mkPath "blah" diff --git a/hnix-store-remote/src/System/Nix/Store/Remote.hs b/hnix-store-remote/src/System/Nix/Store/Remote.hs index f19fefc..b79b739 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote.hs @@ -1,4 +1,10 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE KindSignatures #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} module System.Nix.Store.Remote ( runStore , isValidPathUncached @@ -32,6 +38,7 @@ module System.Nix.Store.Remote ( import Data.Maybe import qualified Data.ByteString.Lazy as LBS import qualified Data.Map.Strict as M +import Data.Proxy (Proxy(Proxy)) import Control.Monad @@ -40,6 +47,7 @@ import qualified System.Nix.Derivation as Drv import qualified System.Nix.GC as GC import System.Nix.Hash (Digest, HashAlgorithm) import System.Nix.Path +import System.Nix.Hash import System.Nix.Util import System.Nix.Store.Remote.Types @@ -159,9 +167,40 @@ type Source = () -- abstract binary source addToStoreNar :: ValidPathInfo -> Source -> RepairFlag -> CheckSigsFlag -> MonadStore () addToStoreNar = undefined -- XXX + +-- class BaseHashAlgorithm (a :: HashAlgorithm) where +-- baseHashAlgorithm :: Bool + +-- instance BaseHashAlgorithm MD5 where +-- baseHashAlgorithm = MD5 + +-- instance BaseHashAlgorithm SHA1 where +-- baseHashAlgorithm = SHA1 + +-- instance BaseHashAlgorithm SHA256 where +-- baseHashAlgorithm = SHA256 + +-- instance forall n a.BaseHashAlgorithm a => BaseHashAlgorithm (Truncated n a) where +-- baseHashAlgorithm = baseHashAlgorithm @a + type PathFilter = Path -> Bool -addToStore :: LBS.ByteString -> Path -> Bool -> HashAlgorithm -> PathFilter -> RepairFlag -> MonadStore Path -addToStore name pth recursive hashAlgo pfilter repair = undefined -- XXX +addToStore + :: forall a. AlgoVal a + => LBS.ByteString + -> Path + -> Bool + -> Proxy a + -> PathFilter + -> RepairFlag + -> MonadStore Path +addToStore name pth recursive algoProxy pfilter repair = do + runOpArgs AddToStore $ do + putByteStringLen name + putByteStringLen $ if algoVal @a == SHA256 && recursive then 0 else 1 + putByteStringLen $ if recursive then 0 else 1 + putByteStringLen name + fmap (fromMaybe "TODO: Error") sockGetPath + addTextToStore :: LBS.ByteString -> LBS.ByteString -> PathSet -> RepairFlag -> MonadStore (Maybe Path) addTextToStore name text references' repair = do From 7bd82c9ce900344b4070987cb7ea184e8a8c6162 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sun, 18 Nov 2018 14:27:23 -0500 Subject: [PATCH 02/13] WIP addToStore protocol implementation --- .../src/System/Nix/Internal/Hash.hs | 32 +++++++------ .../src/System/Nix/Store/Remote.hs | 48 ++++++++++++++----- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/hnix-store-core/src/System/Nix/Internal/Hash.hs b/hnix-store-core/src/System/Nix/Internal/Hash.hs index 05cb363..ecee504 100644 --- a/hnix-store-core/src/System/Nix/Internal/Hash.hs +++ b/hnix-store-core/src/System/Nix/Internal/Hash.hs @@ -47,21 +47,6 @@ data HashAlgorithm' n type HashAlgorithm = HashAlgorithm' Nat -class AlgoVal (a :: HashAlgorithm) where - algoVal :: HashAlgorithm' Integer - -instance AlgoVal MD5 where - algoVal = MD5 - -instance AlgoVal SHA1 where - algoVal = SHA1 - -instance AlgoVal SHA256 where - algoVal = SHA256 - -instance forall a n.(AlgoVal a, KnownNat n) => AlgoVal (Truncated n a) where - algoVal = Truncated (natVal (Proxy @n)) (algoVal @a) - -- | Types with kind @HashAlgorithm@ may be a @HasDigest@ instance -- if they are able to hash bytestrings via the init/update/finalize -- API of cryptonite @@ -185,3 +170,20 @@ truncateDigest (Digest c) = Digest $ BS.pack $ map truncOutputByte [0.. n-1] digits32 :: V.Vector Char digits32 = V.fromList "0123456789abcdfghijklmnpqrsvwxyz" + + +-- | Convert type-level @HashAlgorithm@ into the value level +class AlgoVal (a :: HashAlgorithm) where + algoVal :: HashAlgorithm' Integer + +instance AlgoVal MD5 where + algoVal = MD5 + +instance AlgoVal SHA1 where + algoVal = SHA1 + +instance AlgoVal SHA256 where + algoVal = SHA256 + +instance forall a n.(AlgoVal a, KnownNat n) => AlgoVal (Truncated n a) where + algoVal = Truncated (natVal (Proxy @n)) (algoVal @a) diff --git a/hnix-store-remote/src/System/Nix/Store/Remote.hs b/hnix-store-remote/src/System/Nix/Store/Remote.hs index b79b739..4f13aa5 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote.hs @@ -35,19 +35,24 @@ module System.Nix.Store.Remote ( , queryMissing ) where +import Control.Monad +import Control.Monad.IO.Class (liftIO) +import qualified Data.Binary as B +import qualified Data.Binary.Put as B import Data.Maybe import qualified Data.ByteString.Lazy as LBS import qualified Data.Map.Strict as M import Data.Proxy (Proxy(Proxy)) +import qualified Data.Text.Lazy as T +import qualified Data.Text.Lazy.Encoding as T -import Control.Monad - -import qualified System.Nix.Build as Build -import qualified System.Nix.Derivation as Drv -import qualified System.Nix.GC as GC -import System.Nix.Hash (Digest, HashAlgorithm) +import qualified System.Nix.Build as Build +import qualified System.Nix.Derivation as Drv +import qualified System.Nix.GC as GC +import System.Nix.Hash (Digest, HashAlgorithm) import System.Nix.Path import System.Nix.Hash +import System.Nix.Nar (localPackNar, putNar) import System.Nix.Util import System.Nix.Store.Remote.Types @@ -183,23 +188,42 @@ addToStoreNar = undefined -- XXX -- instance forall n a.BaseHashAlgorithm a => BaseHashAlgorithm (Truncated n a) where -- baseHashAlgorithm = baseHashAlgorithm @a +printHashType :: HashAlgorithm' Integer -> T.Text +printHashType MD5 = "MD5" +printHashType SHA1 = "SHA1" +printHashType SHA256 = "SHA256" +printHashType (Truncated _ a) = printHashType a + type PathFilter = Path -> Bool addToStore - :: forall a. AlgoVal a + :: forall a. (HasDigest a, AlgoVal a) => LBS.ByteString - -> Path + -> FilePath -> Bool -> Proxy a -> PathFilter -> RepairFlag -> MonadStore Path addToStore name pth recursive algoProxy pfilter repair = do + -- Get length first + len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar undefined pth + -- Fetch full NAR bytestring separately. We are trying to + -- avoid forcing the full string in memory + bs :: LBS.ByteString <- liftIO $ B.runPut . putNar <$> localPackNar undefined pth runOpArgs AddToStore $ do putByteStringLen name - putByteStringLen $ if algoVal @a == SHA256 && recursive then 0 else 1 - putByteStringLen $ if recursive then 0 else 1 - putByteStringLen name - fmap (fromMaybe "TODO: Error") sockGetPath + -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? + putByteStringLen $ if algoVal @a `elem` [SHA256, Truncated 20 SHA256] + && recursive + then "0" + else "1" + -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? + putByteStringLen $ if recursive then "0" else "1" + putByteStringLen (T.encodeUtf8 . printHashType $ algoVal @a) + + putInt len + B.putLazyByteString bs + fmap (fromMaybe $ error "TODO: Error") sockGetPath addTextToStore :: LBS.ByteString -> LBS.ByteString -> PathSet -> RepairFlag -> MonadStore (Maybe Path) From 34f2ad0287c71bea785b3914746704a3859c6957 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sun, 18 Nov 2018 14:29:10 -0500 Subject: [PATCH 03/13] nareffetsio for addToStore in hnix-store-remote --- hnix-store-remote/src/System/Nix/Store/Remote.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hnix-store-remote/src/System/Nix/Store/Remote.hs b/hnix-store-remote/src/System/Nix/Store/Remote.hs index 4f13aa5..8f37e9b 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote.hs @@ -52,7 +52,7 @@ import qualified System.Nix.GC as GC import System.Nix.Hash (Digest, HashAlgorithm) import System.Nix.Path import System.Nix.Hash -import System.Nix.Nar (localPackNar, putNar) +import System.Nix.Nar (localPackNar, putNar, narEffectsIO) import System.Nix.Util import System.Nix.Store.Remote.Types @@ -206,10 +206,10 @@ addToStore -> MonadStore Path addToStore name pth recursive algoProxy pfilter repair = do -- Get length first - len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar undefined pth + len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar narEffectsIO pth -- Fetch full NAR bytestring separately. We are trying to -- avoid forcing the full string in memory - bs :: LBS.ByteString <- liftIO $ B.runPut . putNar <$> localPackNar undefined pth + bs :: LBS.ByteString <- liftIO $ B.runPut . putNar <$> localPackNar narEffectsIO pth runOpArgs AddToStore $ do putByteStringLen name -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? From 7bd991a52ed53513059a8cbb39f0b3dab2c56b3f Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Fri, 28 Dec 2018 11:00:31 -0500 Subject: [PATCH 04/13] add nix expressions for building hnix-store-* --- hnix-store-core/hnix-store-core.cabal | 2 +- hnix-store-core/shell.nix | 1 + hnix-store-remote/hnix-store-remote.cabal | 2 +- hnix-store-remote/shell.nix | 1 + release.nix | 31 +++++++++++++++++++++++ 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 hnix-store-core/shell.nix create mode 100644 hnix-store-remote/shell.nix create mode 100644 release.nix diff --git a/hnix-store-core/hnix-store-core.cabal b/hnix-store-core/hnix-store-core.cabal index 8a3294e..b06ab03 100644 --- a/hnix-store-core/hnix-store-core.cabal +++ b/hnix-store-core/hnix-store-core.cabal @@ -26,7 +26,7 @@ library , System.Nix.Path , System.Nix.Store , System.Nix.Util - build-depends: base >=4.10 && <4.11 + build-depends: base >=4.10 && <4.12 , bytestring , binary , bytestring diff --git a/hnix-store-core/shell.nix b/hnix-store-core/shell.nix new file mode 100644 index 0000000..ca984d6 --- /dev/null +++ b/hnix-store-core/shell.nix @@ -0,0 +1 @@ +(import ../release.nix).hnix-store-core.env diff --git a/hnix-store-remote/hnix-store-remote.cabal b/hnix-store-remote/hnix-store-remote.cabal index eb7550f..64be764 100644 --- a/hnix-store-remote/hnix-store-remote.cabal +++ b/hnix-store-remote/hnix-store-remote.cabal @@ -20,7 +20,7 @@ library , System.Nix.Store.Remote.Types , System.Nix.Store.Remote.Util - build-depends: base >=4.10 && <4.11 + build-depends: base >=4.10 && <4.12 , bytestring , binary , bytestring diff --git a/hnix-store-remote/shell.nix b/hnix-store-remote/shell.nix new file mode 100644 index 0000000..ce87af4 --- /dev/null +++ b/hnix-store-remote/shell.nix @@ -0,0 +1 @@ +(import ../release.nix).hnix-store-remote.env diff --git a/release.nix b/release.nix new file mode 100644 index 0000000..dd8e253 --- /dev/null +++ b/release.nix @@ -0,0 +1,31 @@ +let + + # TODO pin nixpkgs + pkgs0 = import ; + + hsOverrides = self: super: { + hnix-store-core = pkgs.haskellPackages.callCabal2nix "hnix-store-core" ./hnix-store-core {}; + hnix-store-remote = pkgs.haskellPackages.callCabal2nix "hnix-store-remote" ./hnix-store-remote {}; + }; + + pkgs = pkgs0 { + overlays = [ (self: super: { + haskellPackages = super.haskellPackages.override { + overrides = hsOverrides; + }; + }) ]; + }; + + # pkgs = pkgs0.override { + # overrides = self: super: { + # haskellPackages = self.haskellPackages.override { + # overrides = hsOverrides; + # }; + # }; + # }; + +in +{ + hnix-store-core = pkgs.haskellPackages.hnix-store-core; + hnix-store-remote = pkgs.haskellPackages.hnix-store-remote; +} From a7120f60cc83f8933d459603317aee3e3e68ac48 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sun, 18 Nov 2018 12:05:44 -0500 Subject: [PATCH 05/13] Reflect type level hash algo to value --- hnix-store-core/src/System/Nix/Hash.hs | 4 +- .../src/System/Nix/Internal/Hash.hs | 27 ++++++++++-- hnix-store-core/src/System/Nix/Path.hs | 2 +- hnix-store-remote/app/Main.hs | 14 +++--- .../src/System/Nix/Store/Remote.hs | 43 ++++++++++++++++++- 5 files changed, 76 insertions(+), 14 deletions(-) diff --git a/hnix-store-core/src/System/Nix/Hash.hs b/hnix-store-core/src/System/Nix/Hash.hs index b1fece2..0f8d1ca 100644 --- a/hnix-store-core/src/System/Nix/Hash.hs +++ b/hnix-store-core/src/System/Nix/Hash.hs @@ -13,7 +13,9 @@ Maintainer : Shea Levy ; Greg Hale module System.Nix.Hash ( HNix.Digest - , HNix.HashAlgorithm(..) + , HNix.HashAlgorithm + , HNix.HashAlgorithm'(..) + , HNix.AlgoVal(..) , HNix.HasDigest(..) , HNix.hash , HNix.hashLazy diff --git a/hnix-store-core/src/System/Nix/Internal/Hash.hs b/hnix-store-core/src/System/Nix/Internal/Hash.hs index af80c3b..05cb363 100644 --- a/hnix-store-core/src/System/Nix/Internal/Hash.hs +++ b/hnix-store-core/src/System/Nix/Internal/Hash.hs @@ -11,6 +11,7 @@ Maintainer : Greg Hale {-# LANGUAGE KindSignatures #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeInType #-} module System.Nix.Internal.Hash where @@ -23,6 +24,7 @@ import Data.Bits (xor) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL import qualified Data.Hashable as DataHashable +import Data.Kind (Type) import Data.List (foldl') import Data.Proxy (Proxy(Proxy)) import qualified Data.Text as T @@ -34,12 +36,31 @@ import GHC.TypeLits -- | A tag for different hashing algorithms -- Also used as a type-level tag for hash digests -- (e.g. @Digest SHA256@ is the type for a sha256 hash) -data HashAlgorithm +-- +-- When used at the type level, `n` is `Nat` +data HashAlgorithm' n = MD5 | SHA1 | SHA256 - | Truncated Nat HashAlgorithm + | Truncated n (HashAlgorithm' n) + deriving (Eq, Show) +type HashAlgorithm = HashAlgorithm' Nat + +class AlgoVal (a :: HashAlgorithm) where + algoVal :: HashAlgorithm' Integer + +instance AlgoVal MD5 where + algoVal = MD5 + +instance AlgoVal SHA1 where + algoVal = SHA1 + +instance AlgoVal SHA256 where + algoVal = SHA256 + +instance forall a n.(AlgoVal a, KnownNat n) => AlgoVal (Truncated n a) where + algoVal = Truncated (natVal (Proxy @n)) (algoVal @a) -- | Types with kind @HashAlgorithm@ may be a @HasDigest@ instance -- if they are able to hash bytestrings via the init/update/finalize @@ -49,7 +70,7 @@ data HashAlgorithm -- monomorphic hashing libraries, such as `cryptohash-sha256`. class HasDigest (a :: HashAlgorithm) where - type AlgoCtx a :: * + type AlgoCtx a :: Type initialize :: AlgoCtx a update :: AlgoCtx a -> BS.ByteString -> AlgoCtx a diff --git a/hnix-store-core/src/System/Nix/Path.hs b/hnix-store-core/src/System/Nix/Path.hs index 18d805b..d92aa9e 100644 --- a/hnix-store-core/src/System/Nix/Path.hs +++ b/hnix-store-core/src/System/Nix/Path.hs @@ -18,7 +18,7 @@ module System.Nix.Path ) where import System.Nix.Hash (Digest(..), - HashAlgorithm(Truncated, SHA256)) + HashAlgorithm'(Truncated, SHA256)) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BSC import Data.Hashable (Hashable (..), hashPtrWithSalt) diff --git a/hnix-store-remote/app/Main.hs b/hnix-store-remote/app/Main.hs index 70d1be0..1a8dd7f 100644 --- a/hnix-store-remote/app/Main.hs +++ b/hnix-store-remote/app/Main.hs @@ -1,13 +1,13 @@ {-# LANGUAGE OverloadedStrings #-} import qualified Data.ByteString.Lazy as LBS -import qualified Data.HashSet as HS -import qualified System.Nix.GC as GC -import System.Nix.Store.Remote -import System.Nix.Store.Remote.Util -import Data.Maybe -import Control.Monad.Reader +import qualified Data.HashSet as HS +import Data.Maybe +import Control.Monad.Reader +import Text.Pretty.Simple -import Text.Pretty.Simple +import qualified System.Nix.GC as GC +import System.Nix.Store.Remote +import System.Nix.Store.Remote.Util noSuchPath = fromJust $ mkPath "blah" diff --git a/hnix-store-remote/src/System/Nix/Store/Remote.hs b/hnix-store-remote/src/System/Nix/Store/Remote.hs index f19fefc..b79b739 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote.hs @@ -1,4 +1,10 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE KindSignatures #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} module System.Nix.Store.Remote ( runStore , isValidPathUncached @@ -32,6 +38,7 @@ module System.Nix.Store.Remote ( import Data.Maybe import qualified Data.ByteString.Lazy as LBS import qualified Data.Map.Strict as M +import Data.Proxy (Proxy(Proxy)) import Control.Monad @@ -40,6 +47,7 @@ import qualified System.Nix.Derivation as Drv import qualified System.Nix.GC as GC import System.Nix.Hash (Digest, HashAlgorithm) import System.Nix.Path +import System.Nix.Hash import System.Nix.Util import System.Nix.Store.Remote.Types @@ -159,9 +167,40 @@ type Source = () -- abstract binary source addToStoreNar :: ValidPathInfo -> Source -> RepairFlag -> CheckSigsFlag -> MonadStore () addToStoreNar = undefined -- XXX + +-- class BaseHashAlgorithm (a :: HashAlgorithm) where +-- baseHashAlgorithm :: Bool + +-- instance BaseHashAlgorithm MD5 where +-- baseHashAlgorithm = MD5 + +-- instance BaseHashAlgorithm SHA1 where +-- baseHashAlgorithm = SHA1 + +-- instance BaseHashAlgorithm SHA256 where +-- baseHashAlgorithm = SHA256 + +-- instance forall n a.BaseHashAlgorithm a => BaseHashAlgorithm (Truncated n a) where +-- baseHashAlgorithm = baseHashAlgorithm @a + type PathFilter = Path -> Bool -addToStore :: LBS.ByteString -> Path -> Bool -> HashAlgorithm -> PathFilter -> RepairFlag -> MonadStore Path -addToStore name pth recursive hashAlgo pfilter repair = undefined -- XXX +addToStore + :: forall a. AlgoVal a + => LBS.ByteString + -> Path + -> Bool + -> Proxy a + -> PathFilter + -> RepairFlag + -> MonadStore Path +addToStore name pth recursive algoProxy pfilter repair = do + runOpArgs AddToStore $ do + putByteStringLen name + putByteStringLen $ if algoVal @a == SHA256 && recursive then 0 else 1 + putByteStringLen $ if recursive then 0 else 1 + putByteStringLen name + fmap (fromMaybe "TODO: Error") sockGetPath + addTextToStore :: LBS.ByteString -> LBS.ByteString -> PathSet -> RepairFlag -> MonadStore (Maybe Path) addTextToStore name text references' repair = do From 4c650ca750aca04b92b346563480b284020ffadd Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sun, 18 Nov 2018 14:27:23 -0500 Subject: [PATCH 06/13] WIP addToStore protocol implementation --- .../src/System/Nix/Internal/Hash.hs | 32 +++++++------ .../src/System/Nix/Store/Remote.hs | 48 ++++++++++++++----- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/hnix-store-core/src/System/Nix/Internal/Hash.hs b/hnix-store-core/src/System/Nix/Internal/Hash.hs index 05cb363..ecee504 100644 --- a/hnix-store-core/src/System/Nix/Internal/Hash.hs +++ b/hnix-store-core/src/System/Nix/Internal/Hash.hs @@ -47,21 +47,6 @@ data HashAlgorithm' n type HashAlgorithm = HashAlgorithm' Nat -class AlgoVal (a :: HashAlgorithm) where - algoVal :: HashAlgorithm' Integer - -instance AlgoVal MD5 where - algoVal = MD5 - -instance AlgoVal SHA1 where - algoVal = SHA1 - -instance AlgoVal SHA256 where - algoVal = SHA256 - -instance forall a n.(AlgoVal a, KnownNat n) => AlgoVal (Truncated n a) where - algoVal = Truncated (natVal (Proxy @n)) (algoVal @a) - -- | Types with kind @HashAlgorithm@ may be a @HasDigest@ instance -- if they are able to hash bytestrings via the init/update/finalize -- API of cryptonite @@ -185,3 +170,20 @@ truncateDigest (Digest c) = Digest $ BS.pack $ map truncOutputByte [0.. n-1] digits32 :: V.Vector Char digits32 = V.fromList "0123456789abcdfghijklmnpqrsvwxyz" + + +-- | Convert type-level @HashAlgorithm@ into the value level +class AlgoVal (a :: HashAlgorithm) where + algoVal :: HashAlgorithm' Integer + +instance AlgoVal MD5 where + algoVal = MD5 + +instance AlgoVal SHA1 where + algoVal = SHA1 + +instance AlgoVal SHA256 where + algoVal = SHA256 + +instance forall a n.(AlgoVal a, KnownNat n) => AlgoVal (Truncated n a) where + algoVal = Truncated (natVal (Proxy @n)) (algoVal @a) diff --git a/hnix-store-remote/src/System/Nix/Store/Remote.hs b/hnix-store-remote/src/System/Nix/Store/Remote.hs index b79b739..4f13aa5 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote.hs @@ -35,19 +35,24 @@ module System.Nix.Store.Remote ( , queryMissing ) where +import Control.Monad +import Control.Monad.IO.Class (liftIO) +import qualified Data.Binary as B +import qualified Data.Binary.Put as B import Data.Maybe import qualified Data.ByteString.Lazy as LBS import qualified Data.Map.Strict as M import Data.Proxy (Proxy(Proxy)) +import qualified Data.Text.Lazy as T +import qualified Data.Text.Lazy.Encoding as T -import Control.Monad - -import qualified System.Nix.Build as Build -import qualified System.Nix.Derivation as Drv -import qualified System.Nix.GC as GC -import System.Nix.Hash (Digest, HashAlgorithm) +import qualified System.Nix.Build as Build +import qualified System.Nix.Derivation as Drv +import qualified System.Nix.GC as GC +import System.Nix.Hash (Digest, HashAlgorithm) import System.Nix.Path import System.Nix.Hash +import System.Nix.Nar (localPackNar, putNar) import System.Nix.Util import System.Nix.Store.Remote.Types @@ -183,23 +188,42 @@ addToStoreNar = undefined -- XXX -- instance forall n a.BaseHashAlgorithm a => BaseHashAlgorithm (Truncated n a) where -- baseHashAlgorithm = baseHashAlgorithm @a +printHashType :: HashAlgorithm' Integer -> T.Text +printHashType MD5 = "MD5" +printHashType SHA1 = "SHA1" +printHashType SHA256 = "SHA256" +printHashType (Truncated _ a) = printHashType a + type PathFilter = Path -> Bool addToStore - :: forall a. AlgoVal a + :: forall a. (HasDigest a, AlgoVal a) => LBS.ByteString - -> Path + -> FilePath -> Bool -> Proxy a -> PathFilter -> RepairFlag -> MonadStore Path addToStore name pth recursive algoProxy pfilter repair = do + -- Get length first + len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar undefined pth + -- Fetch full NAR bytestring separately. We are trying to + -- avoid forcing the full string in memory + bs :: LBS.ByteString <- liftIO $ B.runPut . putNar <$> localPackNar undefined pth runOpArgs AddToStore $ do putByteStringLen name - putByteStringLen $ if algoVal @a == SHA256 && recursive then 0 else 1 - putByteStringLen $ if recursive then 0 else 1 - putByteStringLen name - fmap (fromMaybe "TODO: Error") sockGetPath + -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? + putByteStringLen $ if algoVal @a `elem` [SHA256, Truncated 20 SHA256] + && recursive + then "0" + else "1" + -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? + putByteStringLen $ if recursive then "0" else "1" + putByteStringLen (T.encodeUtf8 . printHashType $ algoVal @a) + + putInt len + B.putLazyByteString bs + fmap (fromMaybe $ error "TODO: Error") sockGetPath addTextToStore :: LBS.ByteString -> LBS.ByteString -> PathSet -> RepairFlag -> MonadStore (Maybe Path) From 759abd9f94d051db0681a11c96e04dd94023a77c Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sun, 18 Nov 2018 14:29:10 -0500 Subject: [PATCH 07/13] nareffetsio for addToStore in hnix-store-remote --- hnix-store-remote/src/System/Nix/Store/Remote.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hnix-store-remote/src/System/Nix/Store/Remote.hs b/hnix-store-remote/src/System/Nix/Store/Remote.hs index 4f13aa5..8f37e9b 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote.hs @@ -52,7 +52,7 @@ import qualified System.Nix.GC as GC import System.Nix.Hash (Digest, HashAlgorithm) import System.Nix.Path import System.Nix.Hash -import System.Nix.Nar (localPackNar, putNar) +import System.Nix.Nar (localPackNar, putNar, narEffectsIO) import System.Nix.Util import System.Nix.Store.Remote.Types @@ -206,10 +206,10 @@ addToStore -> MonadStore Path addToStore name pth recursive algoProxy pfilter repair = do -- Get length first - len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar undefined pth + len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar narEffectsIO pth -- Fetch full NAR bytestring separately. We are trying to -- avoid forcing the full string in memory - bs :: LBS.ByteString <- liftIO $ B.runPut . putNar <$> localPackNar undefined pth + bs :: LBS.ByteString <- liftIO $ B.runPut . putNar <$> localPackNar narEffectsIO pth runOpArgs AddToStore $ do putByteStringLen name -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? From 547165d22e6b101a6ceca584b47d450497596d57 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sat, 9 Mar 2019 11:32:23 -0500 Subject: [PATCH 08/13] Add useful garbage --- hnix-store-remote/app/Main.hs | 20 +++-- hnix-store-remote/hnix-store-remote.cabal | 1 + .../src/System/Nix/Store/Remote.hs | 85 +++++++++++++++++-- .../src/System/Nix/Store/Remote/Util.hs | 3 +- 4 files changed, 97 insertions(+), 12 deletions(-) diff --git a/hnix-store-remote/app/Main.hs b/hnix-store-remote/app/Main.hs index 1a8dd7f..d43ee4b 100644 --- a/hnix-store-remote/app/Main.hs +++ b/hnix-store-remote/app/Main.hs @@ -2,10 +2,12 @@ import qualified Data.ByteString.Lazy as LBS import qualified Data.HashSet as HS import Data.Maybe +import Data.Proxy import Control.Monad.Reader import Text.Pretty.Simple import qualified System.Nix.GC as GC +import System.Nix.Path (PathHashAlgo) import System.Nix.Store.Remote import System.Nix.Store.Remote.Util @@ -17,19 +19,27 @@ main = do verifyStore False False - (Just path) <- addTextToStore "hnix-store" "test" (HS.fromList []) False + (Just path) <- addTextToStore "hnix-store" "test" (HS.fromList []) False + + -- (Just path2) <- addTextToStore "hnix-store2" "test2" (HS.fromList []) False + path2 <- addToStore "hi-test-file" + "/home/greghale/code/hnix-store/hnix-store-remote/hi" + False (Proxy :: Proxy PathHashAlgo) (const True) False valid <- isValidPathUncached path - case valid of - True -> do + valid2 <- isValidPathUncached path2 + + case (valid, valid2) of + (True, True) -> do info <- queryPathInfoUncached path - return (path, info) + info2 <- queryPathInfoUncached path2 + return (path, info, path2, info2) _ -> error "shouldn't happen" pPrint x case x of (Left err, log) -> putStrLn err >> print log - (Right (path, pathinfo), log) -> do + (Right (path, pathinfo, path2, pathinfo2), log) -> do gcres <- runStore $ do collectGarbage $ GC.Options { GC.operation = GC.DeleteSpecific diff --git a/hnix-store-remote/hnix-store-remote.cabal b/hnix-store-remote/hnix-store-remote.cabal index 64be764..9e1e3d4 100644 --- a/hnix-store-remote/hnix-store-remote.cabal +++ b/hnix-store-remote/hnix-store-remote.cabal @@ -21,6 +21,7 @@ library , System.Nix.Store.Remote.Util build-depends: base >=4.10 && <4.12 + , base64-bytestring , bytestring , binary , bytestring diff --git a/hnix-store-remote/src/System/Nix/Store/Remote.hs b/hnix-store-remote/src/System/Nix/Store/Remote.hs index 8f37e9b..bea9101 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote.hs @@ -59,6 +59,9 @@ import System.Nix.Store.Remote.Types import System.Nix.Store.Remote.Protocol import System.Nix.Store.Remote.Util +-- tmp +import qualified Data.ByteString.Base64.Lazy as B64 + type RepairFlag = Bool type CheckFlag = Bool type CheckSigsFlag = Bool @@ -194,7 +197,50 @@ printHashType SHA1 = "SHA1" printHashType SHA256 = "SHA256" printHashType (Truncated _ a) = printHashType a + +-- ********************************************************** +-- ** This is the c++ code we are porting for `addToStore` ** +-- ********************************************************** +-- +-- Path RemoteStore::addToStore(const string & name, const Path & _srcPath, +-- bool recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair) +-- { +-- if (repair) throw Error("repairing is not supported when building through the Nix daemon"); + +-- auto conn(getConnection()); + +-- Path srcPath(absPath(_srcPath)); + +-- conn->to << wopAddToStore << name +-- << ((hashAlgo == htSHA256 && recursive) ? 0 : 1) /* backwards compatibility hack */ +-- << (recursive ? 1 : 0) +-- << printHashType(hashAlgo); + +-- try { +-- conn->to.written = 0; +-- conn->to.warn = true; +-- connections->incCapacity(); +-- { +-- Finally cleanup([&]() { connections->decCapacity(); }); +-- dumpPath(srcPath, conn->to, filter); +-- } +-- conn->to.warn = false; +-- conn.processStderr(); +-- } catch (SysError & e) { +-- /* Daemon closed while we were sending the path. Probably OOM +-- or I/O error. */ +-- if (e.errNo == EPIPE) +-- try { +-- conn.processStderr(); +-- } catch (EndOfFile & e) { } +-- throw; +-- } + +-- return readStorePath(*this, conn->from); +-- } + type PathFilter = Path -> Bool + addToStore :: forall a. (HasDigest a, AlgoVal a) => LBS.ByteString @@ -206,26 +252,53 @@ addToStore -> MonadStore Path addToStore name pth recursive algoProxy pfilter repair = do -- Get length first - len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar narEffectsIO pth + -- len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar narEffectsIO pth -- Fetch full NAR bytestring separately. We are trying to -- avoid forcing the full string in memory bs :: LBS.ByteString <- liftIO $ B.runPut . putNar <$> localPackNar narEffectsIO pth + liftIO $ print (LBS.length bs) + bs' <- liftIO $ putNar <$> localPackNar narEffectsIO pth + let bs'' = putByteStringLen "nix-archive-1" + let bs = sampleRegularBaseline + let len = LBS.length bs runOpArgs AddToStore $ do putByteStringLen name -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? putByteStringLen $ if algoVal @a `elem` [SHA256, Truncated 20 SHA256] && recursive - then "0" - else "1" + then (LBS.pack [0]) + else (LBS.pack [1]) + -- then "0" + -- else "1" -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? - putByteStringLen $ if recursive then "0" else "1" - putByteStringLen (T.encodeUtf8 . printHashType $ algoVal @a) + putByteStringLen $ if recursive + then (LBS.pack [1]) + else (LBS.pack [0]) + -- then "1" + -- else "0" - putInt len + -- putByteStringLen (T.encodeUtf8 . T.toLower . printHashType $ algoVal @a) + + -- putByteStringLen bs + -- putInt len B.putLazyByteString bs + -- bs'' + -- when (len `mod` 8 /= 0) $ + -- let pad x = forM_ (take x $ cycle [0]) B.putWord8 + -- in pad $ fromIntegral $ 8 - (len `mod` 8) + fmap (fromMaybe $ error "TODO: Error") sockGetPath +-- "hi" file turned to a NAR with `nix-store --dump`, Base64 encoded +sampleRegularBaseline :: LBS.ByteString +sampleRegularBaseline = B64.decodeLenient $ LBS.concat + ["DQAAAAAAAABuaXgtYXJjaGl2ZS0xAAAAAQAAAAAAAAAoAAAAAAA" + ,"AAAQAAAAAAAAAdHlwZQAAAAAHAAAAAAAAAHJlZ3VsYXIACAAAAA" + ,"AAAABjb250ZW50cwMAAAAAAAAAaGkKAAAAAAABAAAAAAAAACkAA" + ,"AAAAAAA" + ] + addTextToStore :: LBS.ByteString -> LBS.ByteString -> PathSet -> RepairFlag -> MonadStore (Maybe Path) addTextToStore name text references' repair = do runOpArgs AddTextToStore $ do diff --git a/hnix-store-remote/src/System/Nix/Store/Remote/Util.hs b/hnix-store-remote/src/System/Nix/Store/Remote/Util.hs index 78c4843..90555d0 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote/Util.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote/Util.hs @@ -15,6 +15,7 @@ import qualified Data.HashSet as HashSet import Network.Socket.ByteString (recv, sendAll) import System.Nix.Store.Remote.Types +import System.Nix.Hash import System.Nix.Path import System.Nix.Util @@ -73,7 +74,7 @@ mkPath p = case (pathName $ lBSToText p) of -- TODO: replace `undefined` with digest encoding function when -- [issue 24](https://github.com/haskell-nix/hnix-store/issues/24) -- is closed - Just x -> Just $ Path (undefined $ LBS.toStrict p) x --XXX: hash + Just x -> Just $ Path (hash $ LBS.toStrict p) x --XXX: hash Nothing -> Nothing -- WOOT From 0fe7ff5e8492ce6141d0eb400685516b4d07594b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 9 Mar 2019 22:03:28 -0500 Subject: [PATCH 09/13] Weird bit fiddling to make `printHashBytes32` work --- hnix-store-core/src/System/Nix/Internal/Hash.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hnix-store-core/src/System/Nix/Internal/Hash.hs b/hnix-store-core/src/System/Nix/Internal/Hash.hs index af80c3b..d91095e 100644 --- a/hnix-store-core/src/System/Nix/Internal/Hash.hs +++ b/hnix-store-core/src/System/Nix/Internal/Hash.hs @@ -126,7 +126,7 @@ printHashBytes32 :: BS.ByteString -> T.Text printHashBytes32 c = T.pack $ concatMap char32 [nChar - 1, nChar - 2 .. 0] where -- The base32 encoding is 8/5's as long as the base256 digest - nChar = fromIntegral $ BS.length c * 8 `div` 5 + nChar = fromIntegral $ ((BS.length c * 8 - 1) `div` 5) + 1 char32 :: Integer -> [Char] char32 i = [digits32 V.! digitInd] From 7067c32a9a3222a649442014dd610065b0161f6e Mon Sep 17 00:00:00 2001 From: Doug Beardsley Date: Sat, 9 Mar 2019 22:42:23 -0500 Subject: [PATCH 10/13] Implement readonly store path hashing --- hnix-store-core/hnix-store-core.cabal | 4 +- .../src/System/Nix/Internal/Hash.hs | 53 +++++++++++++------ hnix-store-core/src/System/Nix/Path.hs | 9 +++- .../src/System/Nix/ReadonlyStore.hs | 33 ++++++++++++ 4 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 hnix-store-core/src/System/Nix/ReadonlyStore.hs diff --git a/hnix-store-core/hnix-store-core.cabal b/hnix-store-core/hnix-store-core.cabal index b06ab03..7abe7e1 100644 --- a/hnix-store-core/hnix-store-core.cabal +++ b/hnix-store-core/hnix-store-core.cabal @@ -24,9 +24,11 @@ library , System.Nix.Internal.Hash , System.Nix.Nar , System.Nix.Path + , System.Nix.ReadonlyStore , System.Nix.Store , System.Nix.Util build-depends: base >=4.10 && <4.12 + , base16-bytestring , bytestring , binary , bytestring @@ -61,7 +63,7 @@ test-suite format-tests NarFormat Hash hs-source-dirs: - tests + tests build-depends: hnix-store-core , base diff --git a/hnix-store-core/src/System/Nix/Internal/Hash.hs b/hnix-store-core/src/System/Nix/Internal/Hash.hs index ecee504..555138a 100644 --- a/hnix-store-core/src/System/Nix/Internal/Hash.hs +++ b/hnix-store-core/src/System/Nix/Internal/Hash.hs @@ -12,25 +12,29 @@ Maintainer : Greg Hale {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeInType #-} +{-# LANGUAGE OverloadedStrings #-} module System.Nix.Internal.Hash where -import qualified Crypto.Hash.MD5 as MD5 -import qualified Crypto.Hash.SHA1 as SHA1 -import qualified Crypto.Hash.SHA256 as SHA256 -import qualified Data.ByteString as BS -import qualified Data.ByteString.Char8 as BSC -import Data.Bits (xor) -import qualified Data.ByteString as BS -import qualified Data.ByteString.Lazy as BSL -import qualified Data.Hashable as DataHashable -import Data.Kind (Type) -import Data.List (foldl') -import Data.Proxy (Proxy(Proxy)) -import qualified Data.Text as T -import qualified Data.Text.Encoding as T -import qualified Data.Vector as V -import Data.Word (Word8) +import qualified Crypto.Hash.MD5 as MD5 +import qualified Crypto.Hash.SHA1 as SHA1 +import qualified Crypto.Hash.SHA256 as SHA256 +import qualified Data.ByteString as BS +import qualified Data.ByteString.Base16 as Base16 +import qualified Data.ByteString.Char8 as BSC +import Data.Bits (xor) +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as BSL +import qualified Data.Hashable as DataHashable +import Data.Kind (Type) +import Data.List (foldl') +import Data.Monoid +import Data.Proxy (Proxy(Proxy)) +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import qualified Data.Vector as V +import Data.Word (Word8) import GHC.TypeLits -- | A tag for different hashing algorithms @@ -45,6 +49,18 @@ data HashAlgorithm' n | Truncated n (HashAlgorithm' n) deriving (Eq, Show) +class HashAlgoText a where + algoString :: Proxy a -> Text + +instance HashAlgoText 'MD5 where + algoString (Proxy :: Proxy 'MD5) = "md5" + +instance HashAlgoText 'SHA1 where + algoString (Proxy :: Proxy 'SHA1) = "sha1" + +instance HashAlgoText 'SHA256 where + algoString (Proxy :: Proxy 'SHA256) = "sha256" + type HashAlgorithm = HashAlgorithm' Nat -- | Types with kind @HashAlgorithm@ may be a @HasDigest@ instance @@ -80,8 +96,11 @@ hashLazy :: forall a.HasDigest a => BSL.ByteString -> Digest a hashLazy bsl = finalize $ foldl' (update @a) (initialize @a) (BSL.toChunks bsl) +digestText32 :: forall a. HashAlgoText a => Digest a -> T.Text +digestText32 d = algoString (Proxy :: Proxy a) <> ":" <> printAsBase32 d - +digestText16 :: forall a. HashAlgoText a => Digest a -> T.Text +digestText16 (Digest bs) = algoString (Proxy :: Proxy a) <> ":" <> T.decodeUtf8 (Base16.encode bs) -- | Convert any Digest to a base32-encoded string. -- This is not used in producing store path hashes diff --git a/hnix-store-core/src/System/Nix/Path.hs b/hnix-store-core/src/System/Nix/Path.hs index d92aa9e..69e5619 100644 --- a/hnix-store-core/src/System/Nix/Path.hs +++ b/hnix-store-core/src/System/Nix/Path.hs @@ -3,11 +3,13 @@ Description : Types and effects for interacting with the Nix store. Maintainer : Shea Levy -} {-# LANGUAGE DataKinds #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module System.Nix.Path ( FilePathPart(..) , PathHashAlgo , Path(..) + , pathToText , PathSet , SubstitutablePathInfo(..) , ValidPathInfo(..) @@ -19,12 +21,14 @@ module System.Nix.Path import System.Nix.Hash (Digest(..), HashAlgorithm'(Truncated, SHA256)) +import System.Nix.Internal.Hash import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BSC import Data.Hashable (Hashable (..), hashPtrWithSalt) import Data.HashMap.Strict (HashMap) import Data.HashSet (HashSet) import Data.Map.Strict (Map) +import Data.Monoid import Data.Text (Text) import qualified Data.Text as T import System.IO.Unsafe (unsafeDupablePerformIO) @@ -46,7 +50,7 @@ newtype PathName = PathName -- | A regular expression for matching a valid 'PathName' nameRegex :: Regex nameRegex = - makeRegex "[a-zA-Z0-9\\+\\-\\_\\?\\=][a-zA-Z0-9\\+\\-\\.\\_\\?\\=]*" + makeRegex ("[a-zA-Z0-9\\+\\-\\_\\?\\=][a-zA-Z0-9\\+\\-\\.\\_\\?\\=]*" :: String) -- | Construct a 'PathName', assuming the provided contents are valid. pathName :: Text -> Maybe PathName @@ -58,6 +62,9 @@ pathName n = case matchTest nameRegex n of data Path = Path !(Digest PathHashAlgo) !PathName deriving (Eq, Ord, Show) +pathToText :: Text -> Path -> Text +pathToText storeDir (Path h nm) = storeDir <> "/" <> printAsBase32 h <> "-" <> pathNameContents nm + type PathSet = HashSet Path -- | Information about substitutes for a 'Path'. diff --git a/hnix-store-core/src/System/Nix/ReadonlyStore.hs b/hnix-store-core/src/System/Nix/ReadonlyStore.hs new file mode 100644 index 0000000..316fb82 --- /dev/null +++ b/hnix-store-core/src/System/Nix/ReadonlyStore.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} + +module System.Nix.ReadonlyStore where + +import Data.ByteString (ByteString) +import Data.ByteString.Base16 as Base16 +import qualified Data.HashSet as HS +import Data.Text (Text) +import qualified Data.Text as T +import Data.Text.Encoding +import System.Nix.Internal.Hash +import System.Nix.Path + +makeStorePath :: Text -> Text -> Digest 'SHA256 -> Text -> Path +makeStorePath storeDir ty h nm = Path storeHash (PathName nm) + where + s = T.intercalate ":" + [ ty + , digestText16 h + , storeDir + , nm + ] + storeHash = truncateDigest $ hash $ encodeUtf8 s + +makeTextPath :: Text -> Text -> Digest 'SHA256 -> PathSet -> Path +makeTextPath storeDir nm h refs = makeStorePath storeDir ty h nm + where + ty = T.intercalate ":" ("text" : map (pathToText storeDir) (HS.toList refs)) + +computeStorePathForText :: Text -> Text -> ByteString -> PathSet -> Path +computeStorePathForText storeDir nm s refs = makeTextPath storeDir nm (hash s) refs From cf9fbf06c8e293936304828b0acb758849fd47e9 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Sun, 10 Mar 2019 10:29:17 -0400 Subject: [PATCH 11/13] Fix addToStore message encoding and do some cleanup --- .../src/System/Nix/Store/Remote.hs | 69 ++++--------------- .../src/System/Nix/Store/Remote/Protocol.hs | 7 ++ 2 files changed, 20 insertions(+), 56 deletions(-) diff --git a/hnix-store-remote/src/System/Nix/Store/Remote.hs b/hnix-store-remote/src/System/Nix/Store/Remote.hs index 5af52a4..63c916c 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote.hs @@ -175,25 +175,10 @@ type Source = () -- abstract binary source addToStoreNar :: ValidPathInfo -> Source -> RepairFlag -> CheckSigsFlag -> MonadStore () addToStoreNar = undefined -- XXX --- class BaseHashAlgorithm (a :: HashAlgorithm) where --- baseHashAlgorithm :: Bool - --- instance BaseHashAlgorithm MD5 where --- baseHashAlgorithm = MD5 - --- instance BaseHashAlgorithm SHA1 where --- baseHashAlgorithm = SHA1 - --- instance BaseHashAlgorithm SHA256 where --- baseHashAlgorithm = SHA256 - --- instance forall n a.BaseHashAlgorithm a => BaseHashAlgorithm (Truncated n a) where --- baseHashAlgorithm = baseHashAlgorithm @a - printHashType :: HashAlgorithm' Integer -> T.Text -printHashType MD5 = "MD5" -printHashType SHA1 = "SHA1" -printHashType SHA256 = "SHA256" +printHashType MD5 = "MD5" +printHashType SHA1 = "SHA1" +printHashType SHA256 = "SHA256" printHashType (Truncated _ a) = printHashType a type PathFilter = Path -> Bool @@ -208,54 +193,26 @@ addToStore -> RepairFlag -> MonadStore Path addToStore name pth recursive algoProxy pfilter repair = do - -- Get length first - -- len <- liftIO $ LBS.length . B.runPut . putNar <$> localPackNar narEffectsIO pth - -- Fetch full NAR bytestring separately. We are trying to - -- avoid forcing the full string in memory + + -- TODO: Is this lazy enough? We need `B.putLazyByteString bs` to stream `bs` bs :: LBS.ByteString <- liftIO $ B.runPut . putNar <$> localPackNar narEffectsIO pth - liftIO $ print (LBS.length bs) - bs' <- liftIO $ putNar <$> localPackNar narEffectsIO pth - let bs'' = putByteStringLen "nix-archive-1" - let bs = sampleRegularBaseline - let len = LBS.length bs + runOpArgs AddToStore $ do putByteStringLen name - -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? - putByteStringLen $ if algoVal @a `elem` [SHA256, Truncated 20 SHA256] - && recursive - then (LBS.pack [0]) - else (LBS.pack [1]) - -- then "0" - -- else "1" - -- TODO: really send the string 0 or 1? Or is this Word8's 0 and 1? - putByteStringLen $ if recursive - then (LBS.pack [1]) - else (LBS.pack [0]) - -- then "1" - -- else "0" + if algoVal @a `elem` [SHA256, Truncated 20 SHA256] && recursive + then putInt 0 + else putInt 1 + if recursive + then putInt 1 + else putInt 0 - -- putByteStringLen (T.encodeUtf8 . T.toLower . printHashType $ algoVal @a) + putByteStringLen (T.encodeUtf8 . T.toLower . printHashType $ algoVal @a) - -- putByteStringLen bs - -- putInt len B.putLazyByteString bs - -- bs'' - -- when (len `mod` 8 /= 0) $ - -- let pad x = forM_ (take x $ cycle [0]) B.putWord8 - -- in pad $ fromIntegral $ 8 - (len `mod` 8) fmap (fromMaybe $ error "TODO: Error") sockGetPath --- "hi" file turned to a NAR with `nix-store --dump`, Base64 encoded -sampleRegularBaseline :: LBS.ByteString -sampleRegularBaseline = B64.decodeLenient $ LBS.concat - ["DQAAAAAAAABuaXgtYXJjaGl2ZS0xAAAAAQAAAAAAAAAoAAAAAAA" - ,"AAAQAAAAAAAAAdHlwZQAAAAAHAAAAAAAAAHJlZ3VsYXIACAAAAA" - ,"AAAABjb250ZW50cwMAAAAAAAAAaGkKAAAAAAABAAAAAAAAACkAA" - ,"AAAAAAA" - ] - addTextToStore :: LBS.ByteString -> LBS.ByteString -> PathSet -> RepairFlag -> MonadStore (Maybe Path) addTextToStore name text references' repair = do runOpArgs AddTextToStore $ do diff --git a/hnix-store-remote/src/System/Nix/Store/Remote/Protocol.hs b/hnix-store-remote/src/System/Nix/Store/Remote/Protocol.hs index 2c39858..e0e724c 100644 --- a/hnix-store-remote/src/System/Nix/Store/Remote/Protocol.hs +++ b/hnix-store-remote/src/System/Nix/Store/Remote/Protocol.hs @@ -127,6 +127,13 @@ runOp op = runOpArgs op $ return () runOpArgs :: WorkerOp -> Put -> MonadStore () runOpArgs op args = do + + -- Temporary hack for printing the messages destined for nix-daemon socket + when False $ + liftIO $ LBS.writeFile "mytestfile2" $ runPut $ do + putInt $ opNum op + args + sockPut $ do putInt $ opNum op args From 9bbe54a03b571e83d637e874ad656cd9481db022 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 10 Mar 2019 12:30:26 -0400 Subject: [PATCH 12/13] Add test corresponding to `builtins.placeholder "foo"` in layer above We were hashing correctly, but base32-ing wrong. --- hnix-store-core/tests/Hash.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hnix-store-core/tests/Hash.hs b/hnix-store-core/tests/Hash.hs index 4f05fc5..b8f4707 100644 --- a/hnix-store-core/tests/Hash.hs +++ b/hnix-store-core/tests/Hash.hs @@ -31,6 +31,10 @@ spec_hash = do describe "hashing parity with nix-store" $ do + it "produces (base32 . sha256) of \"nix-output:foo\" the same as Nix does at the moment for placeholder \"foo\"" $ + shouldBe (printAsBase32 (hash @SHA256 "nix-output:foo")) + "1x0ymrsy7yr7i9wdsqy9khmzc1yy7nvxw6rdp72yzn50285s67j5" + it "produces (base32 . sha1) of \"Hello World\" the same as the thesis" $ shouldBe (printAsBase32 (hash @SHA1 "Hello World")) "s23c9fs0v32pf6bhmcph5rbqsyl5ak8a" From ecf4d991f92b1d2607ad392fc91befd1cfa13ed3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 10 Mar 2019 13:09:45 -0400 Subject: [PATCH 13/13] Add comment to the mysterious order of the `+ 1` `- 1` in `printHashBytes32` --- hnix-store-core/src/System/Nix/Internal/Hash.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hnix-store-core/src/System/Nix/Internal/Hash.hs b/hnix-store-core/src/System/Nix/Internal/Hash.hs index d91095e..eef68cf 100644 --- a/hnix-store-core/src/System/Nix/Internal/Hash.hs +++ b/hnix-store-core/src/System/Nix/Internal/Hash.hs @@ -125,7 +125,10 @@ newtype Digest (a :: HashAlgorithm) = Digest printHashBytes32 :: BS.ByteString -> T.Text printHashBytes32 c = T.pack $ concatMap char32 [nChar - 1, nChar - 2 .. 0] where - -- The base32 encoding is 8/5's as long as the base256 digest + -- The base32 encoding is 8/5's as long as the base256 digest. This `+ 1` + -- `- 1` business is a bit odd, but has always been used in C++ since the + -- base32 truncation was added in was first added in + -- d58a11e019813902b6c4547ca61a127938b2cc20. nChar = fromIntegral $ ((BS.length c * 8 - 1) `div` 5) + 1 char32 :: Integer -> [Char]