-
Notifications
You must be signed in to change notification settings - Fork 217
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
Jörmungandr: Separate concern between keyToAddress and get/putAddress #417
Conversation
817e92e
to
62ed3fe
Compare
6d26472
to
6ddaeff
Compare
@@ -102,7 +102,7 @@ genesisBlock = Block genesisHeader | |||
{ inputs = [] | |||
, outputs = | |||
[ TxOut | |||
{ address = Address "3$\195xi\193\"h\154\&5\145}\245:O\"\148\163\165/h^\ENQ\245\248\229;\135\231\234E/" | |||
{ address = Address "\131\&3$\195xi\193\"h\154\&5\145}\245:O\"\148\163\165/h^\ENQ\245\248\229;\135\231\234E/" |
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.
The header byte
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.
👍
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.
👍
instance KeyToAddress (Jormungandr n) where | ||
keyToAddress = undefined | ||
instance forall n. KnownNetwork n => KeyToAddress (Jormungandr n) where | ||
keyToAddress key = Address |
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.
keyToAddress key = Address | |
keyToAddress = Address . addressToByteString . getKey |
I would prefer this to be encapsulated in a function which lives in the Binary module. So that the Put stuff doesn't leak out. Something like:
addressToByteString :: XPub -> ByteString
addressToByteString = BL.toStrict . runPut . putSingleAddress
Up to you though.
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.
Agreed with @rvl here 👍
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.
I went with
singleAddressFromKey :: forall n. KnownNetwork n => Proxy n -> XPub -> Address
singleAddressFromKey _ xpub = Address $ BL.toStrict $ runPut $ do
putWord8 (single @n)
putByteString (unXPub xpub)
--> 5bc2157
Considered naming it something like newSingleAddress
too.
@@ -527,7 +527,7 @@ isPending = (== Pending) . (status :: TxMeta -> TxStatus) . snd | |||
-- layer and that the underlying encoding is rather agnostic to the underlying | |||
-- backend. | |||
newtype Address = Address | |||
{ getAddress :: ByteString | |||
{ unAddress :: ByteString |
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.
I do like unAddress
better than getAddress
.
However there is always the option of name qualification or import hiding if we want to keep the newtype accessors named consistently.
-- be included in the underlying Address ByteString. | ||
headerByte <- label "address header" . lookAhead $ getWord8 | ||
let kind = kindValue headerByte | ||
let _discrimination = discriminationValue headerByte |
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.
What's the use for discriminationValue
here 🤔 ?
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.
Nothing… Unless we would like to check that it matches, but as we get the blocks (including genesis) from the node, that would probably not be worth it
case kind of | ||
-- Single Address | ||
0x3 -> Address <$> getByteString 33 | ||
0x4 -> error "unimplemented group address decoder" |
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.
How big of an effort is it to add support for group address? I expect it to be only Address <$> getByteString 65
?
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.
I suppose that will just work…
We won't be able to discover them in isOurs
though as keyToAddress
only can create one type of addresses (now single).
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.
True although the concern is slightly different here. BIP-44 for group addresses is out of the question for now. Support for delegation and delegation addresses will come later.
instance KeyToAddress (Jormungandr n) where | ||
keyToAddress = undefined | ||
instance forall n. KnownNetwork n => KeyToAddress (Jormungandr n) where | ||
keyToAddress key = Address |
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.
Agreed with @rvl here 👍
-- We use 'lookAhead' to not consume the header, and let it | ||
-- be included in the underlying Address ByteString. | ||
headerByte <- label "address header" . lookAhead $ getWord8 | ||
let kind = kindValue headerByte |
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.
Also, @KtorZ, there is difference in style here with the binary arithmetic:
case kind of
-- Single Address
0x3 -> Address <$> getByteString 33
0x4 -> error "unimplemented group address decoder"
0x5 -> error "unimplemented account address decoder"
0x6 -> error "unimplemented multisig address decoder"
other -> fail $ "Invalid address type: " ++ show other
where
kindValue :: Word8 -> Word8
kindValue = (.&. 0b01111111)
discriminationValue :: Word8 -> Network
discriminationValue b = case b .&. 0b10000000 of
0 -> Mainnet
_ -> Testnet
vs hard-coded single/grouped values specific to each network found in Cardano.Wallet.Jormungandr.Environment
instance KnownNetwork 'Mainnet where
networkVal = Mainnet
hrp = unsafeHumanReadablePart "ca"
single = 0x03
grouped = 0x04
instance KnownNetwork 'Testnet where
networkVal = Testnet
hrp = unsafeHumanReadablePart "ta"
single = 0x83
grouped = 0x84
I'm not sure what should be done. Do we really want to throw the arithmetic away?
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.
It's not about throwing it away and it's not about using hard-coded bytes or extracting them from the binary data. It's about checking that both matches. That's the only reason to have a discriminant version, to make sure that we do not use addresses on the wrong network.
We do already perform this check as part of the encode/decode functions since this is our entry-point for user-facing data. We could also do it in the binary modules when parsing blocks although we do expect the underlying node to do that for us already.
173f5dd
to
5bc2157
Compare
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.
LGTM
af72402
to
b8a0d3b
Compare
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.
lgtm
@@ -267,10 +276,6 @@ putTokenTransfer _ (Tx inputs outputs) = do | |||
putOutput (TxOut address coin) = do | |||
putAddress address | |||
putWord64be $ getCoin coin | |||
putAddress address = do | |||
-- NOTE: only single address supported for now | |||
putWord8 (single @n) |
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.
yes - I was not happy to introduce new constrain just to resolve the network here. Having this bit in address is much more elegant solution
@@ -102,7 +102,7 @@ genesisBlock = Block genesisHeader | |||
{ inputs = [] | |||
, outputs = | |||
[ TxOut | |||
{ address = Address "3$\195xi\193\"h\154\&5\145}\245:O\"\148\163\165/h^\ENQ\245\248\229;\135\231\234E/" | |||
{ address = Address "\131\&3$\195xi\193\"h\154\&5\145}\245:O\"\148\163\165/h^\ENQ\245\248\229;\135\231\234E/" |
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.
👍
5b8354b
to
1c9fbed
Compare
it "encodes (network <> tag <> key) correctly into an address" $ | ||
singleAddressFromKey (Proxy @'Mainnet) pub | ||
`shouldBe` | ||
Address (BS.pack [3] <> pub) |
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.
I'm thinking we can these simple cases here, and test keyToAddress
more extensively together comparing with jcli
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.
Where does the 3
come from here?
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.
let singleAddressOnMainnet = 3
@@ -454,15 +452,26 @@ putAddress addr@(Address bs) | |||
++ ": " ++ show addr | |||
where l = BS.length bs | |||
|
|||
singleAddressFromKey :: forall n. KnownNetwork n => Proxy n -> XPub -> Address | |||
singleAddressFromKey _ xpub = Address $ BL.toStrict $ runPut $ do | |||
singleAddressFromKey :: forall n. KnownNetwork n => Proxy n -> ByteString -> Address |
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.
The type was better before using the XPub
. What's the rationale for choosing a raw ByteString instead?
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.
We don't have a ChainCode
.
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.
Yes, but we can extract the relevant bits from within singleAddressFromKey
. For lack of a better intermediate type, XPub
is cleaner than a raw ByteString
lib/jormungandr/src/Cardano/Wallet/Jormungandr/Compatibility.hs
Outdated
Show resolved
Hide resolved
Address (BS.pack [3] <> pub) | ||
it "throws when length (key) != 32" $ | ||
evaluate (singleAddressFromKey (Proxy @'Mainnet) (pub <> "\148")) | ||
`shouldThrow` anyErrorCall |
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.
Ho. Is this the behavior from put
& isolate
?
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.
isolate
is for Get
. I added a isolatePut
.
Also, we should probably ban the unsafe runPut
/runGet
for safer ones soon.
12863d7
to
f585ce5
Compare
to avoid naming collision with Binary.getAddress.
Previously: `getAddress :: ByteString 33 bytes -> Address 32 bytes` `putAddress :: Address 32 bytes -> ByteString 33 bytes` Now: `getAddress :: ByteString 33 bytes -> Address 33 bytes` `putAddress Address 33 bytes -> ByteString 33 bytes` **Implemented** `keyToAddress :: XPub 32 bytes -> Address 33 bytes` - Implement Jörmungandr keyToAddress - Refactor to have singleAddressFromKey in Binary module - Ignore chaincode in jörmungandr keyToAddress & add tests
f585ce5
to
429e310
Compare
429e310
to
3e23420
Compare
3e23420
to
cd81207
Compare
Issue Number
#219 - or bug
Overview
Single addresses contain a header byte and a public key (32 bytes). I believe we should store the entire 33 bytes inside the
Address
data-type. Previously didn't store the header byte.getAddress
andputAddress
work preserve the entire address structure.getAddress :: 33 bytes -> Address 32 bytes
putAddress :: Address 32 bytes -> 33 bytes
getAddress :: 33 bytes -> Address 33 bytes
putAddress Address 33 bytes -> 33 bytes
keyToAddress :: XPub 32 bytes -> Address 33 bytes
Address {getAddress}
toAddress {unAddress}
to avoid name collisions withBinary.getAddress
Comments