diff --git a/dmq-node/cddl/Main.hs b/dmq-node/cddl/Main.hs index 70ad780e..8ea723f1 100644 --- a/dmq-node/cddl/Main.hs +++ b/dmq-node/cddl/Main.hs @@ -362,8 +362,8 @@ indefiniteListFix term = unpackResult :: IO (ExitCode, BL.ByteString, BL.ByteString) -> IO (Either String BL.ByteString) unpackResult r = r <&> \case - (ExitFailure _, _, err) -> (Left $ BL.Char8.unpack err) - (ExitSuccess, bytes, _) -> (Right bytes) + (ExitFailure _, _, err) -> Left (BL.Char8.unpack err) + (ExitSuccess, bytes, _) -> Right bytes withTemporaryFile :: BL.ByteString -> (FilePath -> IO a) -> IO a diff --git a/dmq-node/cddl/specs/sig.cddl b/dmq-node/cddl/specs/sig.cddl index a0b68b23..ddd620ca 100644 --- a/dmq-node/cddl/specs/sig.cddl +++ b/dmq-node/cddl/specs/sig.cddl @@ -1,17 +1,17 @@ message = [ - messagePayload + messageId + , messagePayload , kesSignature , operationalCertificate , coldVerificationKey ] messagePayload = [ - messageId - , messageBody + messageBody , kesPeriod , expiresAt ] -messageId = bstr +messageId = bstr .size 32 messageBody = bstr messageSize = word32 kesSignature = bstr .size 448 diff --git a/dmq-node/changelog.d/20260513_164011_coot_sig_id.md b/dmq-node/changelog.d/20260513_164011_coot_sig_id.md new file mode 100644 index 00000000..3acead8d --- /dev/null +++ b/dmq-node/changelog.d/20260513_164011_coot_sig_id.md @@ -0,0 +1,38 @@ + + +### Breaking + +- Using `Hash Blake2b_256` hashing algorithm for computing `SigId`. +- Changed encoding of `Sig` according to: https://github.com/cardano-foundation/CIPs/pull/1185. +- Removed `TraceLocalMsgSubmission` `msg` parameter, since it was unused. +- Removed `ToJSON (AnyMessage SigSubmissionV2)` instance. + +### Non-Breaking + +- Validation of `SigId`s is implemented using `Hash Blake2b_256` hashing + algorithm. +- Implemented `DMaximum` verbosity for the following tracers: + - `AnyMessage (LocalMsgNotification (Sig crypto))` + - `AnyMessage (SigSubmissionV2 SigId (Sig crypto))` + - `AnyMessage (TxSubmission2 txid tx)` + - `TraceSigSubmissionOutbound SigId (Sig crypto)` + - the `TraceSigSubmissionOutboundSendMsgReplySigs` logs `sigs` rather than + `sigids` with different fields depending on the verbosity level. + in `DMaximum` tracing, all fields of `Sig crypto` are logged. +- Logging of `AnyMessage (SigSubmissionV2 SigId (Sig crypto))` changed syntax: + - log `sigids` of `MsgReplySigIds` + - log `sigids` of `MsgRequestSigs` using JSON syntax + + + + diff --git a/dmq-node/src/DMQ/NodeToClient/LocalMsgSubmission.hs b/dmq-node/src/DMQ/NodeToClient/LocalMsgSubmission.hs index aaa364b8..428fb71b 100644 --- a/dmq-node/src/DMQ/NodeToClient/LocalMsgSubmission.hs +++ b/dmq-node/src/DMQ/NodeToClient/LocalMsgSubmission.hs @@ -3,7 +3,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PackageImports #-} {-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeApplications #-} {-# LANGUAGE UndecidableInstances #-} module DMQ.NodeToClient.LocalMsgSubmission where @@ -18,7 +17,7 @@ import Cardano.Logging qualified as Logging import DMQ.Protocol.LocalMsgSubmission.Server import DMQ.Protocol.LocalMsgSubmission.Type -import DMQ.Protocol.SigSubmission.Type (Sig, SigId) +import DMQ.Protocol.SigSubmission.Type (SigId) -- | Local transaction submission server, for adding txs to the 'Mempool' -- @@ -27,7 +26,7 @@ localMsgSubmissionServer :: Monad m => (msg -> msgid) -- ^ get message id - -> Tracer m (TraceLocalMsgSubmission msg msgid) + -> Tracer m (TraceLocalMsgSubmission msgid) -> (msg -> m (Either (msgid, SigValidationError) msgid)) -- ^ add a msg to mempool -> m (LocalMsgSubmissionServer msg m ()) @@ -50,7 +49,7 @@ localMsgSubmissionServer getMsgId tracer mempoolAddTxs = } -data TraceLocalMsgSubmission msg msgid = +data TraceLocalMsgSubmission msgid = TraceReceivedMsg msgid -- ^ A signature was received. | TraceSubmitFailure msgid SigValidationError @@ -59,10 +58,10 @@ data TraceLocalMsgSubmission msg msgid = -- ^ A signature was validated and accepted into the mempool. deriving instance - (Show msg, Show msgid) - => Show (TraceLocalMsgSubmission msg msgid) + (Show msgid) + => Show (TraceLocalMsgSubmission msgid) -instance Logging.LogFormatting (TraceLocalMsgSubmission (Sig crypto) SigId) where +instance Logging.LogFormatting (TraceLocalMsgSubmission SigId) where forMachine _ (TraceReceivedMsg sigid) = mconcat [ "kind" .= Aeson.String "TraceReceivedMsg" , "sigid" .= sigid @@ -77,7 +76,7 @@ instance Logging.LogFormatting (TraceLocalMsgSubmission (Sig crypto) SigId) wher , "sigid" .= sigid ] -instance Logging.MetaTrace (TraceLocalMsgSubmission (Sig crypto) SigId) where +instance Logging.MetaTrace (TraceLocalMsgSubmission SigId) where namespaceFor TraceReceivedMsg {} = Logging.Namespace [] ["TraceReceivedMsg"] namespaceFor TraceSubmitFailure {} = Logging.Namespace [] ["TraceSubmitFailure"] namespaceFor TraceSubmitAccept {} = Logging.Namespace [] ["TraceSubmitAccept"] @@ -109,7 +108,7 @@ instance (Typeable msgid, Typeable msg, Show msgid) instance ToJSON msgid - => ToJSON (TraceLocalMsgSubmission msg msgid) where + => ToJSON (TraceLocalMsgSubmission msgid) where toJSON (TraceReceivedMsg msgid) = -- TODO: once we have verbosity levels, we could include the full tx, for -- now one can use `TraceSendRecv` tracer for the mini-protocol to see full diff --git a/dmq-node/src/DMQ/Protocol/SigSubmission/Codec.hs b/dmq-node/src/DMQ/Protocol/SigSubmission/Codec.hs index 50878dcf..d8d6ee35 100644 --- a/dmq-node/src/DMQ/Protocol/SigSubmission/Codec.hs +++ b/dmq-node/src/DMQ/Protocol/SigSubmission/Codec.hs @@ -22,13 +22,16 @@ module DMQ.Protocol.SigSubmission.Codec import Control.Monad (when) import Control.Monad.Class.MonadST import Control.Monad.Class.MonadTime.SI -import Data.ByteString.Lazy (ByteString) +import Data.ByteString.Lazy as BS.Lazy +import Data.ByteString.Short qualified as BS.Short import Text.Printf import Codec.CBOR.Decoding qualified as CBOR import Codec.CBOR.Encoding qualified as CBOR import Codec.CBOR.Read qualified as CBOR +import Cardano.Crypto.Hash.Class (hashFromBytes, hashToBytesShort) + import Network.TypedProtocol.Codec.CBOR import Cardano.Binary (FromCBOR (..), ToCBOR (..)) @@ -91,10 +94,14 @@ byteLimitsSigSubmission = ProtocolSizeLimits stateToLimit encodeSigId :: SigId -> CBOR.Encoding -encodeSigId SigId { getSigId } = CBOR.encodeBytes (getSigHash getSigId) +encodeSigId SigId { getSigId } = CBOR.encodeBytes (BS.Short.fromShort (hashToBytesShort getSigId)) decodeSigId :: forall s. CBOR.Decoder s SigId -decodeSigId = SigId . SigHash <$> CBOR.decodeBytes +decodeSigId = do + mbHash <- hashFromBytes <$> CBOR.decodeBytes + case mbHash of + Nothing -> fail "decodeSigId: expected 32 bytes" + Just hs -> pure (SigId hs) -- | We follow the same encoding as in `cardano-ledger` for `OCert`. @@ -149,11 +156,12 @@ decodeSig :: forall crypto s. => CBOR.Decoder s (ByteString -> SigRawWithSignedBytes crypto) decodeSig = do a <- CBOR.decodeListLen - when (a /= 4) $ fail (printf "decodeSig: unexpected number of parameters %d for Sig" a) + when (a /= 5) $ fail (printf "decodeSig: unexpected number of parameters %d for Sig" a) + sigRawId <- decodeSigId -- start of signed data startOffset <- CBOR.peekByteOffset - (sigRawId, sigRawBody, sigRawKESPeriod, sigRawExpiresAt) + (sigRawBody, sigRawKESPeriod, sigRawExpiresAt) <- decodePayload endOffset <- CBOR.peekByteOffset -- end of signed data @@ -175,14 +183,13 @@ decodeSig = do } } where - decodePayload :: CBOR.Decoder s (SigId, SigBody, KESPeriod, POSIXTime) + decodePayload :: CBOR.Decoder s (SigBody, KESPeriod, POSIXTime) decodePayload = do a <- CBOR.decodeListLen - when (a /= 4) $ fail (printf "decodeSig: unexpected number of parameters %d for Sig's payload" a) - (,,,) <$> decodeSigId - <*> (SigBody <$> CBOR.decodeBytes) - <*> (KESPeriod <$> CBOR.decodeWord) - <*> (realToFrac <$> CBOR.decodeWord32) + when (a /= 3) $ fail (printf "decodeSig: unexpected number of parameters %d for Sig's payload" a) + (,,) <$> (SigBody <$> CBOR.decodeBytes) + <*> (KESPeriod <$> CBOR.decodeWord) + <*> (realToFrac <$> CBOR.decodeWord32) diff --git a/dmq-node/src/DMQ/Protocol/SigSubmission/Type.hs b/dmq-node/src/DMQ/Protocol/SigSubmission/Type.hs index 9612595d..f0a9eb6c 100644 --- a/dmq-node/src/DMQ/Protocol/SigSubmission/Type.hs +++ b/dmq-node/src/DMQ/Protocol/SigSubmission/Type.hs @@ -6,13 +6,13 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeData #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module DMQ.Protocol.SigSubmission.Type ( -- * Data types - SigHash (..) - , SigId (..) + SigId (..) , SigBody (..) , SigKESSignature (..) , SigOpCertificate (..) @@ -29,6 +29,7 @@ module DMQ.Protocol.SigSubmission.Type , SigValidationException (..) -- * Utilities , CBORBytes (..) + , Verbose (..) -- * Re-exports from `kes-agent` , KESPeriod (..) ) where @@ -36,21 +37,22 @@ module DMQ.Protocol.SigSubmission.Type import Control.Exception (Exception (..)) import Data.Aeson import Data.ByteString (ByteString) -import Data.ByteString.Base16 as BS.Base16 import Data.ByteString.Base16.Lazy as LBS.Base16 import Data.ByteString.Lazy qualified as LBS import Data.ByteString.Lazy.Char8 qualified as LBS.Char8 import Data.Text (Text) -import Data.Text qualified as Text -import Data.Text.Encoding qualified as Text import Data.Time.Clock.POSIX (POSIXTime) import Data.Typeable import Data.Word (Word64) import Cardano.Crypto.DSIGN.Class (DSIGNAlgorithm, VerKeyDSIGN) +import Cardano.Crypto.Hash.Blake2b (Blake2b_256) +import Cardano.Crypto.Hash.Class (Hash) import Cardano.Crypto.KES.Class (KESAlgorithm (..)) +import Cardano.Crypto.Util (SignableRepresentation (..)) import Cardano.KESAgent.KES.Crypto as KES -import Cardano.KESAgent.KES.OCert (KESPeriod (..), OCert (..)) +import Cardano.KESAgent.KES.OCert (KESPeriod (..), OCert (..), + OCertSignable (..)) import Cardano.Logging qualified as Logging import Ouroboros.Network.Protocol.TxSubmission2.Type as SigSubmission hiding @@ -59,20 +61,13 @@ import Ouroboros.Network.Protocol.TxSubmission2.Type as TxSubmission2 import Ouroboros.Network.Util.ShowProxy -newtype SigHash = SigHash { getSigHash :: ByteString } - deriving stock (Eq, Ord) +type data SigPayload -instance Show SigHash where - -- show first 10 bytes in hex - show (SigHash bs) = take 20 . Text.unpack . Text.decodeUtf8Lenient . BS.Base16.encode $ bs - -newtype SigId = SigId { getSigId :: SigHash } +newtype SigId = SigId { getSigId :: Hash Blake2b_256 SigPayload } deriving stock (Show, Eq, Ord) instance ToJSON SigId where - toJSON (SigId (SigHash bs)) = - -- show first 10 bytes in hex - String (Text.take 20 . Text.decodeUtf8Lenient . BS.Base16.encode $ bs) + toJSON (SigId sigId) = toJSON sigId instance ShowProxy SigId where @@ -139,30 +134,43 @@ deriving instance ( DSIGNAlgorithm (KES.DSIGN crypto) instance Crypto crypto => ToJSON (SigRaw crypto) where - -- TODO: it is too verbose, we need verbosity levels for these JSON fields toJSON SigRaw { sigRawId - {- , sigRawBody -} , sigRawKESPeriod , sigRawExpiresAt - {- , sigRawKESSignature - , sigRawOpCertificate - , sigRawColdKey -} } = object [ "id" .= sigRawId - {- , "body" .= show (getSigBody sigRawBody) -} , "kesPeriod" .= unKESPeriod sigRawKESPeriod , "expiresAt" .= show sigRawExpiresAt - {- , "kesSignature" .= show (getSigKESSignature sigRawKESSignature) + ] + +newtype Verbose a = Verbose { unVerbose :: a } + +instance Crypto crypto + => ToJSON (Verbose (SigRaw crypto)) where + toJSON Verbose { unVerbose = + SigRaw { sigRawId + , sigRawBody + , sigRawKESPeriod + , sigRawExpiresAt + , sigRawKESSignature + , sigRawOpCertificate + , sigRawColdKey + } + } = + + object [ "id" .= sigRawId + , "body" .= show (getSigBody sigRawBody) + , "kesPeriod" .= unKESPeriod sigRawKESPeriod + , "expiresAt" .= show sigRawExpiresAt + , "kesSignature" .= show (getSigKESSignature sigRawKESSignature) , "opCertificate" .= show (getSignableRepresentation signable) - , "coldKey" .= show (getSigColdKey sigRawColdKey) -} + , "coldKey" .= show (getSigColdKey sigRawColdKey) ] - {- where ocert = getSigOpCertificate sigRawOpCertificate signable :: OCertSignable crypto signable = OCertSignable (ocertVkHot ocert) (ocertN ocert) (ocertKESPeriod ocert) - -} data SigRawWithSignedBytes crypto = SigRawWithSignedBytes { sigRawSignedBytes :: LBS.ByteString, @@ -186,6 +194,10 @@ instance Crypto crypto => ToJSON (SigRawWithSignedBytes crypto) where toJSON SigRawWithSignedBytes {sigRaw} = toJSON sigRaw +instance Crypto crypto + => ToJSON (Verbose (SigRawWithSignedBytes crypto)) where + toJSON (Verbose SigRawWithSignedBytes {sigRaw}) = toJSON (Verbose sigRaw) + data Sig crypto = SigWithBytes { sigRawBytes :: LBS.ByteString, @@ -196,11 +208,13 @@ data Sig crypto = SigWithBytes { -- TODO: this show instance is too minimal. Proper `Show` instance is useful -- in `QuickCheck` tests. This minimal instance is for example --- useful in `TraceTxLogic` tracer.. +-- useful in `TraceTxLogic` tracer. We should move the `Verbose` wrapper to +-- `ouroboros-network` and use it in the `LogFormatting TraceTxLogic` instance. +-- -- instance Show (Sig crypto) where show Sig { sigId, sigKESPeriod, sigExpiresAt } = - "Sig { sigId = \"" ++ show (getSigId sigId) ++ "\"" + "Sig { sigId = " ++ show (getSigId sigId) ++ " , sigKESPeriod = " ++ show (unKESPeriod sigKESPeriod) ++ " , sigExpiresAt = " ++ show sigExpiresAt ++ " }" @@ -220,6 +234,10 @@ instance Crypto crypto => ToJSON (Sig crypto) where toJSON SigWithBytes {sigRawWithSignedBytes} = toJSON sigRawWithSignedBytes +instance Crypto crypto + => ToJSON (Verbose (Sig crypto)) where + toJSON (Verbose SigWithBytes { sigRawWithSignedBytes}) = toJSON (Verbose sigRawWithSignedBytes) + -- | A convenient bidirectional pattern synonym for the `Sig` type. -- pattern Sig @@ -296,7 +314,8 @@ type SigSubmission crypto = TxSubmission2.TxSubmission2 SigId (Sig crypto) data SigValidationError = - InvalidKESSignature KESPeriod KESPeriod String + InvalidSigId + | InvalidKESSignature KESPeriod KESPeriod String | InvalidSignatureOCERT !Word64 -- OCert counter !KESPeriod -- OCert KES period diff --git a/dmq-node/src/DMQ/Protocol/SigSubmission/Validate.hs b/dmq-node/src/DMQ/Protocol/SigSubmission/Validate.hs index 8ded1ee5..3e179544 100644 --- a/dmq-node/src/DMQ/Protocol/SigSubmission/Validate.hs +++ b/dmq-node/src/DMQ/Protocol/SigSubmission/Validate.hs @@ -1,7 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} @@ -33,6 +32,7 @@ import Data.Maybe (fromJust, isNothing) import Data.Typeable import Cardano.Crypto.DSIGN.Class qualified as DSIGN +import Cardano.Crypto.Hash.Class (castHash, hashWith) import Cardano.Crypto.KES.Class (KESAlgorithm (..)) import Cardano.KESAgent.KES.Crypto as KES import Cardano.KESAgent.KES.OCert (OCert (..), OCertSignable, validateOCert) @@ -59,6 +59,13 @@ pattern ZeroSetSnapshot <- (Ledger.isZero . ssSetPool -> True) {-# COMPLETE NotZeroSetSnapshot, NotZeroMarkSnapshot, ZeroSetSnapshot #-} +validateSigId :: Sig crypto -> Bool +validateSigId Sig { sigId = SigId hash, sigSignedBytes } = + hash + == + castHash (hashWith id (LBS.toStrict sigSignedBytes)) + + validateSig :: forall crypto. ( Crypto crypto , DSIGN crypto ~ Ledger.DSIGN @@ -95,6 +102,11 @@ validateSig now sigs ctx0 = sigColdKey = SigColdKey coldKey, sigKESSignature = SigKESSignature kesSig } = do + -- TODO: if new cborg version is released, validation of SigId should be + -- moved to the decoder, right now the decoder only verifies that we + -- received the right amount of bytes. + validateSigId sig ?! InvalidSigId + ctx@PoolValidationCtx { vctxEpoch, vctxStakeMap, vctxOcertMap } <- State.get -- diff --git a/dmq-node/src/DMQ/Protocol/SigSubmissionV2/Type.hs b/dmq-node/src/DMQ/Protocol/SigSubmissionV2/Type.hs index 9d55e208..07bfa920 100644 --- a/dmq-node/src/DMQ/Protocol/SigSubmissionV2/Type.hs +++ b/dmq-node/src/DMQ/Protocol/SigSubmissionV2/Type.hs @@ -36,17 +36,14 @@ module DMQ.Protocol.SigSubmissionV2.Type ) where import Control.DeepSeq (NFData (..)) -import Data.Aeson (KeyValue ((.=)), ToJSON (toJSON), Value (String), object) import Data.Kind (Type) import Data.Monoid (Sum (..)) import Data.Singletons -import Data.Text (pack) import Data.Word (Word16) import GHC.Generics (Generic) import NoThunks.Class (NoThunks (..)) import Quiet (Quiet (..)) -import Network.TypedProtocol.Codec (AnyMessage (AnyMessageAndAgency)) import Network.TypedProtocol.Core import DMQ.Protocol.SigSubmission.Type as SigSubmission (Sig (..), SigBody (..), @@ -98,41 +95,6 @@ instance ( ShowProxy sigId instance ShowProxy (StIdle :: SigSubmissionV2 sigId sig) where showProxy _ = "StIdle" -instance (Show sigId, Show sig) - => ToJSON (AnyMessage (SigSubmissionV2 sigId sig)) where - toJSON (AnyMessageAndAgency stok MsgRequestSigIds{}) = - object - [ "kind" .= String "MsgRequestSigIds" - , "agency" .= String (pack $ show stok) - ] - toJSON (AnyMessageAndAgency stok (MsgReplySigIds ids)) = - object - [ "kind" .= String "MsgReplySigIds" - , "agency" .= String (pack $ show stok) - , "ids" .= String (pack $ show ids) - ] - toJSON (AnyMessageAndAgency stok MsgReplyNoSigIds) = - object - [ "kind" .= String "MsgReplyNoSigIds" - , "agency" .= String (pack $ show stok) - ] - toJSON (AnyMessageAndAgency stok (MsgRequestSigs{})) = - object - [ "kind" .= String "MsgRequestSigs" - , "agency" .= String (pack $ show stok) - ] - toJSON (AnyMessageAndAgency stok (MsgReplySigs sigs)) = - object - [ "kind" .= String "MsgReplySigs" - , "agency" .= String (pack $ show stok) - , "sigs" .= String (pack $ show sigs) - ] - toJSON (AnyMessageAndAgency stok MsgDone) = - object - [ "kind" .= String "MsgDone" - , "agency" .= String (pack $ show stok) - ] - type SingSigSubmissionV2 :: SigSubmissionV2 sigId sig diff --git a/dmq-node/src/DMQ/Tracer.hs b/dmq-node/src/DMQ/Tracer.hs index c3ef6563..1f458f91 100644 --- a/dmq-node/src/DMQ/Tracer.hs +++ b/dmq-node/src/DMQ/Tracer.hs @@ -1,9 +1,8 @@ +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PackageImports #-} {-# LANGUAGE RankNTypes #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wno-orphans #-} @@ -84,7 +83,7 @@ import DMQ.Protocol.LocalMsgSubmission.Type (LocalMsgSubmission, LocalTxSubmission) import DMQ.Protocol.LocalMsgSubmission.Type qualified as LMS import DMQ.Protocol.SigSubmission.Type (Sig, SigId, SigSubmission, - SigValidationTrace) + SigValidationTrace, Verbose (..)) import DMQ.Protocol.SigSubmissionV2.Type (SigSubmissionV2) import DMQ.Protocol.SigSubmissionV2.Type qualified as SigSubV2 import DMQ.SigSubmissionV2.Outbound qualified as SigSubV2 @@ -98,7 +97,7 @@ data DMQTracers crypto ntnAddr ntcAddr m = DMQTracers { localMsgSubmissionProtocolTracer :: Tracer m (Mx.WithBearer (ConnectionId ntcAddr) (TraceSendRecv (LocalMsgSubmission (Sig crypto)))), localMsgSubmissionServerTracer - :: Tracer m (Mx.WithBearer (ConnectionId ntcAddr) (TraceLocalMsgSubmission (Sig crypto) SigId)), + :: Tracer m (Mx.WithBearer (ConnectionId ntcAddr) (TraceLocalMsgSubmission SigId)), localMsgNotificationProtocolTracer :: Tracer m (Mx.WithBearer (ConnectionId ntcAddr) (TraceSendRecv (LocalMsgNotification (Sig crypto)))), localMsgNotificationServerTracer @@ -611,18 +610,27 @@ instance Logging.MetaTrace (AnyMessage (LMS.LocalTxSubmission tx err)) where instance Crypto crypto => Logging.LogFormatting (AnyMessage (LocalMsgNotification (Sig crypto))) where - forMachine _dtal (AnyMessageAndAgency _agency msg) = case msg of + forMachine dtal (AnyMessageAndAgency _agency msg) = case msg of LMN.MsgRequest blockingStyle -> mconcat [ "type" .= String "MsgRequest" , "blockingStyle" .= show blockingStyle ] LMN.MsgReply msgs hasMore -> - mconcat [ "type" .= String "MsgReply" - , "msgs" .= Foldable.toList msgs - , "hasMore" .= case hasMore of - LMN.HasMore -> True - LMN.DoesNotHaveMore -> False - ] + case dtal of + Logging.DMaximum -> + mconcat [ "type" .= String "MsgReply" + , "msgs" .= (Verbose <$> Foldable.toList msgs) + , "hasMore" .= case hasMore of + LMN.HasMore -> True + LMN.DoesNotHaveMore -> False + ] + _ -> + mconcat [ "type" .= String "MsgReply" + , "msgs" .= Foldable.toList msgs + , "hasMore" .= case hasMore of + LMN.HasMore -> True + LMN.DoesNotHaveMore -> False + ] LMN.MsgClientDone -> mconcat [ "type" .= String "MsgClientDone" ] @@ -674,16 +682,18 @@ instance Logging.MetaTrace (LMN.TraceMessageNotificationServer SigId) where -- SigSubmissionV2 Tracer -------------------------------------------------------------------------------- -instance Logging.LogFormatting (AnyMessage (SigSubmissionV2 SigId (Sig crypto))) where +instance Crypto crypto + => Logging.LogFormatting (AnyMessage (SigSubmissionV2 SigId (Sig crypto))) where forMachine _dtal (AnyMessageAndAgency stok SigSubV2.MsgRequestSigIds {}) = mconcat [ "kind" .= String "MsgRequestSigIds" , "agency" .= String (Text.pack $ show stok) ] - forMachine _dtal (AnyMessageAndAgency stok (SigSubV2.MsgReplySigIds _)) = + forMachine _dtal (AnyMessageAndAgency stok (SigSubV2.MsgReplySigIds sigs)) = mconcat [ "kind" .= String "MsgReplySigIds" , "agency" .= String (Text.pack $ show stok) + , "sigds" .= Foldable.toList sigs ] forMachine _dtal (AnyMessageAndAgency stok SigSubV2.MsgReplyNoSigIds) = mconcat @@ -694,14 +704,22 @@ instance Logging.LogFormatting (AnyMessage (SigSubmissionV2 SigId (Sig crypto))) mconcat [ "kind" .= String "MsgRequestSigs" , "agency" .= String (Text.pack $ show stok) - , "sigids" .= String (Text.pack $ show sigids) - ] - forMachine _dtal (AnyMessageAndAgency stok (SigSubV2.MsgReplySigs sigs)) = - mconcat - [ "kind" .= String "MsgReplyTxs" - , "agency" .= String (Text.pack $ show stok) - , "sigs" .= String (Text.pack $ show sigs) + , "sigids" .= sigids ] + forMachine dtal (AnyMessageAndAgency stok (SigSubV2.MsgReplySigs sigs)) = + case dtal of + Logging.DMaximum -> + mconcat + [ "kind" .= String "MsgReplyTxs" + , "agency" .= String (Text.pack $ show stok) + , "sigs" .= (Verbose <$> sigs) + ] + _ -> + mconcat + [ "kind" .= String "MsgReplyTxs" + , "agency" .= String (Text.pack $ show stok) + , "sigs" .= sigs + ] forMachine _dtal (AnyMessageAndAgency stok SigSubV2.MsgDone) = mconcat [ "kind" .= String "MsgDone" @@ -742,35 +760,46 @@ instance Logging.MetaTrace (AnyMessage (SigSubmissionV2 SigId (Sig crypto))) whe -- TODO: move instances from `Cardano.Node.Tracing.Tracers.NodeToNode` to `ouroboros-network`. -instance (Show txid, Show tx) +instance (ToJSON txid, ToJSON tx, ToJSON (Verbose tx)) => Logging.LogFormatting (AnyMessage (TxSubmission2 txid tx)) where forMachine _dtal (AnyMessageAndAgency stok STX.MsgInit) = mconcat [ "kind" .= String "MsgInit" , "agency" .= String (Text.pack $ show stok) ] - forMachine _dtal (AnyMessageAndAgency stok STX.MsgRequestTxIds {}) = + forMachine _dtal (AnyMessageAndAgency stok (STX.MsgRequestTxIds _ numToAck numToReq)) = mconcat [ "kind" .= String "MsgRequestTxIds" , "agency" .= String (Text.pack $ show stok) + , "numToAck" .= STX.getNumTxIdsToAck numToAck + , "numToReq" .= STX.getNumTxIdsToReq numToReq ] - forMachine _dtal (AnyMessageAndAgency stok (STX.MsgReplyTxIds _)) = + forMachine _dtal (AnyMessageAndAgency stok (STX.MsgReplyTxIds txids)) = mconcat [ "kind" .= String "MsgReplyTxIds" , "agency" .= String (Text.pack $ show stok) + , "txIds" .= Foldable.toList txids ] forMachine _dtal (AnyMessageAndAgency stok (STX.MsgRequestTxs txids)) = mconcat [ "kind" .= String "MsgRequestTxs" , "agency" .= String (Text.pack $ show stok) - , "txIds" .= String (Text.pack $ show txids) - ] - forMachine _dtal (AnyMessageAndAgency stok (STX.MsgReplyTxs txs)) = - mconcat - [ "kind" .= String "MsgReplyTxs" - , "agency" .= String (Text.pack $ show stok) - , "txs" .= String (Text.pack $ show txs) + , "txIds" .= txids ] + forMachine dtal (AnyMessageAndAgency stok (STX.MsgReplyTxs txs)) = + case dtal of + Logging.DMaximum -> + mconcat + [ "kind" .= String "MsgReplyTxs" + , "agency" .= String (Text.pack $ show stok) + , "txs" .= (Verbose <$> txs) + ] + _ -> + mconcat + [ "kind" .= String "MsgReplyTxs" + , "agency" .= String (Text.pack $ show stok) + , "txs" .= txs + ] forMachine _dtal (AnyMessageAndAgency stok STX.MsgDone) = mconcat [ "kind" .= String "MsgDone" @@ -897,15 +926,23 @@ instance Logging.MetaTrace (AnyMessage (TxSubmission2 SigId (Sig crypto))) where , Namespace [] ["Done"] ] -instance Logging.LogFormatting (SigSubV2.TraceSigSubmissionOutbound SigId (Sig crypto)) where +instance Crypto crypto + => Logging.LogFormatting (SigSubV2.TraceSigSubmissionOutbound SigId (Sig crypto)) where forMachine _ (SigSubV2.TraceSigSubmissionOutboundRecvMsgRequestSigs sigids) = mconcat [ "kind" .= String "TraceSigSubmissionOutboundRecvMsgRequestSigs" , "sigids" .= sigids ] - forMachine _ (SigSubV2.TraceSigSubmissionOutboundSendMsgReplySigs sigs) = - mconcat [ "kind" .= String "TraceSigSubmissionOutboundSendMsgReplySigs" - , "sigids" .= (SigSubV2.sigId <$> sigs) - ] + forMachine dtal (SigSubV2.TraceSigSubmissionOutboundSendMsgReplySigs sigs) = + case dtal of + Logging.DMaximum -> + mconcat [ "kind" .= String "TraceSigSubmissionOutboundSendMsgReplySigs" + , "sigs" .= (Verbose <$> sigs) + ] + _ -> + mconcat [ "kind" .= String "TraceSigSubmissionOutboundSendMsgReplySigs" + , "sigs" .= sigs + ] + instance Logging.MetaTrace (SigSubV2.TraceSigSubmissionOutbound SigId (Sig crypto)) where namespaceFor SigSubV2.TraceSigSubmissionOutboundRecvMsgRequestSigs {} = Logging.Namespace [] ["TraceSigSubmissionOutboundRecvMsgRequestSigs"] namespaceFor SigSubV2.TraceSigSubmissionOutboundSendMsgReplySigs {} = Logging.Namespace [] ["TraceSigSubmissionOutboundSendMsgReplySigs"] diff --git a/dmq-node/test/DMQ/Protocol/SigSubmission/Test.hs b/dmq-node/test/DMQ/Protocol/SigSubmission/Test.hs index fe4602e5..f4f6d65e 100644 --- a/dmq-node/test/DMQ/Protocol/SigSubmission/Test.hs +++ b/dmq-node/test/DMQ/Protocol/SigSubmission/Test.hs @@ -32,6 +32,7 @@ import Control.Monad.Class.MonadTime.SI import Control.Monad.ST (runST) import Data.Bifunctor (second) import Data.ByteString (ByteString) +import Data.ByteString qualified as BS import Data.ByteString.Lazy qualified as BL import Data.List.NonEmpty qualified as NonEmpty import Data.Map qualified as Map @@ -47,6 +48,9 @@ import Network.TypedProtocol.Codec.Properties hiding (prop_codec) import Cardano.Crypto.DSIGN.Class (DSIGNAlgorithm, SignKeyDSIGN, deriveVerKeyDSIGN, encodeVerKeyDSIGN) import Cardano.Crypto.DSIGN.Class qualified as DSIGN +import Cardano.Crypto.Hash.Blake2b (Blake2b_256) +import Cardano.Crypto.Hash.Class (Hash, castHash, hashFromBytes, hashSize, + hashWith) import Cardano.Crypto.KES.Class (KESAlgorithm (..), VerKeyKES, encodeSigKES) import Cardano.Crypto.KES.Class qualified as KES import Cardano.Crypto.PinnedSizedBytes (PinnedSizedBytes, psbToByteString) @@ -136,9 +140,18 @@ tests = ] ] -instance Arbitrary SigHash where - arbitrary = SigHash <$> arbitrary - shrink = map SigHash . shrink . getSigHash +instance Arbitrary (Hash Blake2b_256 a) where + arbitrary = do + bs <- BS.take size <$> arbitrary `suchThat` (\bs -> BS.length bs >= size) + pure $ case hashFromBytes bs of + Just a -> a + Nothing -> error "Arbitrary (Hash Blake2b_256): unexpected error" + where + size :: Int + size = fromIntegral $ hashSize (Proxy @Blake2b_256) + + -- there's no need to shrink hashes + shrink _ = [] instance Arbitrary SigId where arbitrary = SigId <$> arbitrary @@ -348,7 +361,6 @@ instance ( Crypto crypto ) => Arbitrary (WithConstrKES size kesCrypto (SigRawWithSignedBytes crypto)) where arbitrary = do - sigRawId <- arbitrary sigRawExpiresAt <- arbitrary let maxKESOffset :: Word maxKESOffset = totalPeriodsKES (Proxy :: Proxy kesCrypto) @@ -371,7 +383,7 @@ instance ( Crypto crypto + kesOffset sigRaw = SigRaw { - sigRawId, + sigRawId = undefined, -- to be filled below sigRawBody, sigRawKESPeriod, sigRawOpCertificate, @@ -379,19 +391,21 @@ instance ( Crypto crypto sigRawExpiresAt, sigRawKESSignature = undefined -- to be filled below } - signedBytes = CBOR.toStrictByteString (encodeSigRaw' sigRaw) + signedBytes = CBOR.toStrictByteString (encodeSigPayload sigRaw) -- evolve the key to the target period mbSnKESKey <- KES.updateKESTo () sigRawKESPeriod ocert (KES.SignKeyWithPeriodKES snKESKey 0) case mbSnKESKey of Just (KES.SignKeyWithPeriodKES snKESKey' _) -> do + let sigRawId = SigId (castHash $ hashWith id signedBytes) -- signed bytes with the snKESKey' sigRawKESSignature <- SigKESSignature <$> KES.signKES () kesOffset signedBytes snKESKey' return SigRawWithSignedBytes { sigRawSignedBytes = BL.fromStrict signedBytes, - sigRaw = sigRaw { sigRawKESSignature } + sigRaw = sigRaw { sigRawId, + sigRawKESSignature } } Nothing -> error $ "arbitrary SigRawWithSignedBytes: could not evolve KES key to the target period by KES offset: " @@ -454,7 +468,7 @@ shrinkSigRawWithSignedBytesFn SigRawWithSignedBytes { sigRaw } = [ SigRawWithSignedBytes { sigRaw = sigRaw', sigRawSignedBytes = sigRawSignedBytes' } | sigRaw' <- shrinkSigRawFn sigRaw - , let sigRawSignedBytes' = CBOR.toLazyByteString (encodeSigRaw' sigRaw') + , let sigRawSignedBytes' = CBOR.toLazyByteString (encodeSigPayload sigRaw') ] @@ -500,17 +514,14 @@ mkSig sigRawWithSignedBytes@SigRawWithSignedBytes { sigRaw } = sigRawBytes = CBOR.toLazyByteString (encodeSigRaw sigRaw) --- encode only signed part -encodeSigRaw' :: SigRaw crypto - -> CBOR.Encoding -encodeSigRaw' SigRaw { - sigRawId, +-- encode the payload +encodeSigPayload :: SigRaw crypto -> CBOR.Encoding +encodeSigPayload SigRaw { sigRawBody, sigRawKESPeriod, sigRawExpiresAt } - = CBOR.encodeListLen 4 - <> encodeSigId sigRawId + = CBOR.encodeListLen 3 <> CBOR.encodeBytes (getSigBody sigRawBody) <> CBOR.encodeWord (unKESPeriod sigRawKESPeriod) <> CBOR.encodeWord32 (floor sigRawExpiresAt) @@ -519,9 +530,10 @@ encodeSigRaw' SigRaw { encodeSigRaw :: Crypto crypto => SigRaw crypto -> CBOR.Encoding -encodeSigRaw sigRaw@SigRaw { sigRawKESSignature, sigRawOpCertificate, sigRawColdKey } = - CBOR.encodeListLen 4 - <> encodeSigRaw' sigRaw +encodeSigRaw sigRaw@SigRaw { sigRawId, sigRawKESSignature, sigRawOpCertificate, sigRawColdKey } = + CBOR.encodeListLen 5 + <> encodeSigId sigRawId + <> encodeSigPayload sigRaw <> encodeSigKES (getSigKESSignature sigRawKESSignature) <> encodeSigOpCertificate sigRawOpCertificate <> encodeVerKeyDSIGN (getSigColdKey sigRawColdKey) @@ -672,7 +684,7 @@ prop_codec_ocert_standardcrypto = prop_codec_ocert . getBlind -- Verify `Sig` encoding/decoding roundtrip: -- * `SigRaw` is preserved by encoding/decoding. -- * bytes match the encoding of `encodeSig`. --- * signed bytes match the encoding of `encodeSigRaw'`. +-- * signed bytes match the encoding of `encodeSigPayload`. prop_codec_sig :: forall crypto. Crypto crypto => WithConstrKES (SeedSizeKES (KES crypto)) (KES crypto) (Sig crypto)