-
Notifications
You must be signed in to change notification settings - Fork 721
/
Protocol.hs
229 lines (200 loc) · 8.73 KB
/
Protocol.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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-all-missed-specialisations #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Cardano.Config.Protocol
( Protocol(..)
, SomeProtocol(..)
, fromProtocol
, TraceConstraints
) where
import Cardano.Prelude
import Prelude (error, fail)
import Test.Cardano.Prelude (canonicalDecodePretty)
import Codec.CBOR.Read (deserialiseFromBytes, DeserialiseFailure)
import qualified Data.ByteString.Lazy as LB
import Data.Text (unpack)
import qualified Cardano.Chain.Genesis as Genesis
import qualified Cardano.Chain.Update as Update
import Cardano.Crypto (RequiresNetworkMagic, decodeHash)
import qualified Cardano.Crypto.Signing as Signing
import Ouroboros.Consensus.Block (Header)
import Ouroboros.Consensus.Mempool.API (ApplyTxErr, GenTx, GenTxId)
import Ouroboros.Consensus.Node.ProtocolInfo (NumCoreNodes (..),
PBftLeaderCredentials,
PBftSignatureThreshold(..),
mkPBftLeaderCredentials)
import Ouroboros.Consensus.NodeId (CoreNodeId (..), NodeId (..))
import Ouroboros.Consensus.Node.Run (RunNode)
import Ouroboros.Consensus.Protocol (SecurityParam (..),
PraosParams (..),
PBftParams (..))
import qualified Ouroboros.Consensus.Protocol as Consensus
import qualified Ouroboros.Consensus.Ledger.Byron as Consensus
import Ouroboros.Consensus.Util (Dict(..))
import Ouroboros.Consensus.Util.Condense
import Ouroboros.Network.Block
import Cardano.Config.Types
(DelegationCertFile (..), GenesisFile (..),
LastKnownBlockVersion (..), Update (..),
Protocol (..), SigningKeyFile (..))
-- TODO: consider not throwing this, or wrap it in a local error type here
-- that has proper error messages.
instance Exception Genesis.ConfigurationError
-- | Tracing-related constraints for monitoring purposes.
--
-- When you need a 'Show' or 'Condense' instance for more types, just add the
-- appropriate constraint here. There's no need to modify the consensus
-- code-base, unless the corresponding instance is missing.
type TraceConstraints blk =
( Condense blk
, Condense [blk]
, Condense (Header blk)
, Condense (HeaderHash blk)
, Condense (GenTx blk)
, Show (ApplyTxErr blk)
, Show (GenTx blk)
, Show (GenTxId blk)
, Show blk
, Show (Header blk)
)
{-------------------------------------------------------------------------------
Untyped/typed protocol boundary
-------------------------------------------------------------------------------}
mockSecurityParam :: SecurityParam
mockSecurityParam = SecurityParam 5
-- | Helper for creating a 'SomeProtocol' for a mock protocol that needs the
-- 'CoreNodeId' and NumCoreNodes'. If one of them is missing from the
-- 'CardanoConfiguration', a 'MissingNodeInfo' exception is thrown.
mockSomeProtocol
:: (RunNode blk, TraceConstraints blk)
=> Maybe NodeId
-> Maybe Int
-- ^ Number of core nodes
-> (CoreNodeId -> NumCoreNodes -> Consensus.Protocol blk)
-> IO SomeProtocol
mockSomeProtocol nId mNumCoreNodes mkConsensusProtocol = do
(cid, numCoreNodes) <- either throwIO return $ extractNodeInfo nId mNumCoreNodes
let p = mkConsensusProtocol cid numCoreNodes
case Consensus.runProtocol p of
Dict -> return $ SomeProtocol p
data SomeProtocol where
SomeProtocol :: (RunNode blk, TraceConstraints blk)
=> Consensus.Protocol blk -> SomeProtocol
fromProtocol
:: Text
-> Maybe NodeId
-> Maybe Int
-- ^ Number of core nodes
-> GenesisFile
-> RequiresNetworkMagic
-> Maybe Double
-> Maybe DelegationCertFile
-> Maybe SigningKeyFile
-> Update
-> Protocol
-> IO SomeProtocol
fromProtocol _ _ _ _ _ _ _ _ _ ByronLegacy =
error "Byron Legacy protocol is not implemented."
fromProtocol _ nId mNumCoreNodes _ _ _ _ _ _ BFT =
mockSomeProtocol nId mNumCoreNodes $ \cid numCoreNodes ->
Consensus.ProtocolMockBFT numCoreNodes cid mockSecurityParam
fromProtocol _ nId mNumCoreNodes _ _ _ _ _ _ Praos =
mockSomeProtocol nId mNumCoreNodes $ \cid numCoreNodes ->
Consensus.ProtocolMockPraos numCoreNodes cid PraosParams {
praosSecurityParam = mockSecurityParam
, praosSlotsPerEpoch = 3
, praosLeaderF = 0.5
, praosLifetimeKES = 1000000
}
fromProtocol _ nId mNumCoreNodes _ _ _ _ _ _ MockPBFT =
mockSomeProtocol nId mNumCoreNodes $ \cid numCoreNodes@(NumCoreNodes numNodes) ->
Consensus.ProtocolMockPBFT numCoreNodes cid
PBftParams { pbftSecurityParam = mockSecurityParam
, pbftNumNodes = fromIntegral numNodes
, pbftSignatureThreshold = (1.0 / fromIntegral numNodes) + 0.1
}
fromProtocol gHash _ _ genFile nMagic sigThresh delCertFp sKeyFp update RealPBFT = do
let genHash = either panic identity $ decodeHash gHash
gcE <- runExceptT (Genesis.mkConfigFromFile
nMagic
(unGenesisFile genFile)
genHash
)
gc <- case gcE of
Left err -> panic $ show err
Right x -> pure x
optionalLeaderCredentials <- readLeaderCredentials
gc
delCertFp
sKeyFp
let p = protocolConfigRealPbft update sigThresh gc optionalLeaderCredentials
case Consensus.runProtocol p of
Dict -> return $ SomeProtocol p
-- | The plumbing to select and convert the appropriate configuration subset
-- for the 'RealPBFT' protocol.
--
protocolConfigRealPbft :: Update
-> Maybe Double
-> Genesis.Config
-> Maybe PBftLeaderCredentials
-> Consensus.Protocol Consensus.ByronBlock
protocolConfigRealPbft (Update appName appVer lastKnownBlockVersion)
pbftSignatureThresh
genesis leaderCredentials =
Consensus.ProtocolRealPBFT
genesis
(PBftSignatureThreshold <$> pbftSignatureThresh)
(convertProtocolVersion lastKnownBlockVersion)
(Update.SoftwareVersion appName appVer)
leaderCredentials
where
convertProtocolVersion
LastKnownBlockVersion {lkbvMajor, lkbvMinor, lkbvAlt} =
Update.ProtocolVersion lkbvMajor lkbvMinor lkbvAlt
readLeaderCredentials :: Genesis.Config
-> Maybe DelegationCertFile
-> Maybe SigningKeyFile
-> IO (Maybe PBftLeaderCredentials)
readLeaderCredentials gc mDelCertFp mSKeyFp = do
case (mDelCertFp, mSKeyFp) of
(Nothing, Nothing) -> pure Nothing
(Just _, Nothing) -> panic "Signing key filepath not specified"
(Nothing, Just _) -> panic "Delegation certificate filepath not specified"
(Just delegCertFile, Just signingKeyFile) -> do
signingKeyFileBytes <- LB.readFile $ unSigningKey signingKeyFile
delegCertFileBytes <- LB.readFile $ unDelegationCert delegCertFile
--TODO: review the style of reporting for input validation failures
-- If we use throwIO, we should use a local exception type that
-- wraps the other structured failures and reports them appropriatly
signingKey <- either throwIO return $
deserialiseSigningKey signingKeyFileBytes
delegCert <- either (fail . unpack) return $
canonicalDecodePretty delegCertFileBytes
either throwIO (return . Just)
(mkPBftLeaderCredentials gc signingKey delegCert)
where
deserialiseSigningKey :: LB.ByteString
-> Either DeserialiseFailure Signing.SigningKey
deserialiseSigningKey =
fmap (Signing.SigningKey . snd)
. deserialiseFromBytes Signing.fromCBORXPrv
-- | Info missing from the config needed to run a protocol
data MissingNodeInfo
= MissingCoreNodeId
| MissingNumCoreNodes
deriving (Show, Exception)
extractNodeInfo
:: Maybe NodeId
-> Maybe Int
-> Either MissingNodeInfo (CoreNodeId, NumCoreNodes)
extractNodeInfo mNodeId ncNumCoreNodes = do
coreNodeId <- case mNodeId of
Just (CoreId coreNodeId) -> pure coreNodeId
_ -> Left MissingCoreNodeId
numCoreNodes <- maybe (Left MissingNumCoreNodes) Right ncNumCoreNodes
return (CoreNodeId coreNodeId , NumCoreNodes numCoreNodes)