Skip to content

Commit 2eb2374

Browse files
committed
Implement serialization to CBOR of StakeCertificateObject
1 parent 3aa659c commit 2eb2374

File tree

1 file changed

+123
-51
lines changed

1 file changed

+123
-51
lines changed
Lines changed: 123 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE ExistentialQuantification #-}
2+
{-# LANGUAGE FlexibleContexts #-}
23
{-# LANGUAGE GADTs #-}
34
{-# LANGUAGE NamedFieldPuns #-}
45
{-# LANGUAGE RankNTypes #-}
@@ -7,27 +8,50 @@
78

89
module Cardano.Wasm.Api.Certificate.StakeCertificate
910
( StakeCertificateObject (..)
10-
, createStakeKeyCertificate
11-
, asStakeRegistration
12-
, asStakeUnregistration
13-
, asDelegateOnly
14-
, withDeposit
15-
, withoutDeposit
16-
, withDelegation
17-
, withoutDelegation
11+
, createStakeKeyCertificateImpl
12+
, asStakeRegistrationImpl
13+
, asStakeUnregistrationImpl
14+
, asDelegateOnlyImpl
15+
, withDepositImpl
16+
, withoutDepositImpl
17+
, withDelegationImpl
18+
, withoutDelegationImpl
19+
, toCborImpl
1820
)
1921
where
2022

21-
import Cardano.Api (Coin, Hash, PoolId, StakeKey, ToJSON (..))
23+
import Cardano.Api
24+
( Coin
25+
, Hash
26+
, PoolId
27+
, StakeKey
28+
, ToJSON (..)
29+
, convert
30+
, conwayEraOnwardsConstraints
31+
, serialiseToCBOR
32+
, unStakeKeyHash
33+
, unStakePoolKeyHash
34+
)
2235
import Cardano.Api.Experimental (Era (..), obtainCommonConstraints)
2336
import Cardano.Api.Experimental qualified as Exp
37+
import Cardano.Api.Experimental.Certificate (Certificate (..))
38+
import Cardano.Api.Ledger
39+
( ConwayDelegCert (..)
40+
, ConwayTxCert (..)
41+
, Credential (..)
42+
, maybeToStrictMaybe
43+
)
2444
import Cardano.Api.Serialise.Raw qualified as Api
2545

46+
import Cardano.Ledger.Api (Delegatee (..))
2647
import Cardano.Wasm.ExceptionHandling (rightOrError, throwError, toMonadFail)
2748

49+
import Control.Monad.Catch (MonadThrow)
2850
import Data.Aeson (FromJSON, (.:), (.=))
2951
import Data.Aeson qualified as Aeson
52+
import Data.ByteString.Base16 qualified as Base16
3053
import Data.Text (Text)
54+
import Data.Text qualified as Text
3155
import Data.Text.Encoding qualified as Text
3256

3357
data StakeCertificateAction
@@ -36,25 +60,20 @@ data StakeCertificateAction
3660
| DelegateOnly
3761
deriving (Show, Eq)
3862

39-
data Delegation
40-
= NoDelegation
41-
| DelegateToPool PoolId
42-
-- ToDo: Add DRep delegation
43-
deriving (Show, Eq)
44-
4563
data StakeCertificateObject
4664
= forall era. StakeCertificateObject
47-
{ era :: !(Era era)
65+
{ era :: !(Exp.Era era)
4866
, stakeCredential :: !(Hash StakeKey) -- ToDo: Generalize to support scripts as well
4967
, deposit :: !(Maybe Coin)
5068
, action :: !StakeCertificateAction
51-
, delegation :: Delegation
69+
, delegateStake :: Maybe PoolId
70+
-- ToDo: Add DRep delegation
5271
}
5372

5473
deriving instance Show StakeCertificateObject
5574

5675
instance ToJSON StakeCertificateObject where
57-
toJSON (StakeCertificateObject{era, stakeCredential, deposit, action, delegation}) =
76+
toJSON (StakeCertificateObject{era, stakeCredential, deposit, action, delegateStake}) =
5877
obtainCommonConstraints era $
5978
Aeson.object
6079
[ "era" .= Exp.Some era
@@ -64,9 +83,7 @@ instance ToJSON StakeCertificateObject where
6483
RegisterStake -> Aeson.String "RegisterStake"
6584
UnregisterStake -> Aeson.String "UnregisterStake"
6685
DelegateOnly -> Aeson.String "DelegateOnly"
67-
, "delegation" .= case delegation of
68-
NoDelegation -> Aeson.String "NoDelegation"
69-
DelegateToPool pid -> Aeson.object ["DelegateToPool" .= show pid]
86+
, "delegateStake" .= fmap (Text.decodeUtf8 . Api.serialiseToRawBytesHex) delegateStake
7087
]
7188

7289
instance FromJSON StakeCertificateObject where
@@ -85,73 +102,128 @@ instance FromJSON StakeCertificateObject where
85102
"UnregisterStake" -> return UnregisterStake
86103
"DelegateOnly" -> return DelegateOnly
87104
_ -> toMonadFail $ throwError ("Invalid action for StakeCertificateObject: " ++ show actionStr)
88-
delegationVal <- o .: "delegation"
89-
delegation <-
90-
case delegationVal of
91-
Aeson.String "NoDelegation" -> return NoDelegation
92-
Aeson.Object obj -> do
93-
pidStr :: Text <- obj .: "DelegateToPool"
94-
DelegateToPool
95-
<$> toMonadFail (rightOrError $ Api.deserialiseFromRawBytesHex $ Text.encodeUtf8 pidStr)
96-
_ ->
97-
toMonadFail $ throwError ("Invalid delegation for StakeCertificateObject: " ++ show delegationVal)
105+
delegateStakeText :: Maybe Text <- o .: "delegateStake"
106+
delegateStake :: Maybe PoolId <-
107+
traverse
108+
( toMonadFail
109+
. rightOrError
110+
. Api.deserialiseFromRawBytesHex
111+
. Text.encodeUtf8
112+
)
113+
delegateStakeText
98114
obtainCommonConstraints era $
99115
return $
100116
StakeCertificateObject
101117
{ era
102118
, stakeCredential
103119
, deposit
104120
, action
105-
, delegation
121+
, delegateStake
106122
}
107123

108124
-- | Creates an empty stake certificate object for the given stake key hash.
109125
-- For the certificate to be valid must be either a registration, an unregistration or
110126
-- a delegation certificate. But it can be both registration and delegation.
111-
createStakeKeyCertificate :: Hash StakeKey -> StakeCertificateObject
112-
createStakeKeyCertificate skHash =
127+
createStakeKeyCertificateImpl :: Hash StakeKey -> StakeCertificateObject
128+
createStakeKeyCertificateImpl skHash =
113129
StakeCertificateObject
114130
{ era = ConwayEra
115131
, stakeCredential = skHash
116132
, deposit = Nothing
117133
, action = DelegateOnly
118-
, delegation = NoDelegation
134+
, delegateStake = Nothing
119135
}
120136

121137
-- | Marks the certificate as a stake registration certificate.
122-
asStakeRegistration :: StakeCertificateObject -> StakeCertificateObject
123-
asStakeRegistration certObj =
138+
asStakeRegistrationImpl :: StakeCertificateObject -> StakeCertificateObject
139+
asStakeRegistrationImpl certObj =
124140
certObj{action = RegisterStake}
125141

126142
-- | Marks the certificate as a stake un-registration certificate.
127-
asStakeUnregistration :: StakeCertificateObject -> StakeCertificateObject
128-
asStakeUnregistration certObj =
143+
asStakeUnregistrationImpl :: StakeCertificateObject -> StakeCertificateObject
144+
asStakeUnregistrationImpl certObj =
129145
certObj{action = UnregisterStake}
130146

131147
-- | Marks the certificate as a delegation-only certificate (not registration nor un-registration).
132-
asDelegateOnly :: StakeCertificateObject -> StakeCertificateObject
133-
asDelegateOnly certObj =
148+
asDelegateOnlyImpl :: StakeCertificateObject -> StakeCertificateObject
149+
asDelegateOnlyImpl certObj =
134150
certObj{action = DelegateOnly}
135151

136152
-- | Sets the deposit for the stake certificate. This only has effect for stake registration
137153
-- and unregistration certificates. The amount must match the expected deposit amount specified by
138154
-- 'ppKeyDepositL' in the protocol parameters for registration certificates and the amount
139155
-- depositted for unregistration certificates.
140-
withDeposit :: Coin -> StakeCertificateObject -> StakeCertificateObject
141-
withDeposit dep certObj =
156+
withDepositImpl :: Coin -> StakeCertificateObject -> StakeCertificateObject
157+
withDepositImpl dep certObj =
142158
certObj{deposit = Just dep}
143159

144160
-- | Resets the deposit for the stake certificate.
145-
withoutDeposit :: StakeCertificateObject -> StakeCertificateObject
146-
withoutDeposit certObj =
161+
withoutDepositImpl :: StakeCertificateObject -> StakeCertificateObject
162+
withoutDepositImpl certObj =
147163
certObj{deposit = Nothing}
148164

149165
-- | Sets the pool to which the stake key will be delegated.
150-
withDelegation :: PoolId -> StakeCertificateObject -> StakeCertificateObject
151-
withDelegation poolId certObj =
152-
certObj{delegation = DelegateToPool poolId}
166+
withDelegationImpl :: PoolId -> StakeCertificateObject -> StakeCertificateObject
167+
withDelegationImpl poolId certObj =
168+
certObj{delegateStake = Just poolId}
153169

154170
-- | Resets the delegation for the stake certificate.
155-
withoutDelegation :: StakeCertificateObject -> StakeCertificateObject
156-
withoutDelegation certObj =
157-
certObj{delegation = NoDelegation}
171+
withoutDelegationImpl :: StakeCertificateObject -> StakeCertificateObject
172+
withoutDelegationImpl certObj =
173+
certObj{delegateStake = Nothing}
174+
175+
-- | Convert a StakeCertificateObject to the base16 encoding of its CBOR representation.
176+
toCborImpl :: MonadThrow m => StakeCertificateObject -> m String
177+
toCborImpl
178+
( StakeCertificateObject
179+
{ era
180+
, stakeCredential
181+
, deposit
182+
, action
183+
, delegateStake
184+
}
185+
) = do
186+
stakeCert <- toCardanoApiCertificate era stakeCredential deposit action delegateStake
187+
return $
188+
obtainCommonConstraints era $
189+
Text.unpack $
190+
Text.decodeUtf8 $
191+
Base16.encode $
192+
serialiseToCBOR
193+
stakeCert
194+
195+
toCardanoApiCertificate
196+
:: MonadThrow m
197+
=> Exp.Era era
198+
-> Hash StakeKey
199+
-> Maybe Coin
200+
-> StakeCertificateAction
201+
-> Maybe PoolId
202+
-> m (Certificate (Exp.LedgerEra era))
203+
toCardanoApiCertificate era stakeCredential deposit action delegateStake =
204+
Exp.obtainCommonConstraints era $
205+
conwayEraOnwardsConstraints (convert era) $
206+
Certificate . ConwayTxCertDeleg
207+
<$> ( case (action, delegateStake) of
208+
(DelegateOnly, Nothing) ->
209+
throwError
210+
"Certificate must at least either: register, unregister, or delegate"
211+
(RegisterStake, Nothing) ->
212+
return $ ConwayRegCert (KeyHashObj $ unStakeKeyHash stakeCredential) (maybeToStrictMaybe deposit)
213+
(UnregisterStake, Nothing) ->
214+
return $ ConwayUnRegCert (KeyHashObj $ unStakeKeyHash stakeCredential) (maybeToStrictMaybe deposit)
215+
(DelegateOnly, Just poolId) ->
216+
return $
217+
ConwayDelegCert
218+
(KeyHashObj $ unStakeKeyHash stakeCredential)
219+
(DelegStake $ unStakePoolKeyHash poolId)
220+
(RegisterStake, Just poolId) ->
221+
ConwayRegDelegCert
222+
(KeyHashObj $ unStakeKeyHash stakeCredential)
223+
(DelegStake $ unStakePoolKeyHash poolId)
224+
<$> case deposit of
225+
Just dep -> return dep
226+
Nothing -> throwError "Deposit must be specified for stake registration and delegation certificate"
227+
(UnregisterStake, Just _) ->
228+
throwError "Cannot unregister and delegate in the same certificate"
229+
)

0 commit comments

Comments
 (0)