From 1d558fc646a7758d60a721124812070de222c2e1 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 4 Oct 2023 11:12:30 -0500 Subject: [PATCH] Implement check of Unified Address and UVK --- CHANGELOG.md | 1 + librustzcash-wrapper/src/lib.rs | 38 ++++++++++++++++++++++++++++++++- src/C/Zcash.chs | 7 ++++++ src/ZcashHaskell/Orchard.hs | 5 +++++ test/Spec.hs | 15 +++++++++++++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3ad8c1..14e77d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `matchOrchardAddress` function to ensure a UA matches a UVK and corresponding tests - `makeZcashCall` function moved into this library - `RpcResponse`, `RpcCall` types moved into this library - Functions to decode Sapling transactions diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index f372a2e..27e30f9 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -47,7 +47,7 @@ use zcash_primitives::{ use zcash_address::{ Network, - unified::{Address, Encoding, Ufvk, Container, Fvk}, + unified::{Address, Encoding, Ufvk, Container, Fvk, Receiver}, ZcashAddress }; @@ -320,6 +320,42 @@ pub extern "C" fn rust_wrapper_svk_check_address( } } +#[no_mangle] +pub extern "C" fn rust_wrapper_ufvk_check_address( + key_input: *const u8, + key_input_len: usize, + address_input: *const u8, + address_input_len: usize + ) -> bool { + let key: String = marshall_from_haskell_var(key_input, key_input_len, RW); + let addy: String = marshall_from_haskell_var(address_input, address_input_len, RW); + let dec_key = Ufvk::decode(&key); + let dec_addy = Address::decode(&addy); + match dec_key { + Ok((n, ufvk)) => { + let i = ufvk.items(); + if let Fvk::Orchard(k) = i[0] { + let orch_key = FullViewingKey::from_bytes(&k).unwrap(); + let orch_addy = orch_key.address_at(0u32, Scope::External).to_raw_address_bytes(); + match dec_addy { + Ok((n, recs)) => { + let j = recs.items(); + j[0] == Receiver::Orchard(orch_addy) + }, + Err(_e) => { + false + } + } + } else { + false + } + }, + Err(_e) => { + false + } + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_ufvk_decode( input: *const u8, diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 67a5d31..a2e1ecd 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -72,6 +72,13 @@ import ZcashHaskell.Types -> `Bool' #} +{# fun pure unsafe rust_wrapper_ufvk_check_address as rustWrapperOrchardCheck + { toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + } + -> `Bool' +#} + {# fun unsafe rust_wrapper_sapling_note_decrypt_v2 as rustWrapperSaplingNoteDecode { toBorshVar* `BS.ByteString'& , toBorshVar* `BS.ByteString'& diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index edee2ff..d7c3665 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -13,6 +13,7 @@ module ZcashHaskell.Orchard where import C.Zcash ( rustWrapperIsUA + , rustWrapperOrchardCheck , rustWrapperOrchardNoteDecode , rustWrapperUfvkDecode ) @@ -33,6 +34,10 @@ decodeUfvk str = where decodedKey = (withPureBorshVarBuffer . rustWrapperUfvkDecode) str +-- | Check if the given UVK matches the UA given +matchOrchardAddress :: BS.ByteString -> BS.ByteString -> Bool +matchOrchardAddress = rustWrapperOrchardCheck + -- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@. decryptOrchardAction :: UnifiedFullViewingKey -> OrchardAction -> Maybe DecodedNote diff --git a/test/Spec.hs b/test/Spec.hs index 660d342..d61f92d 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -2,14 +2,17 @@ import C.Zcash (rustWrapperIsUA) import Data.Aeson +import Data.Bool (Bool(True)) import qualified Data.ByteString as BS import qualified Data.Text as T import qualified Data.Text.Encoding as E import qualified Data.Text.Lazy.Encoding as LE import qualified Data.Text.Lazy.IO as LTIO import Data.Word +import GHC.Float.RealFracMethods (properFractionDoubleInteger) import Test.Hspec import ZcashHaskell.Orchard +import ZcashHaskell.Orchard (matchOrchardAddress) import ZcashHaskell.Sapling ( decodeSaplingOutput , getShieldedOutputs @@ -28,6 +31,7 @@ import ZcashHaskell.Types , decodeHexText ) import ZcashHaskell.Utils +import ZcashHaskell.Utils (decodeBech32) main :: IO () main = do @@ -312,6 +316,17 @@ main = do let fakeUvk = "uview1u83changinga987bundchofch4ract3r5x8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm" decodeUfvk fakeUvk `shouldBe` Nothing + describe "Check if UA and UVK match" $ do + let ua = + "u15hjz9v46azzmdept050heh8795qxzwy2pykg097lg69jpk4qzah90cj2q4amq0c07gta60x8qgw00qewcy3hg9kv9h6zjkh3jc66vr40u6uu2dxmqkqhypud95vm0gq7y5ga7c8psdqgthsrwvgd676a2pavpcd4euwwapgackxa3qhvga0wnl0k6vncskxlq94vqwjd7zepy3qd5jh" + let ua' = + "u17n7hpwaujyq7ux8f9jpyymtnk5urw7pyrf60smp5mawy7jgz325hfvz3jn3zsfya8yxryf9q7ldk8nu8df0emra5wne28zq9d9nm2pu4x6qwjha565av9aze0xgujgslz74ufkj0c0cylqwjyrh9msjfh7jzal6d3qzrnhkkqy3pqm8j63y07jxj7txqeac982778rmt64f32aum94x" + let uvk = + "uview1u833rp8yykd7h4druwht6xp6k8krle45fx8hqsw6vzw63n24atxpcatws82z092kryazuu6d7rayyut8m36wm4wpjy2z8r9hj48fx5pf49gw4sjrq8503qpz3vqj5hg0vg9vsqeasg5qjuyh94uyfm7v76udqcm2m0wfc25hcyqswcn56xxduq3xkgxkr0l73cjy88fdvf90eq5fda9g6x7yv7d0uckpevxg6540wc76xrc4axxvlt03ptaa2a0rektglmdy68656f3uzcdgqqyu0t7wk5cvwghyyvgqc0rp3vgu5ye4nd236ml57rjh083a2755qemf6dk6pw0qrnfm7246s8eg2hhzkzpf9h73chhng7xhmyem2sjh8rs2m9nhfcslsgenm" + it "succeeds with correct address" $ do + matchOrchardAddress uvk ua `shouldBe` True + it "fails with wrong address" $ do + matchOrchardAddress uvk ua' `shouldBe` False describe "Decode Sapling tx" $ do let svk = "zxviews1qvapd723qqqqpqq09ldgykvyusthmkky2w062esx5xg3nz4m29qxcvndyx6grrhrdepu4ns88sjr3u6mfp2hhwj5hfd6y24r0f64uwq65vjrmsh9mr568kenk33fcumag6djcjywkm5v295egjuk3qdd47atprs0j33nhaaqep3uqspzp5kg4mthugvug0sc3gc83atkrgmguw9g7gkvh82tugrntf66lnvyeh6ufh4j2xt0xr2r4zujtm3qvrmd3vvnulycuwqtetg2jk384"