-
Notifications
You must be signed in to change notification settings - Fork 23
Implement Cip129
class
#778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Jimbo4350
wants to merge
6
commits into
master
Choose a base branch
from
jordan/cip-129
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
6d9cc7f
Implement CIP129 class
Jimbo4350 09eeece
Adddressing reviewa
Jimbo4350 105a6c0
Reorganize Orphans module to avoid cyclical dependencies
Jimbo4350 851e397
Rename class to Cip129
Jimbo4350 a9312df
Return HumanReadablePart instead of Text
Jimbo4350 97714e9
Address review
Jimbo4350 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE DefaultSignatures #-} | ||
{-# LANGUAGE FlexibleInstances #-} | ||
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE RankNTypes #-} | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
{-# LANGUAGE TypeFamilies #-} | ||
{-# OPTIONS_GHC -Wno-orphans #-} | ||
|
||
module Cardano.Api.Internal.CIP.Cip129 | ||
( Cip129 (..) | ||
, deserialiseFromBech32CIP129 | ||
, serialiseToBech32Cip129 | ||
, serialiseGovActionIdToBech32CIP129 | ||
, deserialiseGovActionIdFromBech32CIP129 | ||
, AsType (AsColdCommitteeCredential, AsDrepCredential, AsHotCommitteeCredential) | ||
) | ||
where | ||
|
||
import Cardano.Api.Internal.Governance.Actions.ProposalProcedure | ||
import Cardano.Api.Internal.HasTypeProxy | ||
import Cardano.Api.Internal.Orphans (AsType (..)) | ||
import Cardano.Api.Internal.SerialiseBech32 | ||
import Cardano.Api.Internal.SerialiseRaw | ||
import Cardano.Api.Internal.TxIn | ||
import Cardano.Api.Internal.Utils | ||
|
||
import Cardano.Ledger.Conway.Governance qualified as Gov | ||
import Cardano.Ledger.Credential (Credential (..)) | ||
import Cardano.Ledger.Credential qualified as L | ||
import Cardano.Ledger.Keys qualified as L | ||
|
||
import Codec.Binary.Bech32 qualified as Bech32 | ||
import Control.Monad (guard) | ||
import Data.ByteString (ByteString) | ||
import Data.ByteString qualified as BS | ||
import Data.ByteString.Base16 qualified as Base16 | ||
import Data.ByteString.Char8 qualified as C8 | ||
import Data.Text (Text) | ||
import Data.Text.Encoding qualified as Text | ||
import GHC.Exts (IsList (..)) | ||
|
||
-- | Cip-129 is a typeclass that captures the serialisation requirements of https://cips.cardano.org/cip/CIP-0129 | ||
-- which pertain to governance credentials and governance action ids. | ||
class (SerialiseAsRawBytes a, HasTypeProxy a) => Cip129 a where | ||
-- | The human readable part of the Bech32 encoding for the credential. | ||
cip129Bech32PrefixFor :: AsType a -> Bech32.HumanReadablePart | ||
|
||
-- | The header byte that identifies the credential type according to Cip-129. | ||
cip129HeaderHexByte :: a -> ByteString | ||
|
||
-- | Permitted bech32 prefixes according to Cip-129. | ||
cip129Bech32PrefixesPermitted :: AsType a -> [Text] | ||
default cip129Bech32PrefixesPermitted :: AsType a -> [Text] | ||
cip129Bech32PrefixesPermitted = return . Bech32.humanReadablePartToText . cip129Bech32PrefixFor | ||
|
||
-- | The human readable part of the Bech32 encoding for the credential. This will | ||
-- error if the prefix is not valid. | ||
unsafeHumanReadablePartFromText :: Text -> Bech32.HumanReadablePart | ||
Jimbo4350 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
unsafeHumanReadablePartFromText = | ||
either (error . ("Error while parsing Bech32: " <>) . show) id | ||
. Bech32.humanReadablePartFromText | ||
|
||
instance Cip129 (Credential L.ColdCommitteeRole) where | ||
cip129Bech32PrefixFor _ = unsafeHumanReadablePartFromText "cc_cold" | ||
cip129Bech32PrefixesPermitted AsColdCommitteeCredential = ["cc_cold"] | ||
|
||
cip129HeaderHexByte = | ||
BS.singleton . \case | ||
L.KeyHashObj{} -> 0x12 -- 0001 0010 | ||
L.ScriptHashObj{} -> 0x13 -- 0001 0011 | ||
|
||
instance Cip129 (Credential L.HotCommitteeRole) where | ||
cip129Bech32PrefixFor _ = unsafeHumanReadablePartFromText "cc_hot" | ||
cip129Bech32PrefixesPermitted AsHotCommitteeCredential = ["cc_hot"] | ||
cip129HeaderHexByte = | ||
BS.singleton . \case | ||
L.KeyHashObj{} -> 0x02 -- 0000 0010 | ||
L.ScriptHashObj{} -> 0x03 -- 0000 0011 | ||
|
||
instance Cip129 (Credential L.DRepRole) where | ||
cip129Bech32PrefixFor _ = unsafeHumanReadablePartFromText "drep" | ||
cip129Bech32PrefixesPermitted AsDrepCredential = ["drep"] | ||
cip129HeaderHexByte = | ||
BS.singleton . \case | ||
L.KeyHashObj{} -> 0x22 -- 0010 0010 | ||
L.ScriptHashObj{} -> 0x23 -- 0010 0011 | ||
|
||
-- | Serialize a accoding to the serialisation requirements of https://cips.cardano.org/cip/CIP-0129 | ||
-- which currently pertain to governance credentials. Governance action ids are dealt separately with | ||
-- via 'serialiseGovActionIdToBech32CIP129'. | ||
serialiseToBech32Cip129 :: forall a. Cip129 a => a -> Text | ||
serialiseToBech32Cip129 a = | ||
Bech32.encodeLenient | ||
humanReadablePart | ||
(Bech32.dataPartFromBytes (cip129HeaderHexByte a <> serialiseToRawBytes a)) | ||
where | ||
humanReadablePart = cip129Bech32PrefixFor (proxyToAsType (Proxy :: Proxy a)) | ||
|
||
deserialiseFromBech32CIP129 | ||
:: Cip129 a | ||
=> AsType a -> Text -> Either Bech32DecodeError a | ||
deserialiseFromBech32CIP129 asType bech32Str = do | ||
(prefix, dataPart) <- | ||
Bech32.decodeLenient bech32Str | ||
?!. Bech32DecodingError | ||
|
||
let actualPrefix = Bech32.humanReadablePartToText prefix | ||
permittedPrefixes = cip129Bech32PrefixesPermitted asType | ||
guard (actualPrefix `elem` permittedPrefixes) | ||
?! Bech32UnexpectedPrefix actualPrefix (fromList permittedPrefixes) | ||
|
||
payload <- | ||
Bech32.dataPartToBytes dataPart | ||
?! Bech32DataPartToBytesError (Bech32.dataPartToText dataPart) | ||
|
||
(header, credential) <- | ||
case C8.uncons payload of | ||
Just (header, credential) -> return (C8.singleton header, credential) | ||
Nothing -> Left $ Bech32DeserialiseFromBytesError payload | ||
|
||
value <- case deserialiseFromRawBytes asType credential of | ||
Right a -> Right a | ||
Left _ -> Left $ Bech32DeserialiseFromBytesError payload | ||
|
||
let expectedHeader = cip129HeaderHexByte value | ||
|
||
guard (header == expectedHeader) | ||
?! Bech32UnexpectedHeader (toBase16Text expectedHeader) (toBase16Text header) | ||
|
||
let expectedPrefix = Bech32.humanReadablePartToText $ cip129Bech32PrefixFor asType | ||
guard (actualPrefix == expectedPrefix) | ||
?! Bech32WrongPrefix actualPrefix expectedPrefix | ||
|
||
return value | ||
where | ||
toBase16Text = Text.decodeUtf8 . Base16.encode | ||
|
||
-- | Governance Action ID | ||
-- According to Cip129 there is no header byte for GovActionId. | ||
-- Instead they append the txid and index to form the payload. | ||
serialiseGovActionIdToBech32CIP129 :: Gov.GovActionId -> Text | ||
serialiseGovActionIdToBech32CIP129 (Gov.GovActionId txid index) = | ||
let txidHex = serialiseToRawBytes $ fromShelleyTxId txid | ||
indexHex = C8.pack $ show $ Gov.unGovActionIx index | ||
payload = txidHex <> indexHex | ||
in Bech32.encodeLenient | ||
humanReadablePart | ||
(Bech32.dataPartFromBytes payload) | ||
where | ||
humanReadablePart = | ||
let prefix = "gov_action" | ||
in case Bech32.humanReadablePartFromText prefix of | ||
Right p -> p | ||
Left err -> | ||
error $ | ||
"serialiseGovActionIdToBech32CIP129: invalid prefix " | ||
++ show prefix | ||
++ ", " | ||
++ show err | ||
|
||
deserialiseGovActionIdFromBech32CIP129 | ||
:: Text -> Either Bech32DecodeError Gov.GovActionId | ||
deserialiseGovActionIdFromBech32CIP129 bech32Str = do | ||
let permittedPrefixes = ["gov_action"] | ||
(prefix, dataPart) <- | ||
Bech32.decodeLenient bech32Str | ||
?!. Bech32DecodingError | ||
let actualPrefix = Bech32.humanReadablePartToText prefix | ||
guard (actualPrefix `elem` permittedPrefixes) | ||
?! Bech32UnexpectedPrefix actualPrefix (fromList permittedPrefixes) | ||
|
||
payload <- | ||
Bech32.dataPartToBytes dataPart | ||
?! Bech32DataPartToBytesError (Bech32.dataPartToText dataPart) | ||
|
||
case deserialiseFromRawBytes AsGovActionId payload of | ||
Right a -> Right a | ||
Left _ -> Left $ Bech32DeserialiseFromBytesError payload |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2102,6 +2102,7 @@ instance HasTextEnvelope (SigningKey DRepKey) where | |
--- | ||
--- Drep extended keys | ||
--- | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only whitespace change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, just adjusting to conform to the other headings in the module. |
||
data DRepExtendedKey | ||
|
||
instance HasTypeProxy DRepExtendedKey where | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth double checking that all exported symbols have haddocks