packages feed

lsm-tree-1.0.0.0: src-extras/Database/LSMTree/Extras/UTxO.hs

{-# LANGUAGE DeriveAnyClass             #-}
{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE DerivingVia                #-}
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
{-# LANGUAGE NamedFieldPuns             #-}
{-# LANGUAGE TypeFamilies               #-}

module Database.LSMTree.Extras.UTxO (
    UTxOKey (..)
  , UTxOValue (..)
  , zeroUTxOValue
  , UTxOBlob (..)
  ) where

import           Control.DeepSeq
import           Data.Bits
import qualified Data.ByteString as BS
import qualified Data.Primitive as P
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Generic.Mutable as VGM
import qualified Data.Vector.Primitive as VP
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
import           Data.WideWord.Word128
import           Data.WideWord.Word256
import           Data.Word
import           Database.LSMTree.Extras.Orphans
import           Database.LSMTree.Internal.Primitive
import qualified Database.LSMTree.Internal.RawBytes as RB
import           Database.LSMTree.Internal.Serialise ()
import           Database.LSMTree.Internal.Serialise.Class as Class
import           Database.LSMTree.Internal.Vector
import           GHC.Generics
import           System.Random
import           Test.QuickCheck hiding ((.&.))

{-------------------------------------------------------------------------------
  UTxO keys
-------------------------------------------------------------------------------}

-- | A model of a UTxO key (34 bytes) after @TxIn@: a 256-bit hash, 16-bit identifier
data UTxOKey = UTxOKey {
    txId :: !Word256 -- no unpack, since the @TxId@ field doesn't have it
  , txIx :: {-# UNPACK #-} !Word16
  }
  deriving stock (Show, Eq, Ord, Generic)
  deriving anyclass (Uniform, NFData)

-- TODO: reduce number of allocations, optimise (by using unsafe functions)
instance SerialiseKey UTxOKey where
  serialiseKey UTxOKey{txId, txIx} =
    RB.RawBytes $ mkPrimVector 0 34 $ P.runByteArray $ do
      ba <- P.newByteArray 34
      let !cut = fromIntegral ((txId .&. (fromIntegral (0xffff :: Word16) `unsafeShiftL` 192)) `unsafeShiftR` 192)
      P.writeByteArray ba 0  $ byteSwapWord256 txId
      P.writeByteArray ba 3  $ byteSwap16 txIx
      P.writeByteArray ba 16 $ byteSwap16 cut
      pure ba
  deserialiseKey (RawBytes (VP.Vector off len ba)) =
    requireBytesExactly "UTxOKey" 34 len $
      let !cut   = byteSwap16      $ indexWord8ArrayAsWord16  ba (off + 32)
          !txIx  = byteSwap16      $ indexWord8ArrayAsWord16  ba (off + 6)
          !txId_ = byteSwapWord256 $ indexWord8ArrayAsWord256 ba off
          !txId  = (txId_ .&. (complement (fromIntegral (0xffff :: Word16) `unsafeShiftL` 192)))
                      .|. (fromIntegral cut `unsafeShiftL` 192)
      in UTxOKey{txId, txIx}

instance Arbitrary UTxOKey where
  arbitrary = UTxOKey <$> arbitrary <*> arbitrary
  shrink (UTxOKey a b) = [ UTxOKey a' b' | (a', b') <- shrink (a, b) ]

newtype instance VUM.MVector s UTxOKey = MV_UTxOKey (VU.MVector s (Word256, Word16))
newtype instance VU.Vector     UTxOKey = V_UTxOKey  (VU.Vector    (Word256, Word16))

instance VU.IsoUnbox UTxOKey (Word256, Word16) where
  toURepr (UTxOKey a b) = (a, b)
  fromURepr (a, b) = UTxOKey a b
  {-# INLINE toURepr #-}
  {-# INLINE fromURepr #-}

deriving via VU.As UTxOKey (Word256, Word16) instance VGM.MVector VU.MVector UTxOKey
deriving via VU.As UTxOKey (Word256, Word16) instance VG.Vector   VU.Vector  UTxOKey

instance VUM.Unbox UTxOKey

{-------------------------------------------------------------------------------
  UTxO values
-------------------------------------------------------------------------------}

-- | A model of a UTxO value (60 bytes)
data UTxOValue = UTxOValue {
    utxoValue256 :: !Word256
  , utxoValue128 :: !Word128
  , utxoValue64  :: !Word64
  , utxoValue32  :: !Word32
  }
  deriving stock (Show, Eq, Ord, Generic)
  deriving anyclass (Uniform, NFData)

instance SerialiseValue UTxOValue where
  serialiseValue (UTxOValue {utxoValue256, utxoValue128, utxoValue64, utxoValue32}) =
    RB.RawBytes $ mkPrimVector 0 60 $ P.runByteArray $ do
      ba <- P.newByteArray 60
      P.writeByteArray ba 0 utxoValue256
      P.writeByteArray ba 2 utxoValue128
      P.writeByteArray ba 6 utxoValue64
      P.writeByteArray ba 14 utxoValue32
      pure ba
  deserialiseValue (RawBytes (VP.Vector off len ba)) =
    requireBytesExactly "UTxOValue" 60 len $
      UTxOValue (indexWord8ArrayAsWord256 ba off)
                (indexWord8ArrayAsWord128 ba (off + 32))
                (indexWord8ArrayAsWord64  ba (off + 48))
                (indexWord8ArrayAsWord32  ba (off + 56))

instance Arbitrary UTxOValue where
  arbitrary = UTxOValue <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
  shrink (UTxOValue a b c d) = [ UTxOValue a' b' c' d'
                               | (a', b', c', d') <- shrink (a, b, c, d) ]

newtype instance VUM.MVector s UTxOValue = MV_UTxOValue (VU.MVector s (Word256, Word128, Word64, Word32))
newtype instance VU.Vector     UTxOValue = V_UTxOValue  (VU.Vector    (Word256, Word128, Word64, Word32))

instance VU.IsoUnbox UTxOValue (Word256, Word128, Word64, Word32) where
  toURepr (UTxOValue a b c d) = (a, b, c, d)
  fromURepr (a, b, c, d) = UTxOValue a b c d
  {-# INLINE toURepr #-}
  {-# INLINE fromURepr #-}

deriving via VU.As UTxOValue (Word256, Word128, Word64, Word32) instance VGM.MVector VU.MVector UTxOValue
deriving via VU.As UTxOValue (Word256, Word128, Word64, Word32) instance VG.Vector   VU.Vector  UTxOValue

instance VUM.Unbox UTxOValue

zeroUTxOValue :: UTxOValue
zeroUTxOValue = UTxOValue 0 0 0 0

{-------------------------------------------------------------------------------
  UTxO blobs
-------------------------------------------------------------------------------}

-- | A blob of arbitrary size
newtype UTxOBlob = UTxOBlob BS.ByteString
  deriving stock (Show, Eq, Ord, Generic)
  deriving anyclass NFData

instance SerialiseValue UTxOBlob where
  serialiseValue (UTxOBlob bs) = Class.serialiseValue bs
  deserialiseValue = error "deserialiseValue: UTxOBlob"