99{-# LANGUAGE QuantifiedConstraints #-}
1010{-# LANGUAGE RankNTypes #-}
1111{-# LANGUAGE ScopedTypeVariables #-}
12+ {-# LANGUAGE StandaloneDeriving #-}
1213{-# LANGUAGE TupleSections #-}
1314{-# LANGUAGE TypeApplications #-}
1415{-# LANGUAGE TypeFamilies #-}
2021
2122module Main (main ) where
2223
23- import Control.Monad (forM_ )
24+ import Control.Monad (forM , forM_ , unless )
2425import Control.Monad.Except (ExceptT (.. ), runExceptT )
2526
2627import Codec.CBOR.Read qualified as CBOR
27- -- import Codec.CBOR.Term (Term (..))
28+ import Codec.CBOR.Term (Term (.. ))
2829import Codec.CBOR.Term qualified as CBOR
29- -- import Codec.CBOR.Write qualified as CBOR
30+ import Codec.CBOR.Write qualified as CBOR
3031-- import Codec.Serialise.Class (Serialise)
3132-- import Codec.Serialise.Class qualified as Serialise
3233import Codec.Serialise.Decoding qualified as CBOR
3334-- import Codec.Serialise.Encoding qualified as CBOR
3435
3536import Data.Bool (bool )
36- import Data.ByteString.Base16.Lazy as BL.Base16
37+ import Data.ByteString.Base16.Lazy qualified as BL.Base16
3738import Data.ByteString.Lazy qualified as BL
3839import Data.ByteString.Lazy.Char8 qualified as BL.Char8
40+ import Text.Printf
3941
4042import System.Directory (doesDirectoryExist )
4143import System.Environment (setEnv )
42- import System.Exit (ExitCode (.. ))
44+ import System.Exit (ExitCode (.. ), die )
4345import System.FilePath
4446import System.IO (hClose )
4547import System.IO.Temp (withTempFile )
4648import System.Process.ByteString.Lazy
4749
50+ import Network.TypedProtocol.Codec
51+
4852import Cardano.KESAgent.Protocols.StandardCrypto (StandardCrypto )
4953
54+ import DMQ.Protocol.LocalMsgNotification.Codec
55+ import DMQ.Protocol.LocalMsgNotification.Type as LocalMsgNotification
5056import DMQ.Protocol.SigSubmission.Codec
5157import DMQ.Protocol.SigSubmission.Type
5258
@@ -62,29 +68,58 @@ main = do
6268 defaultMain (tests cddlSpecs)
6369
6470tests :: CDDLSpecs -> TestTree
65- tests CDDLSpecs { cddlSig
71+ tests CDDLSpecs { cddlSig,
72+ cddlLocalMsgNotification
6673 } =
6774 adjustOption (const $ QuickCheckMaxSize 10 ) $
6875 testGroup " cddl"
6976 [ testGroup " decoding"
7077 -- validate decoder by generating messages from the specification
7178 [ testCase " Sig" (unit_decodeSig cddlSig)
79+ , testCase " LocalMsgNotification" (unit_decodeLocalMsgNotification cddlLocalMsgNotification)
7280 ]
81+ -- TODO: validate `LocalMsgNotification` encoder
7382 ]
7483
7584newtype CDDLSpec ps = CDDLSpec BL. ByteString
7685
7786type AnnSigRawWithSignedBytes = BL. ByteString -> SigRawWithSignedBytes StandardCrypto
7887
7988data CDDLSpecs = CDDLSpecs {
80- cddlSig :: CDDLSpec AnnSigRawWithSignedBytes
89+ cddlSig :: CDDLSpec AnnSigRawWithSignedBytes ,
90+ cddlLocalMsgNotification :: CDDLSpec (LocalMsgNotification (Sig StandardCrypto ))
8191 }
8292
8393
8494unit_decodeSig :: CDDLSpec AnnSigRawWithSignedBytes
8595 -> Assertion
8696unit_decodeSig spec = validateDecoder spec decodeSig 100
8797
98+ unit_decodeLocalMsgNotification :: CDDLSpec (LocalMsgNotification (Sig StandardCrypto ))
99+ -> Assertion
100+ unit_decodeLocalMsgNotification spec =
101+ validateAnnotatedDecoder
102+ (Just fix)
103+ spec
104+ codecLocalMsgNotification
105+ [ SomeAgency LocalMsgNotification. SingIdle
106+ , SomeAgency $ LocalMsgNotification. SingBusy LocalMsgNotification. SingBlocking
107+ , SomeAgency $ LocalMsgNotification. SingBusy LocalMsgNotification. SingNonBlocking
108+ ]
109+ 100
110+ where
111+ -- | The cddl spec cannot differentiate between fix-length list encoding and
112+ -- infinite-length encoding. The cddl tool always generates fix-length
113+ -- encoding but tx-submission codec is accepting only indefinite-length
114+ -- encoding.
115+ --
116+ fix :: CBOR. Term -> CBOR. Term
117+ fix = \ case
118+ TList (TInt tag : TList l : as)
119+ | tag == 1 || tag == 2
120+ -> TList (TInt tag : TListI l : as)
121+ term -> term
122+
88123
89124--
90125-- utils
@@ -98,7 +133,9 @@ unit_decodeSig spec = validateDecoder spec decodeSig 100
98133-- The `CDDL_INCLUDE_PATH` environment variable must be set.
99134cddlc :: FilePath -> IO BL. ByteString
100135cddlc path = do
101- (_, cddl, _) <- readProcessWithExitCode " cddlc" [" -u" , " -2" , " -t" , " cddl" , path] mempty
136+ (exitCode, cddl, _) <- readProcessWithExitCode " cddlc" [" -u" , " -2" , " -t" , " cddl" , path] mempty
137+ unless (exitCode == ExitSuccess ) $
138+ die $ printf " cddlc failed on \" %s\" with %s " path (show exitCode)
102139 return cddl
103140
104141
@@ -110,9 +147,11 @@ readCDDLSpecs = do
110147 setEnv " CDDL_INCLUDE_PATH" (dir <> " :" )
111148
112149 sigSpec <- cddlc (dir </> " sig.cddl" )
150+ localMessageNotificationSpec <- cddlc (dir </> " local-msg-notification.cddl" )
113151
114152 return CDDLSpecs {
115- cddlSig = CDDLSpec sigSpec
153+ cddlSig = CDDLSpec sigSpec,
154+ cddlLocalMsgNotification = CDDLSpec localMessageNotificationSpec
116155 }
117156
118157
@@ -130,7 +169,7 @@ validateDecoder (CDDLSpec spec) decoder rounds = do
130169 res = CBOR. deserialiseFromBytes decoder encoded_term
131170 case res of
132171 Left err -> assertFailure $ concat
133- [ " decoding failure:\n "
172+ [ printf " decoding failure:\n "
134173 , show err
135174 , " \n while decoding:\n "
136175 , show decoded_term
@@ -142,6 +181,76 @@ validateDecoder (CDDLSpec spec) decoder rounds = do
142181 Right _ -> return ()
143182
144183
184+ data SomeAgency ps where
185+ SomeAgency :: ( ActiveState st
186+ , Show (StateToken st )
187+ )
188+ => StateToken (st :: ps )
189+ -> SomeAgency ps
190+
191+ deriving instance Show (SomeAgency ps )
192+
193+
194+ -- | Generate valid encoded messages from a specification using `cddl generate`
195+ -- (and encoded with `diag2cbor.rb`) and check that we can decode it at one of
196+ -- the given agencies.
197+ --
198+ validateAnnotatedDecoder
199+ :: forall ps .
200+ Maybe (CBOR. Term -> CBOR. Term )
201+ -- ^ transform a generated term
202+ -> CDDLSpec ps
203+ -> AnnotatedCodec ps CBOR. DeserialiseFailure IO BL. ByteString
204+ -> [SomeAgency ps ]
205+ -> Int
206+ -> Assertion
207+ validateAnnotatedDecoder transform (CDDLSpec spec) codec stoks rounds = do
208+ eterms <- runExceptT $ generateCBORFromSpec spec rounds
209+ case eterms of
210+ Left err -> assertFailure err
211+ Right terms ->
212+ forM_ terms $ \ (generated_term, encoded_term) -> do
213+ let encoded_term' = case transform of
214+ Nothing -> encoded_term
215+ Just tr -> case CBOR. deserialiseFromBytes CBOR. decodeTerm encoded_term of
216+ Right (rest, term) | BL. null rest
217+ -> CBOR. toLazyByteString (CBOR. encodeTerm (tr term))
218+ Right _ -> error " validateDecoder: trailing bytes"
219+ Left err -> error $ " validateDecoder: decoding error: "
220+ ++ show err
221+
222+ Right (_, decoded_term) =
223+ CBOR. deserialiseFromBytes CBOR. decodeTerm encoded_term'
224+ res <- decodeMsg encoded_term'
225+ case res of
226+ Just errs -> assertFailure $ concat
227+ [ printf " decoding failures:\n "
228+ , unlines ((\ (SomeAgency a, e) -> printf " %-25s: %s" (show a) (show e)) <$> errs)
229+ , " while decoding:\n "
230+ , show decoded_term
231+ , " \n generated term (not rewritten):\n "
232+ , BL.Char8. unpack generated_term
233+ , " \n encoded term:\n "
234+ , BL.Char8. unpack (BL.Base16. encode encoded_term')
235+ ]
236+ Nothing -> return ()
237+ where
238+ -- | Try decode at all given agencies. If one succeeds return
239+ -- 'Nothing' otherwise return all 'DeserialiseFailure's.
240+ --
241+ decodeMsg :: BL. ByteString
242+ -> IO (Maybe [(SomeAgency ps , CBOR. DeserialiseFailure )])
243+ decodeMsg bs =
244+ -- sequence [Nothing, ...] = Nothing
245+ fmap sequence $
246+ forM stoks $ \ (a@ (SomeAgency (stok :: StateToken st ))) -> do
247+ decoder <- decode codec stok
248+ res <- runDecoder [bs] decoder
249+ return $ case res of
250+ Left err -> Just (a, err)
251+ Right {} -> Nothing
252+
253+
145254generateCBORFromSpec :: BL. ByteString
146255 -> Int
147256 -> ExceptT String IO [(BL. ByteString , BL. ByteString )]
0 commit comments