-
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
base: master
Are you sure you want to change the base?
Conversation
5f2abfb
to
6245452
Compare
0834fcd
to
baacdf3
Compare
baacdf3
to
53e3eba
Compare
d90b999
to
26c2331
Compare
26c2331
to
5b71d37
Compare
cip129HeaderHexByte c = | ||
case c of | ||
L.KeyHashObj{} -> BS.singleton 0x02 -- 0000 0010 | ||
L.ScriptHashObj{} -> BS.singleton 0x03 -- 0000 0011 |
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.
Might be a use case for Binary Literals.
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.
For instance, the binary integer literal 0b11001001 will be desugared into fromInteger 201 when BinaryLiterals is enabled.
I find fromInteger
+ BinaryLiterals
to be less explicit than 0x
.
@@ -2045,6 +2045,7 @@ instance HasTextEnvelope (SigningKey DRepKey) where | |||
--- | |||
--- Drep extended keys | |||
--- | |||
|
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.
Only whitespace change.
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.
Yep, just adjusting to conform to the other headings in the module.
...den/errors/Cardano.Api.Internal.SerialiseBech32.Bech32DecodeError/Bech32UnexpectedHeader.txt
Show resolved
Hide resolved
error $ | ||
"serialiseToBech32: invalid prefix " |
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.
error
here is a signal that prefix
has an imprecise type. It should be HumanReadablePart
.
If we're going to throw an exception at runtime it's better for it to be as early as possible i.e. at instance definition.
I guess we'd have to copy a helper function:
unsafeHumanReadablePartFromText :: Text -> HumanReadablePart
unsafeHumanReadablePartFromText
= either (error . ("Error while parsing Bech32: " <>) . show) id
. humanReadablePartFromText
and then
instance CIP129 (Credential L.ColdCommitteeRole) where
cip129Bech32PrefixFor _ = unsafeHumanReadablePartFromText "cc_cold"
....
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.
An alternative is to use TemplateHaskell and: https://hackage.haskell.org/package/bech32-th-1.1.8/docs/Codec-Binary-Bech32-TH.html
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.
or type literals magic...
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module MyLib where
import Data.Typeable
import GHC.TypeLits
import Data.Type.Bool
import Data.Type.Equality
-- | A validated Bech32 prefix with constraints encoded in the type
data ValidatedBech32Prefix (chars :: [Char]) where
ValidatedBech32Prefix ::
( AllValidChars chars ~ 'True
, ValidLength (Length chars) ~ 'True
, ConsistentCase chars ~ 'True
) => Proxy chars -> ValidatedBech32Prefix chars
-- Smart constructor
mkBech32Prefix ::
( AllValidChars chars ~ 'True
, ValidLength (Length chars) ~ 'True
, ConsistentCase chars ~ 'True
) => Proxy chars -> ValidatedBech32Prefix chars
mkBech32Prefix = ValidatedBech32Prefix
-- ======================
-- Type-level validation
-- ======================
-- Check if all characters are valid (ASCII 33–126)
type family AllValidChars (cs :: [Char]) :: Bool where
AllValidChars '[] = 'True
AllValidChars (c ': cs) = IsValidChar c && AllValidChars cs
-- Check single character validity
type family IsValidChar (c :: Char) :: Bool where
IsValidChar c = ((CmpChar c '\x21' == LT) == False) -- 33 <= c
&& ((CmpChar c '\x7E' == GT) == False) -- c <= 126
-- Validate length (1–83)
type family ValidLength (len :: Nat) :: Bool where
ValidLength len = ((CmpNat len 1 == LT) == False) -- len >= 1
&& ((CmpNat len 83 == GT) == False) -- len <= 83
-- Calculate length of character list
type family Length (cs :: [Char]) :: Nat where
Length '[] = 0
Length (c ': cs) = 1 + Length cs
-- Enforce consistent case (all lowercase or all uppercase)
type family ConsistentCase (cs :: [Char]) :: Bool where
ConsistentCase '[] = 'True
ConsistentCase (c ': cs) = CaseMatches (FirstCase c cs) c && ConsistentCase cs
-- Determine expected case from first character
type family FirstCase (c :: Char) (cs :: [Char]) :: Bool where
FirstCase c _ = IsLower c
-- Check if character matches expected case
type family CaseMatches (isLower :: Bool) (c :: Char) :: Bool where
CaseMatches 'True c = IsLower c
CaseMatches 'False c = IsUpper c
-- Character case predicates
type family IsLower (c :: Char) :: Bool where
IsLower c = ((CmpChar c 'a' == LT) == False) && ((CmpChar c 'z' == GT) == False)
type family IsUpper (c :: Char) :: Bool where
IsUpper c = ((CmpChar c 'A' == LT) == False) && ((CmpChar c 'Z' == GT) == False)
-- ======================
-- Example Usage
-- ======================
-- Valid prefix (Bitcoin mainnet)
validExample :: ValidatedBech32Prefix ['b', 'c', 't']
validExample = mkBech32Prefix Proxy
-- Valid prefix (Uppercase)
validUpper :: ValidatedBech32Prefix ['T', 'E', 'S', 'T']
validUpper = mkBech32Prefix Proxy
This is from deepseek. I'm quite surprised that it got it almost right at first try.
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
a3d895c
to
48b1878
Compare
48b1878
to
e9bcbab
Compare
@@ -0,0 +1,269 @@ | |||
{-# LANGUAGE DataKinds #-} | |||
{-# LANGUAGE DeriveAnyClass #-} |
Check warning
Code scanning / HLint
Unused LANGUAGE pragma Warning
Found:
{-# LANGUAGE DeriveAnyClass #-}
Perhaps you should remove it.
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE DeriveAnyClass #-} | ||
{-# LANGUAGE DeriveDataTypeable #-} | ||
{-# LANGUAGE DeriveGeneric #-} |
Check warning
Code scanning / HLint
Unused LANGUAGE pragma Warning
Found:
{-# LANGUAGE DeriveGeneric #-}
Perhaps you should remove it.
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE FlexibleInstances #-} | ||
{-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
{-# LANGUAGE NamedFieldPuns #-} |
Check warning
Code scanning / HLint
Unused LANGUAGE pragma Warning
Found:
{-# LANGUAGE NamedFieldPuns #-}
Perhaps you should remove it.
e9bcbab
to
97714e9
Compare
Changelog
Context
Additional context for the PR goes here. If the PR fixes a particular issue please provide a link to the issue.
How to trust this PR
Highlight important bits of the PR that will make the review faster. If there are commands the reviewer can run to observe the new behavior, describe them.
Checklist