module Singular.Registry.Blueprint.Load
(
loadBlueprint
, NamingCodes (..)
, loadRegistryCodesFromEnv
, extractScriptHash
, extractCompiledCode
) where
import Control.Exception (ErrorCall (..), throwIO)
import Data.Aeson qualified as Aeson
import Data.ByteString qualified as BS
import Data.ByteString.Short qualified as SBS
import Data.Char (isDigit)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Word (Word8)
import Singular.Registry.Blueprint.Schema
( Blueprint (..)
, Validator (..)
)
import System.Environment (lookupEnv)
loadBlueprint
:: FilePath
-> IO (Either String Blueprint)
loadBlueprint :: String -> IO (Either String Blueprint)
loadBlueprint String
path = do
bs <- String -> IO ByteString
BS.readFile String
path
pure $ Aeson.eitherDecodeStrict' bs
extractScriptHash
:: Text
-> Blueprint
-> Maybe Text
Text
prefix Blueprint
bp =
case (Validator -> Bool) -> [Validator] -> [Validator]
forall a. (a -> Bool) -> [a] -> [a]
filter
(Text -> Text -> Bool
T.isPrefixOf Text
prefix (Text -> Bool) -> (Validator -> Text) -> Validator -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Validator -> Text
vTitle)
(Blueprint -> [Validator]
validators Blueprint
bp) of
(Validator
v : [Validator]
_) -> Text -> Maybe Text
forall a. a -> Maybe a
Just (Validator -> Text
vHash Validator
v)
[] -> Maybe Text
forall a. Maybe a
Nothing
extractCompiledCode
:: Text
-> Blueprint
-> Maybe SBS.ShortByteString
Text
prefix Blueprint
bp = do
v <-
case (Validator -> Bool) -> [Validator] -> [Validator]
forall a. (a -> Bool) -> [a] -> [a]
filter
(Text -> Text -> Bool
T.isPrefixOf Text
prefix (Text -> Bool) -> (Validator -> Text) -> Validator -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Validator -> Text
vTitle)
(Blueprint -> [Validator]
validators Blueprint
bp) of
(Validator
x : [Validator]
_) -> Validator -> Maybe Validator
forall a. a -> Maybe a
Just Validator
x
[] -> Maybe Validator
forall a. Maybe a
Nothing
hex <- vCompiledCode v
SBS.toShort <$> decodeHex hex
decodeHex :: Text -> Maybe BS.ByteString
decodeHex :: Text -> Maybe ByteString
decodeHex Text
t
| Int -> Bool
forall a. Integral a => a -> Bool
odd (Text -> Int
T.length Text
t) = Maybe ByteString
forall a. Maybe a
Nothing
| Bool
otherwise =
[Word8] -> ByteString
BS.pack ([Word8] -> ByteString) -> Maybe [Word8] -> Maybe ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Maybe [Word8]
go (Text -> String
T.unpack Text
t)
where
go :: String -> Maybe [Word8]
go [] = [Word8] -> Maybe [Word8]
forall a. a -> Maybe a
Just []
go (Char
a : Char
b : String
rest) = do
hi <- Char -> Maybe Word8
hexDigit Char
a
lo <- hexDigit b
(hi * 16 + lo :) <$> go rest
go String
_ = Maybe [Word8]
forall a. Maybe a
Nothing
hexDigit :: Char -> Maybe Word8
hexDigit :: Char -> Maybe Word8
hexDigit Char
c
| Char -> Bool
isDigit Char
c =
Word8 -> Maybe Word8
forall a. a -> Maybe a
Just (Word8 -> Maybe Word8) -> Word8 -> Maybe Word8
forall a b. (a -> b) -> a -> b
$
Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral
(Char -> Int
forall a. Enum a => a -> Int
fromEnum Char
c Int -> Int -> Int
forall a. Num a => a -> a -> a
- Char -> Int
forall a. Enum a => a -> Int
fromEnum Char
'0')
| Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'a' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'f' =
Word8 -> Maybe Word8
forall a. a -> Maybe a
Just (Word8 -> Maybe Word8) -> Word8 -> Maybe Word8
forall a b. (a -> b) -> a -> b
$
Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral
( Char -> Int
forall a. Enum a => a -> Int
fromEnum Char
c
Int -> Int -> Int
forall a. Num a => a -> a -> a
- Char -> Int
forall a. Enum a => a -> Int
fromEnum Char
'a'
Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
10
)
| Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'A' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'F' =
Word8 -> Maybe Word8
forall a. a -> Maybe a
Just (Word8 -> Maybe Word8) -> Word8 -> Maybe Word8
forall a b. (a -> b) -> a -> b
$
Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral
( Char -> Int
forall a. Enum a => a -> Int
fromEnum Char
c
Int -> Int -> Int
forall a. Num a => a -> a -> a
- Char -> Int
forall a. Enum a => a -> Int
fromEnum Char
'A'
Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
10
)
| Bool
otherwise = Maybe Word8
forall a. Maybe a
Nothing
data NamingCodes = NamingCodes
{ NamingCodes -> ShortByteString
ncApplication :: SBS.ShortByteString
, NamingCodes -> ShortByteString
ncWitness :: SBS.ShortByteString
}
loadRegistryCodesFromEnv :: IO NamingCodes
loadRegistryCodesFromEnv :: IO NamingCodes
loadRegistryCodesFromEnv = do
mPath <- String -> IO (Maybe String)
lookupEnv String
"REGISTRY_BLUEPRINT"
path <- case mPath of
Just String
p | Bool -> Bool
not (String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
p) -> String -> IO String
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
p
Maybe String
_ -> String -> IO String
forall a. String -> IO a
die String
"REGISTRY_BLUEPRINT is not set"
ebp <- loadBlueprint path
bp <- case ebp of
Left String
err -> String -> IO Blueprint
forall a. String -> IO a
die (String
"registry blueprint does not parse: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
err)
Right Blueprint
bp -> Blueprint -> IO Blueprint
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Blueprint
bp
case ( extractCompiledCode "open.open" bp
, extractCompiledCode "witness.witness" bp
) of
(Just ShortByteString
openCode, Just ShortByteString
witnessCode) ->
NamingCodes -> IO NamingCodes
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure NamingCodes{ncApplication :: ShortByteString
ncApplication = ShortByteString
openCode, ncWitness :: ShortByteString
ncWitness = ShortByteString
witnessCode}
(Maybe ShortByteString
Nothing, Maybe ShortByteString
_) ->
String -> IO NamingCodes
forall a. String -> IO a
die
String
"registry blueprint has no open.open code: the open \
\application is not in the registry partition (#173 I1)"
(Maybe ShortByteString
_, Maybe ShortByteString
Nothing) ->
String -> IO NamingCodes
forall a. String -> IO a
die
String
"registry blueprint has no witness.witness code: the three \
\witness policies have not moved here (#173 I2)"
where
die :: String -> IO a
die :: forall a. String -> IO a
die = ErrorCall -> IO a
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO (ErrorCall -> IO a) -> (String -> ErrorCall) -> String -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ErrorCall
ErrorCall