biscuit-haskell-0.5.0.0: src/Auth/Biscuit/ProtoBufAdapter.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-|
Module : Auth.Biscuit.Utils
Copyright : © Clément Delafargue, 2021
License : BSD-3-Clause
Maintainer : clement@delafargue.name
Conversion functions between biscuit components and protobuf-encoded components
-}
module Auth.Biscuit.ProtoBufAdapter
( Symbols
, buildSymbolTable
, pbToBlock
, blockToPb
, pbToSignedBlock
, signedBlockToPb
, pbToProof
, pbToThirdPartyBlockRequest
, thirdPartyBlockRequestToPb
, pbToThirdPartyBlockContents
, thirdPartyBlockContentsToPb
) where
import Control.Monad (unless, when)
import Control.Monad.State (StateT, get, lift, modify)
import Data.Bitraversable (bitraverse)
import Data.ByteString (ByteString)
import Data.Int (Int64)
import qualified Data.List.NonEmpty as NE
import qualified Data.Map as Map
import Data.Maybe (isJust, isNothing)
import qualified Data.Set as Set
import qualified Data.Text as T
import Data.Time (UTCTime)
import Data.Time.Clock.POSIX (posixSecondsToUTCTime,
utcTimeToPOSIXSeconds)
import Data.Void (absurd)
import GHC.Records (getField)
import Validation (Validation (..))
import qualified Auth.Biscuit.Crypto as Crypto
import Auth.Biscuit.Datalog.AST
import qualified Auth.Biscuit.Proto as PB
import Auth.Biscuit.Symbols
import Auth.Biscuit.Utils (maybeToRight)
buildSymbolTable :: Symbols -> Block -> BlockSymbols
buildSymbolTable existingSymbols block =
let allSymbols = listSymbolsInBlock block
allKeys = listPublicKeysInBlock block
in addSymbols existingSymbols allSymbols allKeys
pbToPublicKey :: PB.PublicKey -> Either String Crypto.PublicKey
pbToPublicKey PB.PublicKey{..} =
let keyBytes = PB.getField key
parseKey = Crypto.readEd25519PublicKey
in case PB.getField algorithm of
PB.Ed25519 -> maybeToRight "Invalid ed25519 public key" $ parseKey keyBytes
pbToOptionalSignature :: PB.ExternalSig -> Either String (Crypto.Signature, Crypto.PublicKey)
pbToOptionalSignature PB.ExternalSig{..} = do
let sig = Crypto.signature $ PB.getField signature
pk <- pbToPublicKey $ PB.getField publicKey
pure (sig, pk)
-- | Parse a protobuf signed block into a signed biscuit block
pbToSignedBlock :: PB.SignedBlock -> Either String Crypto.SignedBlock
pbToSignedBlock PB.SignedBlock{..} = do
let sig = Crypto.signature $ PB.getField signature
mSig <- traverse pbToOptionalSignature $ PB.getField externalSig
pk <- pbToPublicKey $ PB.getField nextKey
let sigVersion = fromIntegral <$> PB.getField version
pure ( PB.getField block
, sig
, pk
, mSig
, sigVersion
)
publicKeyToPb :: Crypto.PublicKey -> PB.PublicKey
publicKeyToPb pk = PB.PublicKey
{ algorithm = PB.putField PB.Ed25519
, key = PB.putField $ Crypto.pkBytes pk
}
externalSigToPb :: (Crypto.Signature, Crypto.PublicKey) -> PB.ExternalSig
externalSigToPb (sig, pk) = PB.ExternalSig
{ signature = PB.putField $ Crypto.sigBytes sig
, publicKey = PB.putField $ publicKeyToPb pk
}
signedBlockToPb :: Crypto.SignedBlock -> PB.SignedBlock
signedBlockToPb (block, sig, pk, eSig, sigVersion) = PB.SignedBlock
{ block = PB.putField block
, signature = PB.putField $ Crypto.sigBytes sig
, nextKey = PB.putField $ publicKeyToPb pk
, externalSig = PB.putField $ externalSigToPb <$> eSig
, version = PB.putField $ fromIntegral <$> sigVersion
}
pbToProof :: PB.Proof -> Either String (Either Crypto.Signature Crypto.SecretKey)
pbToProof (PB.ProofSignature rawSig) = Left <$> Right (Crypto.signature $ PB.getField rawSig)
pbToProof (PB.ProofSecret rawPk) = Right <$> maybeToRight "Invalid public key proof" (Crypto.readEd25519SecretKey $ PB.getField rawPk)
pbToBlock :: Maybe Crypto.PublicKey -> PB.Block -> StateT Symbols (Either String) Block
pbToBlock ePk PB.Block{..} = do
blockPks <- lift $ traverse pbToPublicKey $ PB.getField pksTable
let blockSymbols = PB.getField symbols
-- third party blocks use an isolated symbol table,
-- but use the global public keys table:
-- symbols defined in 3rd party blocks are not visible
-- to following blocks, but public keys are
when (isNothing ePk) $ do
modify (registerNewSymbols blockSymbols)
modify (registerNewPublicKeys blockPks)
currentSymbols <- get
let symbolsForCurrentBlock =
-- third party blocks use an isolated symbol and public keys table,
-- 3rd party blocks don't see previously defined
-- symbols or public keys
if isNothing ePk then currentSymbols
else registerNewPublicKeys blockPks $ registerNewSymbols blockSymbols newSymbolTable
let bContext = PB.getField context
bVersion = PB.getField version
lift $ do
let s = symbolsForCurrentBlock
bFacts <- traverse (pbToFact s) $ PB.getField facts
bRules <- traverse (pbToRule s) $ PB.getField rules
bChecks <- traverse (pbToCheck s) $ PB.getField checks
bScope <- Set.fromList <$> traverse (pbToScope s) (PB.getField scope)
let v6Plus = or
[ any isReject bChecks
, not (all predicateHasNoV6Values bFacts)
, not (all ruleHasNoV6Values bRules)
, not (all checkHasNoV6Values bChecks)
]
v5Plus = isJust ePk
v4Plus = not $ and
[ Set.null bScope
, all ruleHasNoScope bRules
, all (queryHasNoScope . cQueries) bChecks
, all isCheckOne bChecks
, all ruleHasNoV4Operators bRules
, all (queryHasNoV4Operators . cQueries) bChecks
]
case (bVersion, v4Plus, v5Plus, v6Plus) of
(Just 6, _, _, _) -> pure Block {..}
(Just 5, _, _, True) ->
Left "Biscuit v6 features are present, but the block version is 5."
(Just 5, _, _, _) -> pure Block {..}
(Just 4, _, False, False) -> pure Block {..}
(Just 4, _, _, True) ->
Left "Biscuit v6 features are present, but the block version is 4."
(Just 4, _, True, False) ->
Left "Biscuit v5 features are present, but the block version is 4."
(Just 3, False, False, False) -> pure Block {..}
(Just 3, True, False, False) ->
Left "Biscuit v4 features are present, but the block version is 3."
(Just 3, _, True, False) ->
Left "Biscuit v5 features are present, but the block version is 3."
(Just 3, _, _, True) ->
Left "Biscuit v6 features are present, but the block version is 3."
_ ->
Left $ "Unsupported biscuit version: " <> maybe "0" show bVersion <> ". Only versions 3 to 6 are supported"
-- | Turn a biscuit block into a protobuf block, for serialization,
-- along with the newly defined symbols
blockToPb :: Bool -> Symbols -> Block -> ((BlockSymbols, Int), PB.Block)
blockToPb hasExternalPk existingSymbols b@Block{..} =
let v4Plus = not $ and
[Set.null bScope
, all ruleHasNoScope bRules
, all (queryHasNoScope . cQueries) bChecks
, all isCheckOne bChecks
, all ruleHasNoV4Operators bRules
, all (queryHasNoV4Operators . cQueries) bChecks
]
v5Plus = hasExternalPk
v6Plus = or
[ any isReject bChecks
, not (all predicateHasNoV6Values bFacts)
, not (all ruleHasNoV6Values bRules)
, not (all checkHasNoV6Values bChecks)
]
bSymbols = buildSymbolTable existingSymbols b
s = reverseSymbols $ addFromBlock existingSymbols bSymbols
symbols = PB.putField $ getSymbolList bSymbols
context = PB.putField bContext
facts = PB.putField $ factToPb s <$> bFacts
rules = PB.putField $ ruleToPb s <$> bRules
checks = PB.putField $ checkToPb s <$> bChecks
scope = PB.putField $ scopeToPb s <$> Set.toList bScope
pksTable = PB.putField $ publicKeyToPb <$> getPkList bSymbols
version = if | v6Plus -> 6
| v5Plus -> 5
| v4Plus -> 4
| otherwise -> 3
in ((bSymbols, version), PB.Block {version = PB.putField $ Just $ fromIntegral version, ..})
pbToFact :: Symbols -> PB.Fact -> Either String Fact
pbToFact s PB.Fact{predicate} = do
let pbName = PB.getField $ PB.name $ PB.getField predicate
pbTerms = PB.getField $ PB.terms $ PB.getField predicate
name <- getSymbol s $ SymbolRef pbName
terms <- traverse (pbToValue s) pbTerms
pure Predicate{..}
factToPb :: ReverseSymbols -> Fact -> PB.Fact
factToPb s Predicate{..} =
let
predicate = PB.Predicate
{ name = PB.putField $ getSymbolRef $ getSymbolCode s name
, terms = PB.putField $ valueToPb s <$> terms
}
in PB.Fact{predicate = PB.putField predicate}
pbToRule :: Symbols -> PB.Rule -> Either String Rule
pbToRule s pbRule = do
let pbHead = PB.getField $ PB.head pbRule
pbBody = PB.getField $ PB.body pbRule
pbExpressions = PB.getField $ PB.expressions pbRule
pbScope = PB.getField $ getField @"scope" pbRule
rhead <- pbToPredicate s pbHead
body <- traverse (pbToPredicate s) pbBody
expressions <- traverse (pbToExpression s) pbExpressions
scope <- Set.fromList <$> traverse (pbToScope s) pbScope
case makeRule rhead body expressions scope of
Failure vs -> Left $ "Unbound variables in rule: " <> T.unpack (T.intercalate ", " $ NE.toList vs)
Success r -> pure r
ruleToPb :: ReverseSymbols -> Rule -> PB.Rule
ruleToPb s Rule{..} =
PB.Rule
{ head = PB.putField $ predicateToPb s rhead
, body = PB.putField $ predicateToPb s <$> body
, expressions = PB.putField $ expressionToPb s <$> expressions
, scope = PB.putField $ scopeToPb s <$> Set.toList scope
}
pbToCheck :: Symbols -> PB.Check -> Either String Check
pbToCheck s PB.Check{queries,kind} = do
let toCheck Rule{body,expressions,scope} = QueryItem{qBody = body, qExpressions = expressions, qScope = scope}
rules <- traverse (pbToRule s) $ PB.getField queries
let cQueries = toCheck <$> rules
let cKind = case PB.getField kind of
Just PB.CheckAll -> CheckAll
Just PB.CheckOne -> CheckOne
Just PB.Reject -> Reject
Nothing -> CheckOne
pure Check{..}
checkToPb :: ReverseSymbols -> Check -> PB.Check
checkToPb s Check{..} =
let dummyHead = Predicate "query" []
toQuery QueryItem{..} =
ruleToPb s $ Rule { rhead = dummyHead
, body = qBody
, expressions = qExpressions
, scope = qScope
}
pbKind = case cKind of
CheckOne -> Nothing
CheckAll -> Just PB.CheckAll
Reject -> Just PB.Reject
in PB.Check { queries = PB.putField $ toQuery <$> cQueries
, kind = PB.putField pbKind
}
pbToScope :: Symbols -> PB.Scope -> Either String RuleScope
pbToScope s = \case
PB.ScType e -> case PB.getField e of
PB.ScopeAuthority -> Right OnlyAuthority
PB.ScopePrevious -> Right Previous
PB.ScBlock pkRef ->
BlockId <$> getPublicKey' s (PublicKeyRef $ PB.getField pkRef)
scopeToPb :: ReverseSymbols -> RuleScope -> PB.Scope
scopeToPb s = \case
OnlyAuthority -> PB.ScType $ PB.putField PB.ScopeAuthority
Previous -> PB.ScType $ PB.putField PB.ScopePrevious
BlockId pk -> PB.ScBlock $ PB.putField $ getPublicKeyCode s pk
pbToPredicate :: Symbols -> PB.Predicate -> Either String (Predicate' 'InPredicate 'Representation)
pbToPredicate s pbPredicate = do
let pbName = PB.getField $ PB.name pbPredicate
pbTerms = PB.getField $ PB.terms pbPredicate
name <- getSymbol s $ SymbolRef pbName
terms <- traverse (pbToTerm s) pbTerms
pure Predicate{..}
predicateToPb :: ReverseSymbols -> Predicate -> PB.Predicate
predicateToPb s Predicate{..} =
PB.Predicate
{ name = PB.putField $ getSymbolRef $ getSymbolCode s name
, terms = PB.putField $ termToPb s <$> terms
}
pbTimeToUtcTime :: Int64 -> UTCTime
pbTimeToUtcTime = posixSecondsToUTCTime . fromIntegral
pbToTerm :: Symbols -> PB.Term -> Either String Term
pbToTerm s = \case
PB.TermInteger f -> pure $ LInteger $ fromIntegral $ PB.getField f
PB.TermString f -> LString <$> getSymbol s (SymbolRef $ PB.getField f)
PB.TermDate f -> pure $ LDate $ pbTimeToUtcTime $ PB.getField f
PB.TermBytes f -> pure $ LBytes $ PB.getField f
PB.TermBool f -> pure $ LBool $ PB.getField f
PB.TermVariable f -> Variable <$> getSymbol s (SymbolRef $ PB.getField f)
PB.TermTermSet f -> TermSet . Set.fromList <$> traverse (pbToSetValue s) (PB.getField . PB.set $ PB.getField f)
PB.TermTermArray f -> TermArray <$> traverse (pbToValue s) (PB.getField . PB.array $ PB.getField f)
PB.TermTermMap f -> TermMap . Map.fromList <$> traverse (pbToMapEntry s) (PB.getField . PB.map $ PB.getField f)
PB.TermNull _ -> pure LNull
termToPb :: ReverseSymbols -> Term -> PB.Term
termToPb s = \case
Variable n -> PB.TermVariable $ PB.putField $ getSymbolRef $ getSymbolCode s n
LInteger v -> PB.TermInteger $ PB.putField $ fromIntegral v
LString v -> PB.TermString $ PB.putField $ getSymbolRef $ getSymbolCode s v
LDate v -> PB.TermDate $ PB.putField $ round $ utcTimeToPOSIXSeconds v
LBytes v -> PB.TermBytes $ PB.putField v
LBool v -> PB.TermBool $ PB.putField v
TermSet vs -> PB.TermTermSet $ PB.putField $ PB.TermSet $ PB.putField $ setValueToPb s <$> Set.toList vs
TermArray vs -> PB.TermTermArray $ PB.putField $ PB.TermArray $ PB.putField $ valueToPb s <$> vs
TermMap vs -> PB.TermTermMap $ PB.putField $ PB.TermMap $ PB.putField $ uncurry (mapEntryToPb s) <$> Map.toList vs
LNull -> PB.TermNull $ PB.putField $ PB.Empty {}
Antiquote v -> absurd v
mapEntryToPb :: ReverseSymbols -> MapKey -> Value -> PB.MapEntry
mapEntryToPb s k v = PB.MapEntry
{ key = PB.putField $ case k of
IntKey i -> PB.MapKeyInt . PB.putField $ fromIntegral i
StringKey n -> PB.MapKeyString . PB.putField $ getSymbolRef $ getSymbolCode s n
, value = PB.putField $ valueToPb s v
}
pbToMapEntry :: Symbols -> PB.MapEntry -> Either String (MapKey, Value)
pbToMapEntry s PB.MapEntry{key,value} = do
k <- case PB.getField key of
PB.MapKeyInt i -> pure . IntKey . fromIntegral $ PB.getField i
PB.MapKeyString i -> StringKey <$> getSymbol s (SymbolRef $ PB.getField i)
v <- pbToValue s $ PB.getField value
pure (k, v)
pbToValue :: Symbols -> PB.Term -> Either String Value
pbToValue s = \case
PB.TermInteger f -> pure $ LInteger $ fromIntegral $ PB.getField f
PB.TermString f -> LString <$> getSymbol s (SymbolRef $ PB.getField f)
PB.TermDate f -> pure $ LDate $ pbTimeToUtcTime $ PB.getField f
PB.TermBytes f -> pure $ LBytes $ PB.getField f
PB.TermBool f -> pure $ LBool $ PB.getField f
PB.TermVariable _ -> Left "Variables can't appear in facts"
PB.TermTermSet f -> TermSet . Set.fromList <$> traverse (pbToSetValue s) (PB.getField . PB.set $ PB.getField f)
PB.TermTermArray f -> TermArray <$> traverse (pbToValue s) (PB.getField . PB.array $ PB.getField f)
PB.TermTermMap f -> TermMap . Map.fromList <$> traverse (pbToMapEntry s) (PB.getField . PB.map $ PB.getField f)
PB.TermNull _ -> pure LNull
valueToPb :: ReverseSymbols -> Value -> PB.Term
valueToPb s = \case
LInteger v -> PB.TermInteger $ PB.putField $ fromIntegral v
LString v -> PB.TermString $ PB.putField $ getSymbolRef $ getSymbolCode s v
LDate v -> PB.TermDate $ PB.putField $ round $ utcTimeToPOSIXSeconds v
LBytes v -> PB.TermBytes $ PB.putField v
LBool v -> PB.TermBool $ PB.putField v
TermSet vs -> PB.TermTermSet $ PB.putField $ PB.TermSet $ PB.putField $ setValueToPb s <$> Set.toList vs
TermArray vs -> PB.TermTermArray $ PB.putField $ PB.TermArray $ PB.putField $ valueToPb s <$> vs
TermMap vs -> PB.TermTermMap $ PB.putField $ PB.TermMap $ PB.putField $ uncurry (mapEntryToPb s) <$> Map.toList vs
LNull -> PB.TermNull $ PB.putField PB.Empty
Variable v -> absurd v
Antiquote v -> absurd v
pbToSetValue :: Symbols -> PB.Term -> Either String (Term' 'WithinSet 'InFact 'Representation)
pbToSetValue s = \case
PB.TermInteger f -> pure $ LInteger $ fromIntegral $ PB.getField f
PB.TermString f -> LString <$> getSymbol s (SymbolRef $ PB.getField f)
PB.TermDate f -> pure $ LDate $ pbTimeToUtcTime $ PB.getField f
PB.TermBytes f -> pure $ LBytes $ PB.getField f
PB.TermBool f -> pure $ LBool $ PB.getField f
PB.TermNull _ -> pure LNull
PB.TermVariable _ -> Left "Variables can't appear in facts or sets"
PB.TermTermSet _ -> Left "Sets can't be nested"
PB.TermTermArray _ -> Left "Arrays can’t appear in sets"
PB.TermTermMap _ -> Left "Maps can’t appear in sets"
setValueToPb :: ReverseSymbols -> Term' 'WithinSet 'InFact 'Representation -> PB.Term
setValueToPb s = \case
LInteger v -> PB.TermInteger $ PB.putField $ fromIntegral v
LString v -> PB.TermString $ PB.putField $ getSymbolRef $ getSymbolCode s v
LDate v -> PB.TermDate $ PB.putField $ round $ utcTimeToPOSIXSeconds v
LBytes v -> PB.TermBytes $ PB.putField v
LBool v -> PB.TermBool $ PB.putField v
LNull -> PB.TermNull $ PB.putField $ PB.Empty {}
TermSet v -> absurd v
TermArray v -> absurd v
TermMap v -> absurd v
Variable v -> absurd v
Antiquote v -> absurd v
pbToExpression :: Symbols -> PB.Expression -> Either String Expression
pbToExpression s PB.Expression{ops} = do
parsedOps <- traverse (pbToOp s) $ PB.getField ops
fromStack parsedOps
expressionToPb :: ReverseSymbols -> Expression -> PB.Expression
expressionToPb s e =
let ops = opToPb s <$> toStack e
in PB.Expression { ops = PB.putField ops }
pbToOp :: Symbols -> PB.Op -> Either String Op
pbToOp s = \case
PB.OpVValue v -> VOp <$> pbToTerm s (PB.getField v)
PB.OpVUnary v -> UOp <$> pbToUnary s (PB.getField v)
PB.OpVBinary v -> BOp <$> pbToBinary s (PB.getField v)
PB.OpVClosure v -> uncurry COp <$> pbToClosure s (PB.getField v)
opToPb :: ReverseSymbols -> Op -> PB.Op
opToPb s = \case
VOp t -> PB.OpVValue $ PB.putField $ termToPb s t
UOp o -> PB.OpVUnary $ PB.putField $ unaryToPb s o
BOp o -> PB.OpVBinary $ PB.putField $ binaryToPb s o
COp p os -> PB.OpVClosure $ PB.putField $ closureToPb s p os
pbToUnary :: Symbols -> PB.OpUnary -> Either String Unary
pbToUnary s PB.OpUnary{kind,ffiName} =
let noFfi = case PB.getField ffiName of
Just _ -> const $ Left "FFI named set on a regular operation "
Nothing -> Right
in case PB.getField kind of
PB.Negate -> noFfi Negate
PB.Parens -> noFfi Parens
PB.Length -> noFfi Length
PB.TypeOf -> noFfi TypeOf
PB.UnaryFfi -> do
nameIdx <- maybeToRight "Missing extern call name" $ PB.getField ffiName
name' <- getSymbol s $ SymbolRef nameIdx
pure $ UnaryFfi name'
unaryToPb :: ReverseSymbols -> Unary -> PB.OpUnary
unaryToPb s = \case
Negate -> PB.OpUnary { kind = PB.putField PB.Negate, ffiName = PB.putField Nothing }
Parens -> PB.OpUnary { kind = PB.putField PB.Parens, ffiName = PB.putField Nothing }
Length -> PB.OpUnary { kind = PB.putField PB.Length, ffiName = PB.putField Nothing }
TypeOf -> PB.OpUnary { kind = PB.putField PB.TypeOf, ffiName = PB.putField Nothing }
UnaryFfi name ->
PB.OpUnary {
kind = PB.putField PB.UnaryFfi,
ffiName = PB.putField . Just . getSymbolRef $ getSymbolCode s name
}
pbToBinary :: Symbols -> PB.OpBinary -> Either String Binary
pbToBinary s PB.OpBinary{kind, ffiName} =
let noFfi = case PB.getField ffiName of
Just _ -> const $ Left "FFI named set on a regular operation "
Nothing -> Right
in case PB.getField kind of
PB.LessThan -> noFfi LessThan
PB.GreaterThan -> noFfi GreaterThan
PB.LessOrEqual -> noFfi LessOrEqual
PB.GreaterOrEqual -> noFfi GreaterOrEqual
PB.Equal -> noFfi Equal
PB.Contains -> noFfi Contains
PB.Prefix -> noFfi Prefix
PB.Suffix -> noFfi Suffix
PB.Regex -> noFfi Regex
PB.Add -> noFfi Add
PB.Sub -> noFfi Sub
PB.Mul -> noFfi Mul
PB.Div -> noFfi Div
PB.And -> noFfi And
PB.Or -> noFfi Or
PB.Intersection -> noFfi Intersection
PB.Union -> noFfi Union
PB.BitwiseAnd -> noFfi BitwiseAnd
PB.BitwiseOr -> noFfi BitwiseOr
PB.BitwiseXor -> noFfi BitwiseXor
PB.NotEqual -> noFfi NotEqual
PB.HeterogeneousEqual -> noFfi HeterogeneousEqual
PB.HeterogeneousNotEqual -> noFfi HeterogeneousNotEqual
PB.LazyAnd -> noFfi LazyAnd
PB.LazyOr -> noFfi LazyOr
PB.All -> noFfi All
PB.Any -> noFfi Any
PB.Get -> noFfi Get
PB.TryOr -> noFfi Try
PB.BinaryFfi -> do
nameIdx <- maybeToRight "Missing extern call name" $ PB.getField ffiName
name' <- getSymbol s $ SymbolRef nameIdx
pure $ BinaryFfi name'
pbToClosure :: Symbols -> PB.OpClosure -> Either String ([T.Text], [Op])
pbToClosure s PB.OpClosure{..} =
let getParams = traverse (getSymbol s . SymbolRef) . PB.getField
getOps = traverse (pbToOp s) . PB.getField
in bitraverse getParams getOps (params, ops)
closureToPb :: ReverseSymbols -> [T.Text] -> [Op] -> PB.OpClosure
closureToPb s params' ops' =
let params = PB.putField $ fmap (getSymbolRef . getSymbolCode s) params'
ops = PB.putField $ fmap (opToPb s) ops'
in PB.OpClosure{..}
binaryToPb :: ReverseSymbols -> Binary -> PB.OpBinary
binaryToPb s = \case
LessThan -> PB.OpBinary { kind = PB.putField PB.LessThan, ffiName = PB.putField Nothing }
GreaterThan -> PB.OpBinary { kind = PB.putField PB.GreaterThan, ffiName = PB.putField Nothing }
LessOrEqual -> PB.OpBinary { kind = PB.putField PB.LessOrEqual, ffiName = PB.putField Nothing }
GreaterOrEqual -> PB.OpBinary { kind = PB.putField PB.GreaterOrEqual, ffiName = PB.putField Nothing }
Equal -> PB.OpBinary { kind = PB.putField PB.Equal, ffiName = PB.putField Nothing }
Contains -> PB.OpBinary { kind = PB.putField PB.Contains, ffiName = PB.putField Nothing }
Prefix -> PB.OpBinary { kind = PB.putField PB.Prefix, ffiName = PB.putField Nothing }
Suffix -> PB.OpBinary { kind = PB.putField PB.Suffix, ffiName = PB.putField Nothing }
Regex -> PB.OpBinary { kind = PB.putField PB.Regex, ffiName = PB.putField Nothing }
Add -> PB.OpBinary { kind = PB.putField PB.Add, ffiName = PB.putField Nothing }
Sub -> PB.OpBinary { kind = PB.putField PB.Sub, ffiName = PB.putField Nothing }
Mul -> PB.OpBinary { kind = PB.putField PB.Mul, ffiName = PB.putField Nothing }
Div -> PB.OpBinary { kind = PB.putField PB.Div, ffiName = PB.putField Nothing }
And -> PB.OpBinary { kind = PB.putField PB.And, ffiName = PB.putField Nothing }
Or -> PB.OpBinary { kind = PB.putField PB.Or, ffiName = PB.putField Nothing }
Intersection -> PB.OpBinary { kind = PB.putField PB.Intersection, ffiName = PB.putField Nothing }
Union -> PB.OpBinary { kind = PB.putField PB.Union, ffiName = PB.putField Nothing }
BitwiseAnd -> PB.OpBinary { kind = PB.putField PB.BitwiseAnd, ffiName = PB.putField Nothing }
BitwiseOr -> PB.OpBinary { kind = PB.putField PB.BitwiseOr, ffiName = PB.putField Nothing }
BitwiseXor -> PB.OpBinary { kind = PB.putField PB.BitwiseXor, ffiName = PB.putField Nothing }
NotEqual -> PB.OpBinary { kind = PB.putField PB.NotEqual, ffiName = PB.putField Nothing }
HeterogeneousEqual -> PB.OpBinary { kind = PB.putField PB.HeterogeneousEqual, ffiName = PB.putField Nothing }
HeterogeneousNotEqual -> PB.OpBinary { kind = PB.putField PB.HeterogeneousNotEqual, ffiName = PB.putField Nothing }
LazyAnd ->PB.OpBinary { kind = PB.putField PB.LazyAnd, ffiName = PB.putField Nothing }
LazyOr -> PB.OpBinary { kind = PB.putField PB.LazyOr, ffiName = PB.putField Nothing }
Any -> PB.OpBinary { kind = PB.putField PB.Any, ffiName = PB.putField Nothing }
All -> PB.OpBinary { kind = PB.putField PB.All, ffiName = PB.putField Nothing }
Get -> PB.OpBinary { kind = PB.putField PB.Get, ffiName = PB.putField Nothing }
Try -> PB.OpBinary { kind = PB.putField PB.TryOr, ffiName = PB.putField Nothing }
BinaryFfi n -> PB.OpBinary
{ kind = PB.putField PB.BinaryFfi
, ffiName = PB.putField . Just . getSymbolRef $ getSymbolCode s n
}
pbToThirdPartyBlockRequest :: PB.ThirdPartyBlockRequest -> Either String Crypto.Signature
pbToThirdPartyBlockRequest PB.ThirdPartyBlockRequest{legacyPk, pkTable, prevSig} = do
unless (isNothing $ PB.getField legacyPk) $ Left "Public key provided in third-party block request"
unless (null $ PB.getField pkTable) $ Left "Public key table provided in third-party block request"
pure . Crypto.signature $ PB.getField prevSig
thirdPartyBlockRequestToPb :: Crypto.Signature -> PB.ThirdPartyBlockRequest
thirdPartyBlockRequestToPb prevSig = PB.ThirdPartyBlockRequest
{ legacyPk = PB.putField Nothing
, pkTable = PB.putField []
, prevSig = PB.putField $ Crypto.sigBytes prevSig
}
pbToThirdPartyBlockContents :: PB.ThirdPartyBlockContents -> Either String (ByteString, Crypto.Signature, Crypto.PublicKey)
pbToThirdPartyBlockContents PB.ThirdPartyBlockContents{payload,externalSig} = do
(sig, pk) <- pbToOptionalSignature $ PB.getField externalSig
pure ( PB.getField payload
, sig
, pk
)
thirdPartyBlockContentsToPb :: (ByteString, Crypto.Signature, Crypto.PublicKey) -> PB.ThirdPartyBlockContents
thirdPartyBlockContentsToPb (payload, sig, pk) = PB.ThirdPartyBlockContents
{ PB.payload = PB.putField payload
, PB.externalSig = PB.putField $ externalSigToPb (sig, pk)
}