{- |
Module      : Singular.Registry.Wire.Proof
Description : Neighbor and proof-step values for MPF Merkle proofs
License     : Apache-2.0

The Aiken @merkle-patricia-forestry@ proof encoding: a single step in
an MPF Merkle proof and the neighbor node a fork step carries, each
with its codec. Reads only "Singular.Registry.Wire.Primitive"; nothing
here is importable by a caller — the public surface is the
"Singular.Registry.Types" facade.
-}
module Singular.Registry.Wire.Proof
    ( -- * Proof steps (Aiken MPF proof encoding)
      ProofStep (..)
    , Neighbor (..)
    ) where

import Data.ByteString (ByteString)
import PlutusCore.Data (Data (..))
import PlutusTx.IsData.Class
    ( FromData (..)
    , ToData (..)
    , UnsafeFromData (..)
    )
import Singular.Registry.Wire.Primitive
    ( bsFromD
    , bsToD
    , mkD
    , unD
    )

{- | A single step in an MPF Merkle proof, matching
the Aiken @ProofStep@ type from
@aiken-lang\/merkle-patricia-forestry@.
-}
data ProofStep
    = -- | Branch step (Constr 0)
      Branch
        { ProofStep -> Integer
branchSkip :: !Integer
        -- ^ Number of shared nibbles to skip
        , ProofStep -> ByteString
branchNeighbors :: !ByteString
        -- ^ Concatenated neighbor hashes (4 x 32 bytes)
        }
    | -- | Fork step (Constr 1)
      Fork
        { ProofStep -> Integer
forkSkip :: !Integer
        -- ^ Number of shared nibbles to skip
        , ProofStep -> Neighbor
forkNeighbor :: !Neighbor
        -- ^ The sibling branch at the fork point
        }
    | -- | Leaf step (Constr 2)
      Leaf
        { ProofStep -> Integer
leafSkip :: !Integer
        -- ^ Number of shared nibbles to skip
        , ProofStep -> ByteString
leafKey :: !ByteString
        -- ^ Remaining key suffix at the leaf
        , ProofStep -> ByteString
leafValue :: !ByteString
        -- ^ Value hash stored at the leaf
        }
    deriving stock (Int -> ProofStep -> ShowS
[ProofStep] -> ShowS
ProofStep -> String
(Int -> ProofStep -> ShowS)
-> (ProofStep -> String)
-> ([ProofStep] -> ShowS)
-> Show ProofStep
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ProofStep -> ShowS
showsPrec :: Int -> ProofStep -> ShowS
$cshow :: ProofStep -> String
show :: ProofStep -> String
$cshowList :: [ProofStep] -> ShowS
showList :: [ProofStep] -> ShowS
Show, ProofStep -> ProofStep -> Bool
(ProofStep -> ProofStep -> Bool)
-> (ProofStep -> ProofStep -> Bool) -> Eq ProofStep
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ProofStep -> ProofStep -> Bool
== :: ProofStep -> ProofStep -> Bool
$c/= :: ProofStep -> ProofStep -> Bool
/= :: ProofStep -> ProofStep -> Bool
Eq)

-- | Neighbor node in a fork proof step.
data Neighbor = Neighbor
    { Neighbor -> Integer
neighborNibble :: !Integer
    -- ^ Hex digit (0-15) identifying the fork branch
    , Neighbor -> ByteString
neighborPrefix :: !ByteString
    -- ^ Common prefix nibbles of the neighbor subtree
    , Neighbor -> ByteString
neighborRoot :: !ByteString
    -- ^ Merkle root hash of the neighbor subtree
    }
    deriving stock (Int -> Neighbor -> ShowS
[Neighbor] -> ShowS
Neighbor -> String
(Int -> Neighbor -> ShowS)
-> (Neighbor -> String) -> ([Neighbor] -> ShowS) -> Show Neighbor
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Neighbor -> ShowS
showsPrec :: Int -> Neighbor -> ShowS
$cshow :: Neighbor -> String
show :: Neighbor -> String
$cshowList :: [Neighbor] -> ShowS
showList :: [Neighbor] -> ShowS
Show, Neighbor -> Neighbor -> Bool
(Neighbor -> Neighbor -> Bool)
-> (Neighbor -> Neighbor -> Bool) -> Eq Neighbor
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Neighbor -> Neighbor -> Bool
== :: Neighbor -> Neighbor -> Bool
$c/= :: Neighbor -> Neighbor -> Bool
/= :: Neighbor -> Neighbor -> Bool
Eq)

-- ---------------------------------------------------------
-- ToData / FromData instances
-- ---------------------------------------------------------

instance ToData Neighbor where
    toBuiltinData :: Neighbor -> BuiltinData
toBuiltinData Neighbor{Integer
ByteString
neighborNibble :: Neighbor -> Integer
neighborPrefix :: Neighbor -> ByteString
neighborRoot :: Neighbor -> ByteString
neighborNibble :: Integer
neighborPrefix :: ByteString
neighborRoot :: ByteString
..} =
        Data -> BuiltinData
mkD (Data -> BuiltinData) -> Data -> BuiltinData
forall a b. (a -> b) -> a -> b
$
            Integer -> [Data] -> Data
Constr
                Integer
0
                [ Integer -> Data
I Integer
neighborNibble
                , ByteString -> Data
bsToD ByteString
neighborPrefix
                , ByteString -> Data
bsToD ByteString
neighborRoot
                ]

instance FromData Neighbor where
    fromBuiltinData :: BuiltinData -> Maybe Neighbor
fromBuiltinData BuiltinData
bd = case BuiltinData -> Data
unD BuiltinData
bd of
        Constr Integer
0 [I Integer
nib, Data
pfx, Data
rt] ->
            Integer -> ByteString -> ByteString -> Neighbor
Neighbor Integer
nib
                (ByteString -> ByteString -> Neighbor)
-> Maybe ByteString -> Maybe (ByteString -> Neighbor)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Data -> Maybe ByteString
bsFromD Data
pfx
                Maybe (ByteString -> Neighbor)
-> Maybe ByteString -> Maybe Neighbor
forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Data -> Maybe ByteString
bsFromD Data
rt
        Data
_ -> Maybe Neighbor
forall a. Maybe a
Nothing

instance UnsafeFromData Neighbor where
    unsafeFromBuiltinData :: BuiltinData -> Neighbor
unsafeFromBuiltinData BuiltinData
bd = case BuiltinData -> Data
unD BuiltinData
bd of
        Constr Integer
0 [I Integer
nib, B ByteString
pfx, B ByteString
rt] ->
            Integer -> ByteString -> ByteString -> Neighbor
Neighbor Integer
nib ByteString
pfx ByteString
rt
        Data
_ -> String -> Neighbor
forall a. HasCallStack => String -> a
error String
"unsafeFromBuiltinData: Neighbor"

instance ToData ProofStep where
    toBuiltinData :: ProofStep -> BuiltinData
toBuiltinData Branch{Integer
ByteString
branchSkip :: ProofStep -> Integer
branchNeighbors :: ProofStep -> ByteString
branchSkip :: Integer
branchNeighbors :: ByteString
..} =
        Data -> BuiltinData
mkD (Data -> BuiltinData) -> Data -> BuiltinData
forall a b. (a -> b) -> a -> b
$
            Integer -> [Data] -> Data
Constr
                Integer
0
                [ Integer -> Data
I Integer
branchSkip
                , ByteString -> Data
bsToD ByteString
branchNeighbors
                ]
    toBuiltinData Fork{Integer
Neighbor
forkSkip :: ProofStep -> Integer
forkNeighbor :: ProofStep -> Neighbor
forkSkip :: Integer
forkNeighbor :: Neighbor
..} =
        Data -> BuiltinData
mkD (Data -> BuiltinData) -> Data -> BuiltinData
forall a b. (a -> b) -> a -> b
$
            Integer -> [Data] -> Data
Constr
                Integer
1
                [ Integer -> Data
I Integer
forkSkip
                , BuiltinData -> Data
unD (Neighbor -> BuiltinData
forall a. ToData a => a -> BuiltinData
toBuiltinData Neighbor
forkNeighbor)
                ]
    toBuiltinData Leaf{Integer
ByteString
leafSkip :: ProofStep -> Integer
leafKey :: ProofStep -> ByteString
leafValue :: ProofStep -> ByteString
leafSkip :: Integer
leafKey :: ByteString
leafValue :: ByteString
..} =
        Data -> BuiltinData
mkD (Data -> BuiltinData) -> Data -> BuiltinData
forall a b. (a -> b) -> a -> b
$
            Integer -> [Data] -> Data
Constr
                Integer
2
                [ Integer -> Data
I Integer
leafSkip
                , ByteString -> Data
bsToD ByteString
leafKey
                , ByteString -> Data
bsToD ByteString
leafValue
                ]

instance FromData ProofStep where
    fromBuiltinData :: BuiltinData -> Maybe ProofStep
fromBuiltinData BuiltinData
bd = case BuiltinData -> Data
unD BuiltinData
bd of
        Constr Integer
0 [I Integer
sk, Data
nb] ->
            Integer -> ByteString -> ProofStep
Branch Integer
sk (ByteString -> ProofStep) -> Maybe ByteString -> Maybe ProofStep
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Data -> Maybe ByteString
bsFromD Data
nb
        Constr Integer
1 [I Integer
sk, Data
nd] ->
            Integer -> Neighbor -> ProofStep
Fork Integer
sk
                (Neighbor -> ProofStep) -> Maybe Neighbor -> Maybe ProofStep
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BuiltinData -> Maybe Neighbor
forall a. FromData a => BuiltinData -> Maybe a
fromBuiltinData (Data -> BuiltinData
mkD Data
nd)
        Constr Integer
2 [I Integer
sk, Data
k, Data
v] ->
            Integer -> ByteString -> ByteString -> ProofStep
Leaf Integer
sk (ByteString -> ByteString -> ProofStep)
-> Maybe ByteString -> Maybe (ByteString -> ProofStep)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Data -> Maybe ByteString
bsFromD Data
k Maybe (ByteString -> ProofStep)
-> Maybe ByteString -> Maybe ProofStep
forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Data -> Maybe ByteString
bsFromD Data
v
        Data
_ -> Maybe ProofStep
forall a. Maybe a
Nothing

instance UnsafeFromData ProofStep where
    unsafeFromBuiltinData :: BuiltinData -> ProofStep
unsafeFromBuiltinData BuiltinData
bd = case BuiltinData -> Data
unD BuiltinData
bd of
        Constr Integer
0 [I Integer
sk, B ByteString
nb] -> Integer -> ByteString -> ProofStep
Branch Integer
sk ByteString
nb
        Constr Integer
1 [I Integer
sk, Data
nd] ->
            Integer -> Neighbor -> ProofStep
Fork Integer
sk (Neighbor -> ProofStep) -> Neighbor -> ProofStep
forall a b. (a -> b) -> a -> b
$
                BuiltinData -> Neighbor
forall a. UnsafeFromData a => BuiltinData -> a
unsafeFromBuiltinData (Data -> BuiltinData
mkD Data
nd)
        Constr Integer
2 [I Integer
sk, B ByteString
k, B ByteString
v] -> Integer -> ByteString -> ByteString -> ProofStep
Leaf Integer
sk ByteString
k ByteString
v
        Data
_ ->
            String -> ProofStep
forall a. HasCallStack => String -> a
error String
"unsafeFromBuiltinData: ProofStep"