From ccd4ede950d046690f36853dc5e3cd998f2cd8f7 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 27 Apr 2023 09:50:07 -0500 Subject: [PATCH] Add functions and tests for Bech32 and F4UnJumble --- package.yaml | 2 + src/C/Zcash.chs | 30 ++++++++--- src/Zcash.hs | 14 +++++- test/Spec.hs | 118 +++++++++++++++++++++++++++++++++++++++++++- zcash-haskell.cabal | 2 + 5 files changed, 156 insertions(+), 10 deletions(-) diff --git a/package.yaml b/package.yaml index a87ea24..e4bb94b 100644 --- a/package.yaml +++ b/package.yaml @@ -30,6 +30,7 @@ library: - borsh >= 0.2 - text - foreign-rust + - generics-sop pkg-config-dependencies: - rustzcash_wrapper-uninstalled @@ -45,3 +46,4 @@ tests: - zcash-haskell - hspec - bytestring + - text diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index e6e4acd..99e5004 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -1,4 +1,6 @@ {-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE UndecidableInstances #-} @@ -18,16 +20,25 @@ import Foreign.Rust.Marshall.Fixed import Foreign.Rust.Marshall.Variable import Foreign.Rust.Serialisation.Raw import Foreign.Rust.Serialisation.Raw.Base16 +import qualified Generics.SOP as SOP +import qualified GHC.Generics as GHC -newtype CodedString = CodedString BS.ByteString - deriving (Eq) - deriving newtype (BorshSize, ToBorsh, FromBorsh) - deriving newtype (IsRaw) - deriving (Prelude.Show, Data.Structured.Show) via AsBase16 CodedString +data RawData = RawData { hrp :: BS.ByteString, bytes :: BS.ByteString} + deriving stock (Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawData {# fun unsafe rust_wrapper_f4jumble as rustWrapperF4Jumble { toBorshVar* `BS.ByteString'& - , getVarBuffer `Buffer (CodedString)'& + , getVarBuffer `Buffer (BS.ByteString)'& + } + -> `()' +#} + +{# fun unsafe rust_wrapper_f4unjumble as rustWrapperF4UnJumble + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer (BS.ByteString)'& } -> `()' #} @@ -43,3 +54,10 @@ newtype CodedString = CodedString BS.ByteString } -> `Bool' #} + +{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode + { toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer RawData'& + } + -> `()' +#} diff --git a/src/Zcash.hs b/src/Zcash.hs index ede2cd2..fd8953f 100644 --- a/src/Zcash.hs +++ b/src/Zcash.hs @@ -1,12 +1,16 @@ module Zcash ( f4Jumble + , f4UnJumble , isValidUnifiedAddress , isValidShieldedAddress + , decodeBech32 ) where import C.Zcash - ( CodedString + ( RawData + , rustWrapperBech32Decode , rustWrapperF4Jumble + , rustWrapperF4UnJumble , rustWrapperIsShielded , rustWrapperIsUA ) @@ -14,11 +18,17 @@ import qualified Data.ByteString as BS import Foreign.Rust.Marshall.Fixed import Foreign.Rust.Marshall.Variable -f4Jumble :: BS.ByteString -> CodedString +f4Jumble :: BS.ByteString -> BS.ByteString f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble +f4UnJumble :: BS.ByteString -> BS.ByteString +f4UnJumble = withPureBorshVarBuffer . rustWrapperF4UnJumble + isValidUnifiedAddress :: BS.ByteString -> Bool isValidUnifiedAddress = rustWrapperIsUA isValidShieldedAddress :: BS.ByteString -> Bool isValidShieldedAddress = rustWrapperIsShielded + +decodeBech32 :: BS.ByteString -> RawData +decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode diff --git a/test/Spec.hs b/test/Spec.hs index dfbc469..c0aa00e 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1,7 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} -import C.Zcash (CodedString(CodedString), rustWrapperIsUA) +import C.Zcash (RawData(..), rustWrapperIsUA) import qualified Data.ByteString as BS +import qualified Data.Text.Encoding as E import Data.Word import Test.Hspec import Zcash @@ -111,7 +112,120 @@ main = do , 0x8d , 0x22 ] :: [Word8] - CodedString (BS.pack out) `shouldBe` f4Jumble (BS.pack input) + BS.pack out `shouldBe` f4Jumble (BS.pack input) + it "unjumble a string" $ do + let input = + [ 0x5d + , 0x7a + , 0x8f + , 0x73 + , 0x9a + , 0x2d + , 0x9e + , 0x94 + , 0x5b + , 0x0c + , 0xe1 + , 0x52 + , 0xa8 + , 0x04 + , 0x9e + , 0x29 + , 0x4c + , 0x4d + , 0x6e + , 0x66 + , 0xb1 + , 0x64 + , 0x93 + , 0x9d + , 0xaf + , 0xfa + , 0x2e + , 0xf6 + , 0xee + , 0x69 + , 0x21 + , 0x48 + , 0x1c + , 0xdd + , 0x86 + , 0xb3 + , 0xcc + , 0x43 + , 0x18 + , 0xd9 + , 0x61 + , 0x4f + , 0xc8 + , 0x20 + , 0x90 + , 0x5d + , 0x04 + , 0x2b + ] :: [Word8] + let out = + [ 0x03 + , 0x04 + , 0xd0 + , 0x29 + , 0x14 + , 0x1b + , 0x99 + , 0x5d + , 0xa5 + , 0x38 + , 0x7c + , 0x12 + , 0x59 + , 0x70 + , 0x67 + , 0x35 + , 0x04 + , 0xd6 + , 0xc7 + , 0x64 + , 0xd9 + , 0x1e + , 0xa6 + , 0xc0 + , 0x82 + , 0x12 + , 0x37 + , 0x70 + , 0xc7 + , 0x13 + , 0x9c + , 0xcd + , 0x88 + , 0xee + , 0x27 + , 0x36 + , 0x8c + , 0xd0 + , 0xc0 + , 0x92 + , 0x1a + , 0x04 + , 0x44 + , 0xc8 + , 0xe5 + , 0x85 + , 0x8d + , 0x22 + ] :: [Word8] + f4UnJumble (BS.pack out) `shouldBe` BS.pack input + describe "Decode UVK from YWallet" $ do + let uvk = + "uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm" + let res = decodeBech32 uvk + let rawBytes = f4UnJumble $ bytes res + it "decodes Bech32" $ do + print $ hrp res + print $ BS.length $ bytes res + hrp res `shouldBe` "uview" + it "unjumble result" $ do + BS.takeEnd 16 rawBytes `shouldBe` "uview00000000000" describe "Unified address" $ do it "succeeds with correct UA" $ do let ua = diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 62b50de..8815cba 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -39,6 +39,7 @@ library , borsh >=0.2 , bytestring , foreign-rust + , generics-sop , text default-language: Haskell2010 @@ -54,5 +55,6 @@ test-suite zcash-haskell-test base >=4.7 && <5 , bytestring , hspec + , text , zcash-haskell default-language: Haskell2010