diff --git a/CHANGELOG.md b/CHANGELOG.md index 97a42c6..80e6446 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.5.4.0] - Function to decode Orchard actions with a spending key +- Functions for Bech32 encoding +- Function to encode a Sapling address ## [0.5.3.0] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index d5533f5..4d1cb7f 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -105,6 +105,7 @@ use orchard::{ use bech32::{ Hrp, + Bech32, Bech32m }; @@ -585,7 +586,7 @@ pub extern "C" fn rust_wrapper_bech32decode( } #[no_mangle] -pub extern "C" fn rust_wrapper_bech32_encode( +pub extern "C" fn rust_wrapper_bech32m_encode( hr: *const u8, hr_len: usize, b: *const u8, @@ -1129,3 +1130,20 @@ pub extern "C" fn rust_wrapper_derive_orchard_receiver( marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW); } + +#[no_mangle] +pub extern "C" fn rust_wrapper_bech32_encode( + hr: *const u8, + hr_len: usize, + b: *const u8, + b_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let hr: String = marshall_from_haskell_var(hr, hr_len, RW); + let hrp = Hrp::parse(&hr).unwrap(); + let b: Vec = marshall_from_haskell_var(b, b_len, RW); + let string = bech32::encode::(hrp, &b).unwrap(); + marshall_to_haskell_var(&string, out, out_len, RW); +} + diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index ea2f9ea..c654405 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -38,7 +38,7 @@ import ZcashHaskell.Types -> `()' #} -{# fun unsafe rust_wrapper_bech32_encode as rustWrapperBech32Encode +{# fun unsafe rust_wrapper_bech32m_encode as rustWrapperBech32mEncode { toBorshVar* `BS.ByteString'& , toBorshVar* `BS.ByteString'& , getVarBuffer `Buffer (T.Text)'& @@ -200,3 +200,12 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_bech32_encode as rustWrapperBech32Encode + { toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , getVarBuffer `Buffer (T.Text)'& + } + -> `()' +#} + diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index b503da2..76bbad2 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -34,7 +34,7 @@ import qualified Data.Text.Encoding as E import Data.Word import Foreign.Rust.Marshall.Variable import ZcashHaskell.Types -import ZcashHaskell.Utils (encodeBech32m, f4Jumble) +import ZcashHaskell.Utils (encodeBech32, encodeBech32m, f4Jumble) -- | Derives an Orchard spending key for the given seed and account ID genOrchardSpendingKey :: @@ -155,6 +155,24 @@ decryptOrchardAction key encAction = withPureBorshVarBuffer $ rustWrapperOrchardNoteDecode (o_key key) encAction +getSaplingFromUA :: BS.ByteString -> Maybe T.Text +getSaplingFromUA uadd = do + let a = isValidUnifiedAddress uadd + case a of + Nothing -> Nothing + Just a -> do + let sraw = s_rec a + case sraw of + Nothing -> Nothing + Just sraw -> do + let net = ua_net a + case net of + MainNet -> + Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sraw) + TestNet -> + Just $ + encodeBech32 (C.pack sapTestPaymentAddressHrp) (getBytes sraw) + -- | Attemtps to decode the given @OrchardAction@ using the given @OrchardSpendingKey@ decryptOrchardActionSK :: OrchardSpendingKey -> Scope -> OrchardAction -> Maybe DecodedNote diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index b6598da..3187c62 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -19,6 +19,7 @@ module ZcashHaskell.Utils where import C.Zcash ( rustWrapperBech32Decode + , rustWrapperBech32mEncode , rustWrapperBech32Encode , rustWrapperF4Jumble , rustWrapperF4UnJumble @@ -45,7 +46,11 @@ decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode -- | Encode the given Human Readable Part and bytestring as a Bech32m string encodeBech32m :: BS.ByteString -> BS.ByteString -> T.Text -encodeBech32m h d = withPureBorshVarBuffer $ rustWrapperBech32Encode h d +encodeBech32m h d = withPureBorshVarBuffer $ rustWrapperBech32mEncode h d + +-- | Encode the given Human Readable Part and bytestring as a Bech32 string +encodeBech32 :: BS.ByteString -> BS.ByteString -> T.Text +encodeBech32 h d = withPureBorshVarBuffer $ rustWrapperBech32Encode h d -- | Apply the F4Jumble transformation to the given bytestring f4Jumble :: BS.ByteString -> BS.ByteString @@ -115,3 +120,4 @@ readZebraTransaction hex = else Just rawTx where rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex + diff --git a/test/Spec.hs b/test/Spec.hs index 6db774b..2333a69 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -844,6 +844,19 @@ main = do let tb = zt_tBundle t' print tb show tb `shouldNotBe` "" + describe "Extract Sapling Address - UA Valid" $ do + let sr = getSaplingFromUA "u14a5c4ufn9feqvxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" + it "Extract sapling address" $ do + case sr of + Nothing -> assertFailure "UA invalid or does not contain a Sapling receiver" + Just t -> do + print t + t `shouldBe` "zs1waxrpde36rlrjdwfhnvw030sn29lzwmvmeupd8x2uuqgypaafx7mqcy0ep8yf2xtg30n5424t60" + describe "Extract Sapling Address - UA Invalid" $ do + let sr = getSaplingFromUA "u14a5c4ufn9qfevxssnvscep29j5cse4gjpg0w3w5vjhafn74hg9k73xgnxqv6m255n23weggr6j97c8kdwvn4pkz7rz6my52z8248gjmr7knlw536tcurs5km7knqnzez4cywudt3q6shr553hurduvljfeqvfzgegenfjashslkz3y4ykhxel6mrjp9gsm9xk7k6kdxn9y84kccmv8l" + it "Try to extract sapling address from invalid UA" $ do + sr `shouldBe` Nothing + -- | Properties prop_PhraseLength :: Property