packages feed

moonlight-delta-0.1.0.1: src-patch/Moonlight/Delta/Patch/Internal/NodeCommitment.hs

module Moonlight.Delta.Patch.Internal.NodeCommitment
  ( DigestSipKeys (..),
    NodeCommitment (..),
    SipHashNodeCommitment,
    sipHashNodeCommitment,
  )
where

import Data.Word (Word64)
import Moonlight.Core
  ( SipKey (..),
    StableHashDigest (..),
    StableHashEncoding,
    stableHashEncodingDigest,
    stableHashEncodingLength,
    stableHashEncodingWord8,
    stableHashEncodingWord64LE,
  )
import Moonlight.Delta.Patch.Internal.IncrementalDigest
  ( RawDigest128 (..),
    deltaHashEncodingVersion,
    rawDigestEncoding,
  )
import Prelude

data DigestSipKeys = DigestSipKeys !SipKey !SipKey

class NodeCommitment commitment where
  pathSipKey :: commitment -> SipKey
  emptySipKey :: commitment -> DigestSipKeys
  leafSipKey :: commitment -> DigestSipKeys
  branchSipKey :: commitment -> DigestSipKeys

  -- | Deterministically localize a key encoding to a 64-bit Patricia route.
  -- This word only chooses trie branches and detects co-resident collisions; it
  -- is no longer the key commitment. The leaf binds the complete key encoding.
  localizePath :: commitment -> StableHashEncoding -> Word64
  localizePath commitment =
    stableHashDigestWord (pathSipKey commitment)

  emptyDigest :: commitment -> RawDigest128
  emptyDigest commitment =
    digestWithKeys
      (emptySipKey commitment)
      ( stableHashEncodingWord8 0
          <> stableHashEncodingWord64LE deltaHashEncodingVersion
      )

  -- | Commit a leaf. Both digest lanes absorb the node tag, protocol version,
  -- routing path, and the length-framed complete key and value encodings, so a
  -- 64-bit path or value collision no longer yields equal leaves.
  leafDigest :: commitment -> Word64 -> StableHashEncoding -> StableHashEncoding -> RawDigest128
  leafDigest commitment path keyEncoding valueEncoding =
    digestWithKeys
      (leafSipKey commitment)
      ( stableHashEncodingWord8 1
          <> stableHashEncodingWord64LE deltaHashEncodingVersion
          <> stableHashEncodingWord64LE path
          <> stableHashEncodingWord64LE (fromIntegral (stableHashEncodingLength keyEncoding))
          <> keyEncoding
          <> stableHashEncodingWord64LE (fromIntegral (stableHashEncodingLength valueEncoding))
          <> valueEncoding
      )

  branchDigest :: commitment -> Word64 -> Word64 -> RawDigest128 -> RawDigest128 -> RawDigest128
  branchDigest commitment prefix branchingBit leftDigest rightDigest =
    digestWithKeys
      (branchSipKey commitment)
      ( stableHashEncodingWord8 2
          <> stableHashEncodingWord64LE deltaHashEncodingVersion
          <> stableHashEncodingWord64LE prefix
          <> stableHashEncodingWord64LE branchingBit
          <> stableHashEncodingWord64LE (rawDigestLane0 leftDigest)
          <> stableHashEncodingWord64LE (rawDigestLane1 leftDigest)
          <> stableHashEncodingWord64LE (rawDigestLane0 rightDigest)
          <> stableHashEncodingWord64LE (rawDigestLane1 rightDigest)
      )

data SipHashNodeCommitment = SipHashNodeCommitment

sipHashNodeCommitment :: SipHashNodeCommitment
sipHashNodeCommitment = SipHashNodeCommitment

instance NodeCommitment SipHashNodeCommitment where
  pathSipKey _commitment =
    SipKey 0x6d6c2d64656c7461 0x706174682d763031

  emptySipKey _commitment =
    DigestSipKeys
      (SipKey 0x6d6c2d64656c7461 0x656d70742d763031)
      (SipKey 0x6d6c2d64656c7461 0x656d70742d763032)

  leafSipKey _commitment =
    DigestSipKeys
      (SipKey 0x6d6c2d64656c7461 0x6c6561662d763031)
      (SipKey 0x6d6c2d64656c7461 0x6c6561662d763032)

  branchSipKey _commitment =
    DigestSipKeys
      (SipKey 0x6d6c2d64656c7461 0x6272616e2d763031)
      (SipKey 0x6d6c2d64656c7461 0x6272616e2d763032)

digestWithKeys :: DigestSipKeys -> StableHashEncoding -> RawDigest128
digestWithKeys (DigestSipKeys lane0Key lane1Key) =
  rawDigestEncoding lane0Key lane1Key

stableHashDigestWord :: SipKey -> StableHashEncoding -> Word64
stableHashDigestWord sipKey encoding =
  case stableHashEncodingDigest sipKey encoding of
    StableHashDigest digestWord -> digestWord