1
1
{-# LANGUAGE DataKinds #-}
2
2
{-# LANGUAGE DefaultSignatures #-}
3
3
{-# LANGUAGE FlexibleInstances #-}
4
+ {-# LANGUAGE LambdaCase #-}
4
5
{-# LANGUAGE RankNTypes #-}
5
6
{-# LANGUAGE ScopedTypeVariables #-}
6
7
{-# LANGUAGE TypeFamilies #-}
7
8
{-# OPTIONS_GHC -Wno-orphans #-}
8
9
9
- module Cardano.Api.Internal.CIP.CIP129
10
- ( CIP129 (.. )
10
+ module Cardano.Api.Internal.CIP.Cip129
11
+ ( Cip129 (.. )
11
12
, deserialiseFromBech32CIP129
12
- , serialiseToBech32CIP129
13
+ , serialiseToBech32Cip129
13
14
, serialiseGovActionIdToBech32CIP129
14
15
, deserialiseGovActionIdFromBech32CIP129
16
+ , AsType (AsColdCommitteeCredential , AsDrepCredential , AsHotCommitteeCredential )
15
17
)
16
18
where
17
19
18
20
import Cardano.Api.Internal.Governance.Actions.ProposalProcedure
19
21
import Cardano.Api.Internal.HasTypeProxy
20
- import Cardano.Api.Internal.Orphans ()
22
+ import Cardano.Api.Internal.Orphans (AsType ( .. ) )
21
23
import Cardano.Api.Internal.SerialiseBech32
22
24
import Cardano.Api.Internal.SerialiseRaw
23
25
import Cardano.Api.Internal.TxIn
24
26
import Cardano.Api.Internal.Utils
25
27
26
- import Cardano.Binary qualified as CBOR
27
28
import Cardano.Ledger.Conway.Governance qualified as Gov
28
29
import Cardano.Ledger.Credential (Credential (.. ))
29
30
import Cardano.Ledger.Credential qualified as L
30
31
import Cardano.Ledger.Keys qualified as L
31
32
32
33
import Codec.Binary.Bech32 qualified as Bech32
33
34
import Control.Monad (guard )
34
- import Data.Bifunctor
35
35
import Data.ByteString (ByteString )
36
36
import Data.ByteString qualified as BS
37
37
import Data.ByteString.Base16 qualified as Base16
38
38
import Data.ByteString.Char8 qualified as C8
39
39
import Data.Text (Text )
40
40
import Data.Text.Encoding qualified as Text
41
41
import GHC.Exts (IsList (.. ))
42
- import Text.Read
43
42
44
- -- | CIP129 is a typeclass that captures the serialisation requirements of https://cips.cardano.org/cip/CIP-0129
45
- class (SerialiseAsRawBytes a , HasTypeProxy a ) => CIP129 a where
46
- cip129Bech32PrefixFor :: AsType a -> Text
43
+ -- | Cip-129 is a typeclass that captures the serialisation requirements of https://cips.cardano.org/cip/CIP-0129
44
+ -- which pertain to governance credentials and governance action ids.
45
+ class (SerialiseAsRawBytes a , HasTypeProxy a ) => Cip129 a where
46
+ -- | The human readable part of the Bech32 encoding for the credential.
47
+ cip129Bech32PrefixFor :: AsType a -> Bech32. HumanReadablePart
47
48
49
+ -- | The header byte that identifies the credential type according to Cip-129.
48
50
cip129HeaderHexByte :: a -> ByteString
49
51
52
+ -- | Permitted bech32 prefixes according to Cip-129.
50
53
cip129Bech32PrefixesPermitted :: AsType a -> [Text ]
51
54
default cip129Bech32PrefixesPermitted :: AsType a -> [Text ]
52
- cip129Bech32PrefixesPermitted = return . cip129Bech32PrefixFor
55
+ cip129Bech32PrefixesPermitted = return . Bech32. humanReadablePartToText . cip129Bech32PrefixFor
53
56
54
- instance CIP129 (Credential L. ColdCommitteeRole ) where
55
- cip129Bech32PrefixFor _ = " cc_cold"
57
+ -- | The human readable part of the Bech32 encoding for the credential. This will
58
+ -- error if the prefix is not valid.
59
+ unsafeHumanReadablePartFromText :: Text -> Bech32. HumanReadablePart
60
+ unsafeHumanReadablePartFromText =
61
+ either (error . (" Error while parsing Bech32: " <> ) . show ) id
62
+ . Bech32. humanReadablePartFromText
63
+
64
+ instance Cip129 (Credential L. ColdCommitteeRole ) where
65
+ cip129Bech32PrefixFor _ = unsafeHumanReadablePartFromText " cc_cold"
56
66
cip129Bech32PrefixesPermitted AsColdCommitteeCredential = [" cc_cold" ]
57
- cip129HeaderHexByte c =
58
- case c of
59
- L. KeyHashObj {} -> BS. singleton 0x12 -- 0001 0010
60
- L. ScriptHashObj {} -> BS. singleton 0x13 -- 0001 0011
61
-
62
- instance HasTypeProxy (Credential L. ColdCommitteeRole ) where
63
- data AsType (Credential L. ColdCommitteeRole ) = AsColdCommitteeCredential
64
- proxyToAsType _ = AsColdCommitteeCredential
65
-
66
- instance SerialiseAsRawBytes (Credential L. ColdCommitteeRole ) where
67
- serialiseToRawBytes = CBOR. serialize'
68
- deserialiseFromRawBytes AsColdCommitteeCredential =
69
- first
70
- ( \ e ->
71
- SerialiseAsRawBytesError
72
- (" Unable to deserialise Credential ColdCommitteeRole: " ++ show e)
73
- )
74
- . CBOR. decodeFull'
75
-
76
- instance CIP129 (Credential L. HotCommitteeRole ) where
77
- cip129Bech32PrefixFor _ = " cc_hot"
67
+
68
+ cip129HeaderHexByte =
69
+ BS. singleton . \ case
70
+ L. KeyHashObj {} -> 0x12 -- 0001 0010
71
+ L. ScriptHashObj {} -> 0x13 -- 0001 0011
72
+
73
+ instance Cip129 (Credential L. HotCommitteeRole ) where
74
+ cip129Bech32PrefixFor _ = unsafeHumanReadablePartFromText " cc_hot"
78
75
cip129Bech32PrefixesPermitted AsHotCommitteeCredential = [" cc_hot" ]
79
- cip129HeaderHexByte c =
80
- case c of
81
- L. KeyHashObj {} -> BS. singleton 0x02 -- 0000 0010
82
- L. ScriptHashObj {} -> BS. singleton 0x03 -- 0000 0011
83
-
84
- instance HasTypeProxy (Credential L. HotCommitteeRole ) where
85
- data AsType (Credential L. HotCommitteeRole ) = AsHotCommitteeCredential
86
- proxyToAsType _ = AsHotCommitteeCredential
87
-
88
- instance SerialiseAsRawBytes (Credential L. HotCommitteeRole ) where
89
- serialiseToRawBytes = CBOR. serialize'
90
- deserialiseFromRawBytes AsHotCommitteeCredential =
91
- first
92
- ( \ e ->
93
- SerialiseAsRawBytesError
94
- (" Unable to deserialise Credential HotCommitteeRole: " ++ show e)
95
- )
96
- . CBOR. decodeFull'
97
-
98
- instance CIP129 (Credential L. DRepRole ) where
99
- cip129Bech32PrefixFor _ = " drep"
76
+ cip129HeaderHexByte =
77
+ BS. singleton . \ case
78
+ L. KeyHashObj {} -> 0x02 -- 0000 0010
79
+ L. ScriptHashObj {} -> 0x03 -- 0000 0011
80
+
81
+ instance Cip129 (Credential L. DRepRole ) where
82
+ cip129Bech32PrefixFor _ = unsafeHumanReadablePartFromText " drep"
100
83
cip129Bech32PrefixesPermitted AsDrepCredential = [" drep" ]
101
- cip129HeaderHexByte c =
102
- case c of
103
- L. KeyHashObj {} -> BS. singleton 0x22 -- 0010 0010
104
- L. ScriptHashObj {} -> BS. singleton 0x23 -- 0010 0011
105
-
106
- instance HasTypeProxy (Credential L. DRepRole ) where
107
- data AsType (Credential L. DRepRole ) = AsDrepCredential
108
- proxyToAsType _ = AsDrepCredential
109
-
110
- instance SerialiseAsRawBytes (Credential L. DRepRole ) where
111
- serialiseToRawBytes = CBOR. serialize'
112
- deserialiseFromRawBytes AsDrepCredential =
113
- first
114
- ( \ e ->
115
- SerialiseAsRawBytesError (" Unable to deserialise Credential DRepRole: " ++ show e)
116
- )
117
- . CBOR. decodeFull'
118
-
119
- serialiseToBech32CIP129 :: forall a . CIP129 a => a -> Text
120
- serialiseToBech32CIP129 a =
84
+ cip129HeaderHexByte =
85
+ BS. singleton . \ case
86
+ L. KeyHashObj {} -> 0x22 -- 0010 0010
87
+ L. ScriptHashObj {} -> 0x23 -- 0010 0011
88
+
89
+ -- | Serialize a accoding to the serialisation requirements of https://cips.cardano.org/cip/CIP-0129
90
+ -- which currently pertain to governance credentials. Governance action ids are dealt separately with
91
+ -- via 'serialiseGovActionIdToBech32CIP129'.
92
+ serialiseToBech32Cip129 :: forall a . Cip129 a => a -> Text
93
+ serialiseToBech32Cip129 a =
121
94
Bech32. encodeLenient
122
95
humanReadablePart
123
96
(Bech32. dataPartFromBytes (cip129HeaderHexByte a <> serialiseToRawBytes a))
124
97
where
125
- prefix = cip129Bech32PrefixFor (proxyToAsType (Proxy :: Proxy a ))
126
- humanReadablePart =
127
- case Bech32. humanReadablePartFromText prefix of
128
- Right p -> p
129
- Left err ->
130
- error $
131
- " serialiseToBech32CIP129: invalid prefix "
132
- ++ show prefix
133
- ++ " , "
134
- ++ show err
98
+ humanReadablePart = cip129Bech32PrefixFor (proxyToAsType (Proxy :: Proxy a ))
135
99
136
100
deserialiseFromBech32CIP129
137
- :: CIP129 a
101
+ :: Cip129 a
138
102
=> AsType a -> Text -> Either Bech32DecodeError a
139
103
deserialiseFromBech32CIP129 asType bech32Str = do
140
104
(prefix, dataPart) <-
@@ -150,7 +114,10 @@ deserialiseFromBech32CIP129 asType bech32Str = do
150
114
Bech32. dataPartToBytes dataPart
151
115
?! Bech32DataPartToBytesError (Bech32. dataPartToText dataPart)
152
116
153
- let (header, credential) = BS. uncons payload
117
+ (header, credential) <-
118
+ case C8. uncons payload of
119
+ Just (header, credential) -> return (C8. singleton header, credential)
120
+ Nothing -> Left $ Bech32DeserialiseFromBytesError payload
154
121
155
122
value <- case deserialiseFromRawBytes asType credential of
156
123
Right a -> Right a
@@ -161,7 +128,7 @@ deserialiseFromBech32CIP129 asType bech32Str = do
161
128
guard (header == expectedHeader)
162
129
?! Bech32UnexpectedHeader (toBase16Text expectedHeader) (toBase16Text header)
163
130
164
- let expectedPrefix = cip129Bech32PrefixFor asType
131
+ let expectedPrefix = Bech32. humanReadablePartToText $ cip129Bech32PrefixFor asType
165
132
guard (actualPrefix == expectedPrefix)
166
133
?! Bech32WrongPrefix actualPrefix expectedPrefix
167
134
@@ -170,7 +137,7 @@ deserialiseFromBech32CIP129 asType bech32Str = do
170
137
toBase16Text = Text. decodeUtf8 . Base16. encode
171
138
172
139
-- | Governance Action ID
173
- -- According to CIP129 there is no header byte for GovActionId.
140
+ -- According to Cip129 there is no header byte for GovActionId.
174
141
-- Instead they append the txid and index to form the payload.
175
142
serialiseGovActionIdToBech32CIP129 :: Gov. GovActionId -> Text
176
143
serialiseGovActionIdToBech32CIP129 (Gov. GovActionId txid index) =
@@ -210,21 +177,3 @@ deserialiseGovActionIdFromBech32CIP129 bech32Str = do
210
177
case deserialiseFromRawBytes AsGovActionId payload of
211
178
Right a -> Right a
212
179
Left _ -> Left $ Bech32DeserialiseFromBytesError payload
213
-
214
- instance HasTypeProxy Gov. GovActionId where
215
- data AsType Gov. GovActionId = AsGovActionId
216
- proxyToAsType _ = AsGovActionId
217
-
218
- instance SerialiseAsRawBytes Gov. GovActionId where
219
- serialiseToRawBytes (Gov. GovActionId txid (Gov. GovActionIx ix)) =
220
- let hex = Base16. encode $ C8. pack $ show ix
221
- in mconcat [serialiseToRawBytes $ fromShelleyTxId txid, hex]
222
- deserialiseFromRawBytes AsGovActionId bytes = do
223
- let (txidBs, index) = BS. splitAt 32 bytes
224
-
225
- txid <- deserialiseFromRawBytes AsTxId txidBs
226
- let asciiIndex = C8. unpack $ Base16. decodeLenient index
227
- case readMaybe asciiIndex of
228
- Just ix -> return $ Gov. GovActionId (toShelleyTxId txid) (Gov. GovActionIx ix)
229
- Nothing ->
230
- Left $ SerialiseAsRawBytesError $ " Unable to deserialise GovActionId: invalid index: " <> asciiIndex
0 commit comments