-
Notifications
You must be signed in to change notification settings - Fork 155
/
ProtocolMagic.hs
163 lines (140 loc) · 5.16 KB
/
ProtocolMagic.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE UndecidableInstances #-}
module Cardano.Crypto.ProtocolMagic (
ProtocolMagicId (..),
ProtocolMagic,
AProtocolMagic (..),
RequiresNetworkMagic (..),
getProtocolMagic,
getProtocolMagicId,
)
where
import Cardano.Ledger.Binary (
Annotated (..),
DecCBOR (..),
EncCBOR (..),
FromCBOR (..),
ToCBOR (..),
decodeTag,
encodeTag,
fromByronCBOR,
toByronCBOR,
)
import Cardano.Prelude
import Control.Monad.Fail (fail)
import Data.Aeson ((.:), (.=))
import qualified Data.Aeson as A
import NoThunks.Class (NoThunks)
import Text.JSON.Canonical (FromJSON (..), JSValue (..), ToJSON (..), expected)
-- | Magic number which should differ for different clusters. It's
-- defined here, because it's used for signing. It also used for other
-- things (e. g. it's part of a serialized block).
--
-- mhueschen: As part of CO-353 I am adding `getRequiresNetworkMagic` in
-- order to pipe configuration to functions which must generate & verify
-- Addresses (which now must be aware of `NetworkMagic`).
type AProtocolMagic :: Type -> Type
data AProtocolMagic a = AProtocolMagic
{ getAProtocolMagicId :: !(Annotated ProtocolMagicId a)
, getRequiresNetworkMagic :: !RequiresNetworkMagic
}
deriving (Eq, Show, Generic, NFData, NoThunks)
type ProtocolMagic :: Type
type ProtocolMagic = AProtocolMagic ()
type ProtocolMagicId :: Type
newtype ProtocolMagicId = ProtocolMagicId
{ unProtocolMagicId :: Word32
}
deriving (Show, Eq, Generic)
deriving newtype (DecCBOR, EncCBOR, FromCBOR, ToCBOR)
deriving anyclass (NFData, NoThunks)
instance A.ToJSON ProtocolMagicId where
toJSON = A.toJSON . unProtocolMagicId
instance A.FromJSON ProtocolMagicId where
parseJSON v = ProtocolMagicId <$> A.parseJSON v
getProtocolMagicId :: AProtocolMagic a -> ProtocolMagicId
getProtocolMagicId = unAnnotated . getAProtocolMagicId
-- mhueschen: For backwards-compatibility reasons, I redefine this function
-- in terms of the two record accessors.
getProtocolMagic :: AProtocolMagic a -> Word32
getProtocolMagic = unProtocolMagicId . getProtocolMagicId
instance A.ToJSON ProtocolMagic where
toJSON (AProtocolMagic (Annotated (ProtocolMagicId ident) ()) rnm) =
A.object ["pm" .= ident, "requiresNetworkMagic" .= rnm]
instance A.FromJSON ProtocolMagic where
parseJSON = A.withObject "ProtocolMagic" $ \o ->
AProtocolMagic
<$> o
.: "pm"
<*> o
.: "requiresNetworkMagic"
-- Canonical JSON instances
instance Monad m => ToJSON m ProtocolMagicId where
toJSON (ProtocolMagicId ident) = toJSON ident
instance MonadError SchemaError m => FromJSON m ProtocolMagicId where
fromJSON v = ProtocolMagicId <$> fromJSON v
--------------------------------------------------------------------------------
-- RequiresNetworkMagic
--------------------------------------------------------------------------------
-- | Bool-isomorphic flag indicating whether we're on testnet
-- or mainnet/staging.
type RequiresNetworkMagic :: Type
data RequiresNetworkMagic
= RequiresNoMagic
| RequiresMagic
deriving (Show, Eq, Generic, NFData, NoThunks)
instance ToCBOR RequiresNetworkMagic where
toCBOR = toByronCBOR
instance FromCBOR RequiresNetworkMagic where
fromCBOR = fromByronCBOR
instance EncCBOR RequiresNetworkMagic where
encCBOR = \case
RequiresNoMagic -> encodeTag 0
RequiresMagic -> encodeTag 1
instance DecCBOR RequiresNetworkMagic where
decCBOR =
decodeTag >>= \case
0 -> return RequiresNoMagic
1 -> return RequiresMagic
tag -> fail $ "RequiresNetworkMagic: unknown tag " ++ show tag
-- Aeson JSON instances
-- N.B @RequiresNetworkMagic@'s ToJSON & FromJSON instances do not round-trip.
-- They should only be used from a parent instance which handles the
-- `requiresNetworkMagic` key.
instance A.ToJSON RequiresNetworkMagic where
toJSON RequiresNoMagic = A.String "RequiresNoMagic"
toJSON RequiresMagic = A.String "RequiresMagic"
instance A.FromJSON RequiresNetworkMagic where
parseJSON =
A.withText "requiresNetworkMagic"
$ toAesonError
. \case
"RequiresNoMagic" -> Right RequiresNoMagic
"RequiresMagic" -> Right RequiresMagic
"NMMustBeNothing" -> Right RequiresNoMagic
"NMMustBeJust" -> Right RequiresMagic
other ->
Left
( "invalid value "
<> other
<> ", acceptable values are RequiresNoMagic | RequiresMagic"
)
-- Canonical JSON instances
instance Monad m => ToJSON m RequiresNetworkMagic where
toJSON RequiresNoMagic = pure (JSString "RequiresNoMagic")
toJSON RequiresMagic = pure (JSString "RequiresMagic")
instance MonadError SchemaError m => FromJSON m RequiresNetworkMagic where
fromJSON = \case
JSString "RequiresNoMagic" -> pure RequiresNoMagic
JSString "RequiresMagic" -> pure RequiresMagic
other ->
expected "RequiresNoMagic | RequiresMagic" (Just $ show other)