{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
module Singular.Registry.TxBuilder.Internal.Edges
(
leafAbsent
, leafActive
, leafTerminal
, walkEdge
, deltaOf
, policyOfKind
, approvalName
, approvalDestination
, destinationDatumHash
, pinScriptHash
, hookAccountAddress
, keyAccountAddress
, ConsumerBinding (..)
, deriveConsumerBinding
, mkConsumerScript
, failedWitnessHash
, evalScriptHash
, isBudgetFailure
) where
import Cardano.Crypto.Hash
( Blake2b_256
, hashFromBytes
, hashToBytes
, hashWith
)
import Cardano.Ledger.Address (AccountAddress (..), AccountId (..))
import Cardano.Ledger.BaseTypes (Network)
import Cardano.Ledger.Core (Script)
import Cardano.Ledger.Credential (Credential (..))
import Cardano.Ledger.Hashes (ScriptHash (..), extractHash)
import Cardano.Ledger.Keys
( KeyHash (..)
, KeyRole (..)
)
import Cardano.Ledger.Plutus.Data (Data (..), hashData)
import Data.ByteString (ByteString)
import Data.ByteString qualified as BS
import Data.ByteString.Short qualified as SBS
import Data.Char (isHexDigit)
import Data.Coerce (coerce)
import Data.List (isInfixOf, isPrefixOf, tails)
import Data.Maybe (fromMaybe)
import PlutusCore.Data qualified as PLC
import Singular.Registry.Config (CageConfig (..))
import Singular.Registry.Ledger (ConwayEra)
import Singular.Registry.Trie (Trie (..))
import Singular.Registry.TxBuilder.Internal.Identity
( computeScriptHash
, scriptFromBytes
, scriptHashBytes
)
import Singular.Registry.Types
( Edge
, ProofStep
, edgeInsertActive
, edgeUpdateTerminal
)
leafAbsent, leafActive, leafTerminal :: ByteString
leafAbsent :: ByteString
leafAbsent = Word8 -> ByteString
BS.singleton Word8
0x00
leafActive :: ByteString
leafActive = Word8 -> ByteString
BS.singleton Word8
0x01
leafTerminal :: ByteString
leafTerminal = Word8 -> ByteString
BS.singleton Word8
0x02
walkEdge :: (Monad m) => Trie m -> ByteString -> Edge -> m [ProofStep]
walkEdge :: forall (m :: * -> *).
Monad m =>
Trie m -> ByteString -> Integer -> m [ProofStep]
walkEdge Trie m
trie ByteString
key Integer
edge
| Integer
edge Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
edgeInsertActive = ByteString -> m [ProofStep]
inserting ByteString
leafActive
| Integer
edge Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
edgeUpdateTerminal = ByteString -> m [ProofStep]
replacing ByteString
leafTerminal
| Bool
otherwise = m [ProofStep]
steps
where
steps :: m [ProofStep]
steps = [ProofStep] -> Maybe [ProofStep] -> [ProofStep]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [ProofStep] -> [ProofStep])
-> m (Maybe [ProofStep]) -> m [ProofStep]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Trie m -> ByteString -> m (Maybe [ProofStep])
forall (m :: * -> *). Trie m -> ByteString -> m (Maybe [ProofStep])
getProofSteps Trie m
trie ByteString
key
inserting :: ByteString -> m [ProofStep]
inserting ByteString
leaf = do
_ <- Trie m -> ByteString -> ByteString -> m Root
forall (m :: * -> *). Trie m -> ByteString -> ByteString -> m Root
insert Trie m
trie ByteString
key ByteString
leaf
steps
replacing :: ByteString -> m [ProofStep]
replacing ByteString
leaf = do
before <- m [ProofStep]
steps
_ <- delete trie key
_ <- insert trie key leaf
pure before
deltaOf :: Integer -> [(Integer, Integer)]
deltaOf :: Integer -> [(Integer, Integer)]
deltaOf Integer
edge = case Integer
edge of
Integer
1 -> [(Integer
1, Integer
1)]
Integer
3 -> [(Integer
1, -Integer
1)]
Integer
_ -> []
policyOfKind :: CageConfig -> Integer -> SBS.ShortByteString
policyOfKind :: CageConfig -> Integer -> ShortByteString
policyOfKind CageConfig
cfg Integer
kind = case Integer
kind of
Integer
0 -> CageConfig -> ShortByteString
cfgAbsentPolicy CageConfig
cfg
Integer
1 -> CageConfig -> ShortByteString
cfgActivePolicy CageConfig
cfg
Integer
2 -> CageConfig -> ShortByteString
cfgTerminalPolicy CageConfig
cfg
Integer
_ -> [Char] -> ShortByteString
forall a. HasCallStack => [Char] -> a
error [Char]
"policyOfKind: not a token kind"
approvalName
:: Integer
-> ByteString
-> ByteString
-> (ByteString, ByteString)
-> ByteString
approvalName :: Integer
-> ByteString
-> ByteString
-> (ByteString, ByteString)
-> ByteString
approvalName Integer
edge ByteString
key ByteString
owner (ByteString
destAddr, ByteString
datumHash) =
ByteString -> ByteString
blake2b256
( Word8 -> ByteString
BS.singleton (Integer -> Word8
forall a. Num a => Integer -> a
fromInteger Integer
edge)
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
key
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
owner
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
destAddr
ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
datumHash
)
destinationDatumHash :: Maybe PLC.Data -> ByteString
destinationDatumHash :: Maybe Data -> ByteString
destinationDatumHash =
ByteString -> (Data -> ByteString) -> Maybe Data -> ByteString
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
ByteString
BS.empty
( \Data
datum ->
Hash Blake2b_256 EraIndependentData -> ByteString
forall h a. Hash h a -> ByteString
hashToBytes (SafeHash EraIndependentData -> Hash Blake2b_256 EraIndependentData
forall i. SafeHash i -> Hash Blake2b_256 i
extractHash (Data ConwayEra -> SafeHash EraIndependentData
forall era. Data era -> SafeHash EraIndependentData
hashData (Data -> Data ConwayEra
forall era. Era era => Data -> Data era
Data Data
datum :: Data ConwayEra)))
)
approvalDestination
:: (ByteString, Maybe PLC.Data) -> (ByteString, ByteString)
approvalDestination :: (ByteString, Maybe Data) -> (ByteString, ByteString)
approvalDestination (ByteString
address, Maybe Data
datum) = (ByteString
address, Maybe Data -> ByteString
destinationDatumHash Maybe Data
datum)
blake2b256 :: ByteString -> ByteString
blake2b256 :: ByteString -> ByteString
blake2b256 = Hash Blake2b_256 ByteString -> ByteString
forall h a. Hash h a -> ByteString
hashToBytes (Hash Blake2b_256 ByteString -> ByteString)
-> (ByteString -> Hash Blake2b_256 ByteString)
-> ByteString
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall h a. HashAlgorithm h => (a -> ByteString) -> a -> Hash h a
hashWith @Blake2b_256 ByteString -> ByteString
forall a. a -> a
id
pinScriptHash :: ByteString -> ScriptHash
pinScriptHash :: ByteString -> ScriptHash
pinScriptHash ByteString
bs = case ByteString -> Maybe (Hash ADDRHASH EraIndependentScript)
forall h a. HashAlgorithm h => ByteString -> Maybe (Hash h a)
hashFromBytes ByteString
bs of
Just Hash ADDRHASH EraIndependentScript
h -> Hash ADDRHASH EraIndependentScript -> ScriptHash
ScriptHash Hash ADDRHASH EraIndependentScript
h
Maybe (Hash ADDRHASH EraIndependentScript)
Nothing ->
[Char] -> ScriptHash
forall a. HasCallStack => [Char] -> a
error
[Char]
"pinScriptHash: consumer pin must be 28 bytes"
hookAccountAddress :: Network -> ByteString -> AccountAddress
hookAccountAddress :: Network -> ByteString -> AccountAddress
hookAccountAddress Network
net ByteString
pinBs =
Network -> AccountId -> AccountAddress
AccountAddress Network
net (Credential Staking -> AccountId
AccountId (ScriptHash -> Credential Staking
forall (kr :: KeyRole). ScriptHash -> Credential kr
ScriptHashObj (ByteString -> ScriptHash
pinScriptHash ByteString
pinBs)))
keyAccountAddress :: Network -> ByteString -> AccountAddress
keyAccountAddress :: Network -> ByteString -> AccountAddress
keyAccountAddress Network
net ByteString
khBs = case ByteString -> Maybe (Hash ADDRHASH (VerKeyDSIGN DSIGN))
forall h a. HashAlgorithm h => ByteString -> Maybe (Hash h a)
hashFromBytes ByteString
khBs of
Just Hash ADDRHASH (VerKeyDSIGN DSIGN)
h ->
Network -> AccountId -> AccountAddress
AccountAddress
Network
net
( Credential Staking -> AccountId
AccountId
( KeyHash Staking -> Credential Staking
forall (kr :: KeyRole). KeyHash kr -> Credential kr
KeyHashObj
(KeyHash Payment -> KeyHash Staking
forall a b. Coercible a b => a -> b
coerce (Hash ADDRHASH (VerKeyDSIGN DSIGN) -> KeyHash Payment
forall (r :: KeyRole).
Hash ADDRHASH (VerKeyDSIGN DSIGN) -> KeyHash r
KeyHash Hash ADDRHASH (VerKeyDSIGN DSIGN)
h :: KeyHash Payment))
)
)
Maybe (Hash ADDRHASH (VerKeyDSIGN DSIGN))
Nothing ->
[Char] -> AccountAddress
forall a. HasCallStack => [Char] -> a
error
[Char]
"keyAccountAddress: key hash must be 28 bytes"
data ConsumerBinding = ConsumerBinding
{ ConsumerBinding -> ShortByteString
cbPin :: SBS.ShortByteString
, ConsumerBinding -> ShortByteString
cbScriptBytes :: SBS.ShortByteString
, ConsumerBinding -> Script ConwayEra
cbScript :: Script ConwayEra
, ConsumerBinding -> ScriptHash
cbHash :: ScriptHash
}
deriveConsumerBinding
:: SBS.ShortByteString -> ConsumerBinding
deriveConsumerBinding :: ShortByteString -> ConsumerBinding
deriveConsumerBinding ShortByteString
unapplied =
let h :: ScriptHash
h = ShortByteString -> ScriptHash
computeScriptHash ShortByteString
unapplied
in ShortByteString
-> ShortByteString
-> Script ConwayEra
-> ScriptHash
-> ConsumerBinding
ConsumerBinding
(ByteString -> ShortByteString
SBS.toShort (ScriptHash -> ByteString
scriptHashBytes ScriptHash
h))
ShortByteString
unapplied
([Char] -> ShortByteString -> Script ConwayEra
scriptFromBytes [Char]
"consumer" ShortByteString
unapplied)
ScriptHash
h
mkConsumerScript :: CageConfig -> Script ConwayEra
mkConsumerScript :: CageConfig -> Script ConwayEra
mkConsumerScript CageConfig
cfg =
[Char] -> ShortByteString -> Script ConwayEra
scriptFromBytes
[Char]
"mkConsumerScript"
(CageConfig -> ShortByteString
cfgConsumerScript CageConfig
cfg)
failedWitnessHash :: String -> Maybe String
failedWitnessHash :: [Char] -> Maybe [Char]
failedWitnessHash [Char]
s =
case [Char] -> [Char] -> Maybe [Char]
findAfter [Char]
"The script hash is:" [Char]
s of
Maybe [Char]
Nothing -> Maybe [Char]
forall a. Maybe a
Nothing
Just [Char]
rest -> case (Char -> Bool) -> [Char] -> [Char]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'"') [Char]
rest of
(Char
'"' : [Char]
after) ->
let hex :: [Char]
hex = (Char -> Bool) -> [Char] -> [Char]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile Char -> Bool
isHexDigit [Char]
after
in if [Char] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
hex Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
56 then [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
hex else Maybe [Char]
forall a. Maybe a
Nothing
[Char]
_ -> Maybe [Char]
forall a. Maybe a
Nothing
evalScriptHash :: String -> Maybe String
evalScriptHash :: [Char] -> Maybe [Char]
evalScriptHash [Char]
s =
case [Char] -> [Char] -> Maybe [Char]
findAfter [Char]
"pwcScriptHash = ScriptHash" [Char]
s of
Maybe [Char]
Nothing -> Maybe [Char]
forall a. Maybe a
Nothing
Just [Char]
rest -> case (Char -> Bool) -> [Char] -> [Char]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'"') [Char]
rest of
(Char
'"' : [Char]
after) ->
let hex :: [Char]
hex = (Char -> Bool) -> [Char] -> [Char]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile Char -> Bool
isHexDigit [Char]
after
in if [Char] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
hex Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
56 then [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
hex else Maybe [Char]
forall a. Maybe a
Nothing
[Char]
_ -> Maybe [Char]
forall a. Maybe a
Nothing
findAfter :: String -> String -> Maybe String
findAfter :: [Char] -> [Char] -> Maybe [Char]
findAfter [Char]
needle [Char]
hay =
case [ Int -> [Char] -> [Char]
forall a. Int -> [a] -> [a]
drop ([Char] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
needle) [Char]
t
| [Char]
t <- [Char] -> [[Char]]
forall a. [a] -> [[a]]
tails [Char]
hay
, [Char]
needle [Char] -> [Char] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [Char]
t
] of
([Char]
r : [[Char]]
_) -> [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
r
[] -> Maybe [Char]
forall a. Maybe a
Nothing
isBudgetFailure :: String -> Bool
isBudgetFailure :: [Char] -> Bool
isBudgetFailure [Char]
s = [Char]
"overspending the budget" [Char] -> [Char] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` [Char]
s