Implement Viewing Keys #106

Merged
pitmutt merged 116 commits from rav001 into master 2025-02-28 18:51:31 +00:00
12 changed files with 1026 additions and 164 deletions

View file

@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.8.1.0]
### Fixed
- Producing nullifiers from decoding with viewing keys
## [0.8.0.0]
### Added
- `UnifiedIncomingViewingKey` type
- `ValidVk` type
- Address derivation from full viewing keys
- Address derivation from incoming viewing keys
- Orchard note decoding with full viewing key
- Orchard note decoding with incoming viewing key
- Sapling note decoding with full viewing key
- Sapling note decoding with incoming viewing key
### Fixed
- Transparent viewing key component generation
## [0.7.8.1] ## [0.7.8.1]
### Changed ### Changed

View file

@ -3,13 +3,13 @@ constraints: any.Cabal ==3.10.3.0,
any.Cabal-syntax ==3.10.3.0, any.Cabal-syntax ==3.10.3.0,
any.HUnit ==1.6.2.0, any.HUnit ==1.6.2.0,
any.OneTuple ==0.4.2, any.OneTuple ==0.4.2,
any.QuickCheck ==2.14.3, any.QuickCheck ==2.15.0.1,
QuickCheck -old-random +templatehaskell, QuickCheck -old-random +templatehaskell,
any.StateVar ==1.2.2, any.StateVar ==1.2.2,
any.aeson ==2.2.3.0, any.aeson ==2.2.3.0,
aeson +ordered-keymap, aeson +ordered-keymap,
any.alex ==3.5.1.0, any.alex ==3.5.2.0,
any.ansi-terminal ==1.1.1, any.ansi-terminal ==1.1.2,
ansi-terminal -example, ansi-terminal -example,
any.ansi-terminal-types ==1.1, any.ansi-terminal-types ==1.1,
any.appar ==0.1.8, any.appar ==0.1.8,
@ -25,7 +25,7 @@ constraints: any.Cabal ==3.10.3.0,
attoparsec -developer, attoparsec -developer,
any.attoparsec-aeson ==2.2.2.0, any.attoparsec-aeson ==2.2.2.0,
any.base ==4.18.2.1, any.base ==4.18.2.1,
any.base-orphans ==0.9.2, any.base-orphans ==0.9.3,
any.base16 ==1.0, any.base16 ==1.0,
any.base16-bytestring ==1.0.2.0, any.base16-bytestring ==1.0.2.0,
any.base58-bytestring ==0.1.0, any.base58-bytestring ==0.1.0,
@ -40,7 +40,7 @@ constraints: any.Cabal ==3.10.3.0,
any.blaze-builder ==0.4.2.3, any.blaze-builder ==0.4.2.3,
any.borsh ==0.3.0, any.borsh ==0.3.0,
any.byteorder ==1.0.4, any.byteorder ==1.0.4,
any.bytes ==0.17.3, any.bytes ==0.17.4,
any.bytestring ==0.11.5.3, any.bytestring ==0.11.5.3,
any.c2hs ==0.28.8, any.c2hs ==0.28.8,
c2hs +base3 -regression, c2hs +base3 -regression,
@ -52,28 +52,25 @@ constraints: any.Cabal ==3.10.3.0,
cereal -bytestring-builder, cereal -bytestring-builder,
any.character-ps ==0.1, any.character-ps ==0.1,
any.colour ==2.3.6, any.colour ==2.3.6,
any.comonad ==5.0.8, any.comonad ==5.0.9,
comonad +containers +distributive +indexed-traversable, comonad +containers +distributive +indexed-traversable,
any.conduit ==1.3.6, any.conduit ==1.3.6,
any.conduit-extra ==1.3.6, any.conduit-extra ==1.3.7,
any.containers ==0.6.7, any.containers ==0.6.7,
any.contravariant ==1.5.5, any.contravariant ==1.5.5,
contravariant +semigroups +statevar +tagged, contravariant +semigroups +statevar +tagged,
any.cookie ==0.5.0, any.cookie ==0.5.0,
any.crypton ==1.0.0, any.crypton ==1.0.1,
crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes, crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes,
any.crypton-connection ==0.4.1, any.crypton-connection ==0.4.3,
any.crypton-x509 ==1.7.7, any.crypton-x509 ==1.7.7,
any.crypton-x509-store ==1.6.9, any.crypton-x509-store ==1.6.9,
any.crypton-x509-system ==1.6.7, any.crypton-x509-system ==1.6.7,
any.crypton-x509-validation ==1.6.12, any.crypton-x509-validation ==1.6.13,
any.cryptonite ==0.30, any.cryptonite ==0.30,
cryptonite -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq -support_pclmuldq +support_rdrand -support_sse +use_target_attributes, cryptonite -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq -support_pclmuldq +support_rdrand -support_sse +use_target_attributes,
any.data-default ==0.7.1.1, any.data-default ==0.8.0.0,
any.data-default-class ==0.1.2.0, any.data-default-class ==0.2.0.0,
any.data-default-instances-containers ==0.0.1,
any.data-default-instances-dlist ==0.0.1,
any.data-default-instances-old-locale ==0.0.1,
any.data-fix ==0.3.4, any.data-fix ==0.3.4,
any.deepseq ==1.4.8.1, any.deepseq ==1.4.8.1,
any.directory ==1.3.8.4, any.directory ==1.3.8.4,
@ -81,9 +78,9 @@ constraints: any.Cabal ==3.10.3.0,
distributive +semigroups +tagged, distributive +semigroups +tagged,
any.dlist ==1.0, any.dlist ==1.0,
dlist -werror, dlist -werror,
any.entropy ==0.4.1.10, any.entropy ==0.4.1.11,
entropy -donotgetentropy, entropy -donotgetentropy,
any.envy ==2.1.3.0, any.envy ==2.1.4.0,
any.exceptions ==0.10.7, any.exceptions ==0.10.7,
any.filepath ==1.4.300.1, any.filepath ==1.4.300.1,
any.foreign-rust ==0.1.0, any.foreign-rust ==0.1.0,
@ -92,56 +89,61 @@ constraints: any.Cabal ==3.10.3.0,
any.ghc-bignum ==1.3, any.ghc-bignum ==1.3,
any.ghc-boot-th ==9.6.5, any.ghc-boot-th ==9.6.5,
any.ghc-prim ==0.10.0, any.ghc-prim ==0.10.0,
any.half ==0.3.1, any.half ==0.3.2,
any.happy ==2.0.2, any.happy ==2.1.4,
any.happy-lib ==2.0.2, any.happy-lib ==2.1.4,
any.hashable ==1.4.7.0, any.hashable ==1.5.0.0,
hashable -arch-native +integer-gmp -random-initial-seed, hashable -arch-native -random-initial-seed,
any.haskell-lexer ==1.1.1, any.haskell-lexer ==1.1.2,
any.haskoin-core ==1.1.0, any.haskoin-core ==1.1.0,
any.hexstring ==0.12.1.0, any.hexstring ==0.12.1.0,
any.hourglass ==0.2.12, any.hourglass ==0.2.12,
any.hsc2hs ==0.68.10, any.hsc2hs ==0.68.10,
hsc2hs -in-ghc-tree, hsc2hs -in-ghc-tree,
any.hspec ==2.11.9, any.hspec ==2.11.10,
any.hspec-core ==2.11.9, any.hspec-core ==2.11.10,
any.hspec-discover ==2.11.9, any.hspec-discover ==2.11.10,
any.hspec-expectations ==0.8.4, any.hspec-expectations ==0.8.4,
any.http-client ==0.7.17, any.http-client ==0.7.18,
http-client +network-uri, http-client +network-uri,
any.http-client-tls ==0.3.6.3, any.http-client-tls ==0.3.6.4,
any.http-conduit ==2.3.9, any.http-conduit ==2.3.9.1,
http-conduit +aeson, http-conduit +aeson,
any.http-types ==0.12.4, any.http-types ==0.12.4,
any.indexed-traversable ==0.1.4, any.indexed-traversable ==0.1.4,
any.indexed-traversable-instances ==0.1.2, any.indexed-traversable-instances ==0.1.2,
any.integer-conversion ==0.1.1, any.integer-conversion ==0.1.1,
any.integer-gmp ==1.1, any.integer-gmp ==1.1,
any.integer-logarithms ==1.0.3.1, any.integer-logarithms ==1.0.4,
integer-logarithms -check-bounds +integer-gmp, integer-logarithms -check-bounds +integer-gmp,
any.iproute ==1.7.14, any.iproute ==1.7.15,
any.language-c ==0.9.3, any.language-c ==0.10.0,
language-c -allwarnings +iecfpextension +usebytestrings, language-c +iecfpextension +usebytestrings,
any.memory ==0.18.0, any.memory ==0.18.0,
memory +support_bytestring +support_deepseq, memory +support_bytestring +support_deepseq,
any.mime-types ==0.1.2.0, any.mime-types ==0.1.2.0,
any.mono-traversable ==1.0.20.0, any.mono-traversable ==1.0.21.0,
any.mtl ==2.3.1, any.mtl ==2.3.1,
any.murmur3 ==1.0.5, any.murmur3 ==1.0.5,
any.network ==3.2.4.0, any.network ==3.2.7.0,
network -devel, network -devel,
any.network-uri ==2.6.4.2, any.network-uri ==2.6.4.2,
any.old-locale ==1.0.0.7, any.old-locale ==1.0.0.7,
any.old-time ==1.1.0.4, any.old-time ==1.1.0.4,
any.os-string ==2.0.6, any.optparse-applicative ==0.18.1.0,
optparse-applicative +process,
any.os-string ==2.0.7,
any.parsec ==3.1.16.1, any.parsec ==3.1.16.1,
any.pem ==0.2.4, any.pem ==0.2.4,
any.pretty ==1.1.3.6, any.pretty ==1.1.3.6,
any.prettyprinter ==1.7.1,
prettyprinter -buildreadme +text,
any.prettyprinter-ansi-terminal ==1.1.3,
any.primitive ==0.9.0.0, any.primitive ==0.9.0.0,
any.process ==1.6.19.0, any.process ==1.6.19.0,
any.quickcheck-io ==0.2.0, any.quickcheck-io ==0.2.0,
any.quickcheck-transformer ==0.3.1.2, any.quickcheck-transformer ==0.3.1.2,
any.random ==1.2.1.2, any.random ==1.2.1.3,
any.regex-base ==0.94.0.2, any.regex-base ==0.94.0.2,
any.regex-compat ==0.95.2.1, any.regex-compat ==0.95.2.1,
any.regex-posix ==0.96.0.1, any.regex-posix ==0.96.0.1,
@ -151,7 +153,7 @@ constraints: any.Cabal ==3.10.3.0,
any.safe ==0.3.21, any.safe ==0.3.21,
any.scientific ==0.3.8.0, any.scientific ==0.3.8.0,
scientific -integer-simple, scientific -integer-simple,
any.secp256k1-haskell ==1.4.0, any.secp256k1-haskell ==1.4.2,
any.semialign ==1.3.1, any.semialign ==1.3.1,
semialign +semigroupoids, semialign +semigroupoids,
any.semigroupoids ==6.0.1, any.semigroupoids ==6.0.1,
@ -161,42 +163,44 @@ constraints: any.Cabal ==3.10.3.0,
any.socks ==0.6.1, any.socks ==0.6.1,
any.sop-core ==0.5.0.2, any.sop-core ==0.5.0.2,
any.split ==0.2.5, any.split ==0.2.5,
any.splitmix ==0.1.0.5, any.splitmix ==0.1.1,
splitmix -optimised-mixer, splitmix -optimised-mixer,
any.stm ==2.5.1.0, any.stm ==2.5.1.0,
any.streaming-commons ==0.2.2.6, any.streaming-commons ==0.2.3.0,
streaming-commons -use-bytestring-builder, streaming-commons -use-bytestring-builder,
any.strict ==0.5.1, any.strict ==0.5.1,
any.string-conversions ==0.4.0.1, any.string-conversions ==0.4.0.1,
any.tagged ==0.8.8, any.tagged ==0.8.9,
tagged +deepseq +transformers, tagged +deepseq +transformers,
any.tasty ==1.5.3,
tasty +unix,
any.template-haskell ==2.20.0.0, any.template-haskell ==2.20.0.0,
any.text ==2.0.2, any.text ==2.0.2,
any.text-iso8601 ==0.1.1, any.text-iso8601 ==0.1.1,
any.text-short ==0.1.6, any.text-short ==0.1.6,
text-short -asserts, text-short -asserts,
any.tf-random ==0.5, any.tf-random ==0.5,
any.th-abstraction ==0.7.0.0, any.th-abstraction ==0.7.1.0,
any.th-compat ==0.1.5, any.th-compat ==0.1.6,
any.these ==1.2.1, any.these ==1.2.1,
any.time ==1.12.2, any.time ==1.12.2,
any.time-compat ==1.9.7, any.time-compat ==1.9.8,
any.tls ==2.1.0, any.tls ==2.1.7,
tls -devel, tls -devel,
any.transformers ==0.6.1.0, any.transformers ==0.6.1.0,
any.transformers-compat ==0.7.2, any.transformers-compat ==0.7.2,
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
any.typed-process ==0.2.12.0, any.typed-process ==0.2.12.0,
any.unix ==2.8.4.0, any.unix ==2.8.4.0,
any.unix-time ==0.4.15, any.unix-time ==0.4.16,
any.unliftio-core ==0.2.1.0, any.unliftio-core ==0.2.1.0,
any.unordered-containers ==0.2.20, any.unordered-containers ==0.2.20,
unordered-containers -debug, unordered-containers -debug,
any.utf8-string ==1.0.2, any.utf8-string ==1.0.2,
any.uuid-types ==1.0.6, any.uuid-types ==1.0.6,
any.vector ==0.13.1.0, any.vector ==0.13.2.0,
vector +boundschecks -internalchecks -unsafechecks -wall, vector +boundschecks -internalchecks -unsafechecks -wall,
any.vector-algorithms ==0.9.0.2, any.vector-algorithms ==0.9.0.3,
vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks, vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks,
any.vector-stream ==0.1.0.1, any.vector-stream ==0.1.0.1,
any.void ==0.7.3, any.void ==0.7.3,
@ -205,4 +209,4 @@ constraints: any.Cabal ==3.10.3.0,
any.witherable ==0.5, any.witherable ==0.5,
any.zlib ==0.7.1.0, any.zlib ==0.7.1.0,
zlib -bundled-c-zlib +non-blocking-ffi +pkg-config zlib -bundled-c-zlib +non-blocking-ffi +pkg-config
index-state: hackage.haskell.org 2024-10-11T12:55:31Z index-state: hackage.haskell.org 2025-01-31T18:30:19Z

View file

@ -66,7 +66,8 @@ use sapling_crypto::{
}, },
note_encryption::{ note_encryption::{
SaplingDomain, SaplingDomain,
Zip212Enforcement Zip212Enforcement,
try_sapling_note_decryption
}, },
bundle::{ bundle::{
GrothProofBytes, GrothProofBytes,
@ -82,7 +83,8 @@ use sapling_crypto::{
}, },
zip32::{ zip32::{
sapling_find_address, sapling_find_address,
DiversifierKey DiversifierKey,
IncomingViewingKey as SaplingIncomingViewingKey
} }
}; };
@ -157,7 +159,7 @@ use orchard::{
Flags Flags
}, },
Action, Action,
keys::{SpendAuthorizingKey, SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, keys::{SpendAuthorizingKey, SpendingKey, FullViewingKey, IncomingViewingKey, PreparedIncomingViewingKey, Scope},
note::{Rho, RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note::{Rho, RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
note_encryption::OrchardDomain, note_encryption::OrchardDomain,
primitives::redpallas::{VerificationKey, SpendAuth, Signature}, primitives::redpallas::{VerificationKey, SpendAuth, Signature},
@ -1143,6 +1145,116 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
} }
} }
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_note_decrypt_fvk(
key: *const u8,
key_len: usize,
note: *const u8,
note_len: usize,
scope: bool,
pos: u64,
out: *mut u8,
out_len: &mut usize
){
let dfvk: Vec<u8> = marshall_from_haskell_var(key, key_len, RW);
let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW);
let svk = DiversifiableFullViewingKey::from_bytes(&to_array(dfvk));
if svk.is_some().into() {
let domain = SaplingDomain::new(Zip212Enforcement::On);
let action2 = note_input.to_output_description();
match action2 {
Ok(action3) => {
let nk =
if scope {
svk.clone().unwrap().to_nk(SaplingScope::External)
} else {
svk.clone().unwrap().to_nk(SaplingScope::Internal)
};
let fvk =
if scope {
svk.unwrap().to_ivk(SaplingScope::External)
} else {
svk.unwrap().to_ivk(SaplingScope::Internal)
};
let pivk = SaplingPreparedIncomingViewingKey::new(&fvk);
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action3);
match result {
Some((n, r, m)) => {
let rseed = match n.rseed() {
Rseed::BeforeZip212(x) => {
Hrseed { kind: 1, bytes: x.to_bytes().to_vec()}
},
Rseed::AfterZip212(y) => {
Hrseed { kind: 2, bytes: y.to_vec()}
}
};
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: n.nf(&nk, pos).to_vec(), rho: vec![0], rseed};
marshall_to_haskell_var(&hn, out, out_len, RW);
}
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
Err(_e1) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
} else {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_note_decrypt_ivk(
key: *const u8,
key_len: usize,
note: *const u8,
note_len: usize,
out: *mut u8,
out_len: &mut usize
){
let ivk: Vec<u8> = marshall_from_haskell_var(key, key_len, RW);
let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW);
let svk = SaplingIncomingViewingKey::from_bytes(&to_array(ivk));
if svk.is_some().into() {
let action2 = note_input.to_output_description();
match action2 {
Ok(action3) => {
let result = try_sapling_note_decryption(&svk.unwrap().prepare(), &action3, Zip212Enforcement::On);
match result {
Some((n, r, m)) => {
let rseed = match n.rseed() {
Rseed::BeforeZip212(x) => {
Hrseed { kind: 1, bytes: x.to_bytes().to_vec()}
},
Rseed::AfterZip212(y) => {
Hrseed { kind: 2, bytes: y.to_vec()}
}
};
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: vec![0], rho: vec![0], rseed};
marshall_to_haskell_var(&hn, out, out_len, RW);
}
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
Err(_e1) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
} else {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_wrapper_orchard_note_decrypt( pub extern "C" fn rust_wrapper_orchard_note_decrypt(
key: *const u8, key: *const u8,
@ -1189,6 +1301,99 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt(
} }
} }
#[no_mangle]
pub extern "C" fn rust_wrapper_orchard_note_decrypt_fvk(
key: *const u8,
key_len: usize,
note: *const u8,
note_len: usize,
external: bool,
out: *mut u8,
out_len: &mut usize
){
let fvk_input: Vec<u8> = marshall_from_haskell_var(key, key_len, RW);
let note_input: Haction = marshall_from_haskell_var(note, note_len, RW);
let action: Action<Signature<SpendAuth>> = Action::from_parts(
Nullifier::from_bytes(&to_array(note_input.nf.bytes)).unwrap(),
VerificationKey::try_from(to_array(note_input.rk.bytes)).unwrap(),
ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx.bytes)).unwrap(),
TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key.bytes), enc_ciphertext: to_array(note_input.enc_txt.bytes), out_ciphertext: to_array(note_input.out_txt.bytes)},
ValueCommitment::from_bytes(&to_array(note_input.cv.bytes)).unwrap(),
Signature::from(to_array(note_input.auth.bytes)));
let fvk_array = to_array(fvk_input);
let domain = OrchardDomain::for_action(&action);
let dec_fvk = FullViewingKey::from_bytes(&fvk_array);
match dec_fvk {
Some(fvk) => {
let ivk = if external {
fvk.to_ivk(Scope::External)
} else {
fvk.to_ivk(Scope::Internal)
};
let pivk = PreparedIncomingViewingKey::new(&ivk);
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
match result {
Some((n, r, m)) => {
let rho = n.rho().to_bytes().to_vec();
let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()};
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: n.nullifier(&fvk).to_bytes().to_vec(), rho, rseed};
marshall_to_haskell_var(&hn, out, out_len, RW);
}
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_orchard_note_decrypt_ivk(
key: *const u8,
key_len: usize,
note: *const u8,
note_len: usize,
out: *mut u8,
out_len: &mut usize
){
let ivk_input: Vec<u8> = marshall_from_haskell_var(key, key_len, RW);
let note_input: Haction = marshall_from_haskell_var(note, note_len, RW);
let action: Action<Signature<SpendAuth>> = Action::from_parts(
Nullifier::from_bytes(&to_array(note_input.nf.bytes)).unwrap(),
VerificationKey::try_from(to_array(note_input.rk.bytes)).unwrap(),
ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx.bytes)).unwrap(),
TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key.bytes), enc_ciphertext: to_array(note_input.enc_txt.bytes), out_ciphertext: to_array(note_input.out_txt.bytes)},
ValueCommitment::from_bytes(&to_array(note_input.cv.bytes)).unwrap(),
Signature::from(to_array(note_input.auth.bytes)));
let ivk_array = to_array(ivk_input);
let domain = OrchardDomain::for_action(&action);
let dec_fvk = IncomingViewingKey::from_bytes(&ivk_array);
if dec_fvk.is_some().into() {
let pivk = PreparedIncomingViewingKey::new(&dec_fvk.unwrap());
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
match result {
Some((n, r, m)) => {
let rho = n.rho().to_bytes().to_vec();
let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()};
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: vec![0], rho, rseed};
marshall_to_haskell_var(&hn, out, out_len, RW);
}
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
} else {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk( pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk(
key: *const u8, key: *const u8,
@ -1370,29 +1575,20 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress(
out_len: &mut usize out_len: &mut usize
){ ){
let extspk: Vec<u8> = marshall_from_haskell_var(extspk, extspk_len, RW); let extspk: Vec<u8> = marshall_from_haskell_var(extspk, extspk_len, RW);
if div_ix == 0 { let sp_key = ExtendedSpendingKey::from_bytes(&extspk);
let sp_key = ExtendedSpendingKey::from_bytes(&extspk); match sp_key {
match sp_key { Ok(sp_key_x) => {
Ok(sp_key_x) => { let (_def_div, def_address) =
let (_def_div, def_address) = sp_key_x.default_address(); if div_ix == 0 {
marshall_to_haskell_var(&def_address.to_bytes().to_vec(), out, out_len, RW); sp_key_x.default_address()
}, } else {
Err(_e) => { let dfvk = sp_key_x.to_diversifiable_full_viewing_key();
marshall_to_haskell_var(&vec![0], out, out_len, RW); dfvk.find_address(DiversifierIndex::from(div_ix)).unwrap()
} };
} marshall_to_haskell_var(&def_address.to_bytes().to_vec(), out, out_len, RW);
} else { },
let expsk = ExpandedSpendingKey::from_spending_key(&extspk); Err(_e) => {
let fvk = SaplingFullViewingKey::from_expanded_spending_key(&expsk); marshall_to_haskell_var(&vec![0], out, out_len, RW);
let dk = DiversifierKey::master(&extspk);
let result = sapling_find_address(&fvk, &dk, DiversifierIndex::from(div_ix));
match result {
Some((_d, p_address)) => {
marshall_to_haskell_var(&p_address.to_bytes().to_vec(), out, out_len, RW);
},
None => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
} }
} }
} }
@ -1420,6 +1616,91 @@ pub extern "C" fn rust_wrapper_sapling_chgpaymentaddress(
marshall_to_haskell_var(&cPmtAddress.to_bytes().to_vec(), out, out_len, RW); marshall_to_haskell_var(&cPmtAddress.to_bytes().to_vec(), out, out_len, RW);
} }
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_receiver_fvk(
fvk_in: *const u8,
fvk_in_len: usize,
div_ix: u32,
out: *mut u8,
out_len: &mut usize
){
let fvk_bytes: Vec<u8> = marshall_from_haskell_var(fvk_in, fvk_in_len, RW);
let fvk = DiversifiableFullViewingKey::from_bytes(&to_array(fvk_bytes));
if div_ix == 0 {
match fvk {
Some(k) => {
let (_def_div, def_address) = k.default_address();
marshall_to_haskell_var(&def_address.to_bytes().to_vec(), out, out_len, RW);
},
None => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
} else {
match fvk {
Some(k) => {
let result = k.find_address(DiversifierIndex::from(div_ix));
match result {
Some((_d, p_address)) => {
marshall_to_haskell_var(&p_address.to_bytes().to_vec(), out, out_len, RW);
},
None => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
},
None => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_change_receiver_fvk(
fvk_in: *const u8,
fvk_in_len: usize,
out: *mut u8,
out_len: &mut usize
){
let fvk_bytes: Vec<u8> = marshall_from_haskell_var(fvk_in, fvk_in_len, RW);
let dfvk = DiversifiableFullViewingKey::from_bytes(&to_array(fvk_bytes));
match dfvk {
Some(k) => {
let ( _div_ix, chg_address ) = k.change_address();
marshall_to_haskell_var(&chg_address.to_bytes().to_vec(), out, out_len, RW);
},
None => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_receiver_ivk(
ivk_in: *const u8,
ivk_in_len: usize,
div_ix: u32,
out: *mut u8,
out_len: &mut usize
){
let ivk_bytes: Vec<u8> = marshall_from_haskell_var(ivk_in, ivk_in_len, RW);
let ivk = SaplingIncomingViewingKey::from_bytes(&to_array(ivk_bytes));
if ivk.is_some().into() {
let result = ivk.unwrap().find_address(DiversifierIndex::from(div_ix));
match result {
Some((_d, p_address)) => {
marshall_to_haskell_var(&p_address.to_bytes().to_vec(), out, out_len, RW);
},
None => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
} else {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_wrapper_derive_orchard_spending_key( pub extern "C" fn rust_wrapper_derive_orchard_spending_key(
seed: *const u8, seed: *const u8,
@ -1461,6 +1742,49 @@ pub extern "C" fn rust_wrapper_derive_orchard_receiver(
} }
#[no_mangle]
pub extern "C" fn rust_wrapper_derive_orchard_receiver_fvk(
f_key: *const u8,
f_key_len: usize,
add_id: u32,
scope: bool,
out: *mut u8,
out_len: &mut usize
){
let vk_in: Vec<u8> = marshall_from_haskell_var(f_key, f_key_len, RW);
let fvk = FullViewingKey::from_bytes(&to_array(vk_in));
let sc = if scope {
Scope::External
} else {Scope::Internal};
match fvk {
Some(k) => {
let o_rec = k.address_at(add_id, sc);
marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW);
},
None => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_derive_orchard_receiver_ivk(
f_key: *const u8,
f_key_len: usize,
add_id: u32,
out: *mut u8,
out_len: &mut usize
){
let vk_in: Vec<u8> = marshall_from_haskell_var(f_key, f_key_len, RW);
let ivk = IncomingViewingKey::from_bytes(&to_array(vk_in));
if ivk.is_some().into() {
let o_rec = ivk.unwrap().address_at(add_id);
marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW);
} else {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_wrapper_bech32_encode( pub extern "C" fn rust_wrapper_bech32_encode(
hr: *const u8, hr: *const u8,

View file

@ -135,6 +135,23 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_orchard_note_decrypt_fvk as rustWrapperOrchardNoteDecodeFvk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `OrchardAction'&
, `Bool'
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_orchard_note_decrypt_ivk as rustWrapperOrchardNoteDecodeIvk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `OrchardAction'&
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_orchard_note_decrypt_sk as rustWrapperOrchardNoteDecodeSK {# fun unsafe rust_wrapper_orchard_note_decrypt_sk as rustWrapperOrchardNoteDecodeSK
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, toBorshVar* `OrchardAction'& , toBorshVar* `OrchardAction'&
@ -210,6 +227,23 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_derive_orchard_receiver_fvk as rustWrapperGenOrchardReceiverFvk
{ toBorshVar* `BS.ByteString'&
, `Word32'
, `Bool'
, getVarBuffer `Buffer (BS.ByteString)'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_derive_orchard_receiver_ivk as rustWrapperGenOrchardReceiverIvk
{ toBorshVar* `BS.ByteString'&
, `Word32'
, getVarBuffer `Buffer (BS.ByteString)'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_read_sapling_commitment_tree as rustWrapperReadSaplingCommitmentTree {# fun unsafe rust_wrapper_read_sapling_commitment_tree as rustWrapperReadSaplingCommitmentTree
{ toBorshVar* `SaplingFrontier'& { toBorshVar* `SaplingFrontier'&
, toBorshVar* `BS.ByteString'& , toBorshVar* `BS.ByteString'&
@ -445,3 +479,44 @@ import ZcashHaskell.Types
} }
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_sapling_receiver_fvk as rustWrapperSaplingReceiverFvk
{ toBorshVar* `BS.ByteString'&
, `Word32'
, getVarBuffer `Buffer BS.ByteString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_receiver_ivk as rustWrapperSaplingReceiverIvk
{ toBorshVar* `BS.ByteString'&
, `Word32'
, getVarBuffer `Buffer BS.ByteString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_change_receiver_fvk as rustWrapperSaplingChgReceiverFvk
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer BS.ByteString'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_note_decrypt_fvk as rustWrapperSaplingDecodeFvk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `ShieldedOutput'&
, `Bool'
, `Int64'
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_note_decrypt_ivk as rustWrapperSaplingDecodeIvk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `ShieldedOutput'&
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}

View file

@ -1,3 +1,5 @@
{-# LANGUAGE OverloadedStrings #-}
-- Copyright 2022-2024 Vergara Technologies LLC -- Copyright 2022-2024 Vergara Technologies LLC
-- This file is part of Zcash-Haskell. -- This file is part of Zcash-Haskell.
-- --
@ -15,9 +17,11 @@
module ZcashHaskell.Keys where module ZcashHaskell.Keys where
import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed) import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed)
import Crypto.Secp256k1 (createContext) import Crypto.Secp256k1 (PubKey(..), createContext, exportPubKey)
import qualified Data.Binary as Bi
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.HexString (hexBytes) import qualified Data.ByteString.Lazy as BSL
import Data.HexString (HexString(..), hexBytes)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Data.Word (Word8(..)) import Data.Word (Word8(..))
@ -44,6 +48,9 @@ import ZcashHaskell.Types
, Seed(..) , Seed(..)
, ToBytes(..) , ToBytes(..)
, TransparentSpendingKey(..) , TransparentSpendingKey(..)
, UnifiedFullViewingKey(..)
, UnifiedIncomingViewingKey(..)
, ValidVk(..)
, ZcashNet(..) , ZcashNet(..)
, uniFullViewingKeyHrp , uniFullViewingKeyHrp
, uniIncomingViewingKeyHrp , uniIncomingViewingKeyHrp
@ -70,11 +77,11 @@ getWalletSeed p =
deriveFullTransparentNode :: TransparentSpendingKey -> IO BS.ByteString deriveFullTransparentNode :: TransparentSpendingKey -> IO BS.ByteString
deriveFullTransparentNode sk = do deriveFullTransparentNode sk = do
ioCtx <- createContext ioCtx <- createContext
let tPubKey = deriveXPubKey ioCtx sk let (XPubKey d p i c k) = deriveXPubKey ioCtx sk
let tPubKeyBytes = decodeBase58 $ xPubExport btc ioCtx tPubKey let tPubKeyBytes = BSL.toStrict (Bi.encode c) <> exportPubKey ioCtx True k
case tPubKeyBytes of if BS.length tPubKeyBytes == 65
Nothing -> fail "Unable to get transparent key bytes" then return tPubKeyBytes
Just pb -> return $ BS.takeEnd 65 pb else fail "Unable to get transparent key bytes"
-- | Derive a transparent incoming root node for unified incoming viewing keys -- | Derive a transparent incoming root node for unified incoming viewing keys
deriveIncomingTransparentNode :: TransparentSpendingKey -> IO BS.ByteString deriveIncomingTransparentNode :: TransparentSpendingKey -> IO BS.ByteString
@ -82,72 +89,113 @@ deriveIncomingTransparentNode sk = do
ioCtx <- createContext ioCtx <- createContext
let path = Deriv :/ 0 :: DerivPath let path = Deriv :/ 0 :: DerivPath
let childPrvKey = derivePath ioCtx path sk let childPrvKey = derivePath ioCtx path sk
let tPubKey = deriveXPubKey ioCtx childPrvKey let (XPubKey d p i c k) = deriveXPubKey ioCtx childPrvKey
let tPubKeyBytes = decodeBase58 $ xPubExport btc ioCtx tPubKey let tPubKeyBytes = BSL.toStrict (Bi.encode c) <> exportPubKey ioCtx True k
case tPubKeyBytes of if BS.length tPubKeyBytes == 65
Nothing -> fail "Unable to get transparent key bytes" then return tPubKeyBytes
Just pb -> return $ BS.takeEnd 65 pb else fail "Unable to get transparent key bytes"
-- | Derive a Unified Full Viewing Key -- | Derive a Unified Full Viewing Key
deriveUfvk :: deriveUfvk ::
ZcashNet ZcashNet
-> OrchardSpendingKey -> Maybe OrchardSpendingKey
-> SaplingSpendingKey -> Maybe SaplingSpendingKey
-> TransparentSpendingKey -> Maybe TransparentSpendingKey
-> IO T.Text -> IO ValidVk
deriveUfvk net okey skey tkey = do deriveUfvk net okey skey tkey = do
tSec <- deriveFullTransparentNode tkey tSec <- maybe (return BS.empty) deriveFullTransparentNode tkey
let oSec = deriveOrchardFvk okey let oSec = deriveOrchardFvk =<< okey
let sSec = deriveSaplingFvk skey let sSec = deriveSaplingFvk =<< skey
case oSec of case oSec of
Nothing -> fail "Unable to derive Orchard viewing key" Nothing -> fail "Unable to derive Orchard viewing key"
Just oSec' -> do Just oSec' -> do
case sSec of return $
Nothing -> fail "Unable to derive Sapling viewing key" FullVk $
Just sSec' -> UnifiedFullViewingKey
return $ encodeVK (hexBytes oSec') (hexBytes sSec') tSec net True (case net of
MainNet -> 1
TestNet -> 2)
(hexBytes oSec')
(maybe BS.empty hexBytes sSec)
tSec
-- | Derive a Unified Incoming Viewing Key -- | Derive a Unified Incoming Viewing Key
deriveUivk :: deriveUivk ::
ZcashNet ZcashNet
-> OrchardSpendingKey -> Maybe OrchardSpendingKey
-> SaplingSpendingKey -> Maybe SaplingSpendingKey
-> TransparentSpendingKey -> Maybe TransparentSpendingKey
-> IO T.Text -> IO ValidVk
deriveUivk net okey skey tkey = do deriveUivk net okey skey tkey = do
tSec <- deriveIncomingTransparentNode tkey tSec <- maybe (return BS.empty) deriveIncomingTransparentNode tkey
let oSec = deriveOrchardIvk okey let oSec = deriveOrchardIvk =<< okey
let sSec = deriveSaplingIvk skey let sSec = deriveSaplingIvk =<< skey
case oSec of case oSec of
Nothing -> fail "Unable to derive Orchard viewing key" Nothing -> fail "Unable to derive Orchard viewing key"
Just oSec' -> do Just oSec' -> do
case sSec of return $
Nothing -> fail "Unable to derive Sapling viewing key" IncomingVk $
Just sSec' -> UnifiedIncomingViewingKey
return $ encodeVK (hexBytes oSec') (hexBytes sSec') tSec net False (case net of
MainNet -> 1
TestNet -> 2)
(hexBytes oSec')
(maybe BS.empty hexBytes sSec)
tSec
-- | Encode a Unified Viewing Key per [ZIP-316](https://zips.z.cash/zip-0316) -- | Encode a Unified Viewing Key per [ZIP-316](https://zips.z.cash/zip-0316)
encodeVK :: encodeVK ::
BS.ByteString -- ^ Orchard FVK ValidVk -- ^ The viewing key
-> BS.ByteString -- ^ Sapling FVK
-> BS.ByteString -- ^ Transparent root node
-> ZcashNet -- ^ Network
-> Bool -- ^ Full?
-> T.Text -> T.Text
encodeVK ovk svk tvk net full = encodeBech32m (E.encodeUtf8 hr) b encodeVK vk = encodeBech32m (E.encodeUtf8 hr) b
where where
tReceiver = packReceiver 0x00 $ Just tvk tReceiver =
if tvk /= BS.empty
then packReceiver 0x00 $ Just tvk
else packReceiver 0x00 Nothing
b = f4Jumble $ tReceiver <> sReceiver <> oReceiver <> padding b = f4Jumble $ tReceiver <> sReceiver <> oReceiver <> padding
tvk =
case vk of
FullVk f -> t_key f
IncomingVk i -> i_t_key i
svk =
case vk of
FullVk f -> s_key f
IncomingVk i -> i_s_key i
ovk =
case vk of
FullVk f -> o_key f
IncomingVk i -> i_o_key i
znet =
case vk of
FullVk f ->
if net f == 1
then MainNet
else TestNet
IncomingVk i ->
if i_net i == 1
then MainNet
else TestNet
hr = hr =
if full case vk of
then case net of FullVk _ ->
MainNet -> uniFullViewingKeyHrp case znet of
TestNet -> uniTestFullViewingKeyHrp MainNet -> uniFullViewingKeyHrp
else case net of TestNet -> uniTestFullViewingKeyHrp
MainNet -> uniIncomingViewingKeyHrp IncomingVk _ ->
TestNet -> uniTestIncomingViewingKeyHrp case znet of
sReceiver = packReceiver 0x02 $ Just svk MainNet -> uniIncomingViewingKeyHrp
oReceiver = packReceiver 0x03 $ Just ovk TestNet -> uniTestIncomingViewingKeyHrp
sReceiver =
packReceiver 0x02 $
if svk /= BS.empty
then Just svk
else Nothing
oReceiver =
packReceiver 0x03 $
if ovk /= BS.empty
then Just ovk
else Nothing
padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr
packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString
packReceiver typeCode receiver' = packReceiver typeCode receiver' =

View file

@ -22,11 +22,15 @@ import C.Zcash
, rustWrapperCreateOrchardFvk , rustWrapperCreateOrchardFvk
, rustWrapperCreateOrchardIvk , rustWrapperCreateOrchardIvk
, rustWrapperGenOrchardReceiver , rustWrapperGenOrchardReceiver
, rustWrapperGenOrchardReceiverFvk
, rustWrapperGenOrchardReceiverIvk
, rustWrapperGenOrchardSpendKey , rustWrapperGenOrchardSpendKey
, rustWrapperGetOrchardRootTest , rustWrapperGetOrchardRootTest
, rustWrapperOrchardAddNodeTest , rustWrapperOrchardAddNodeTest
, rustWrapperOrchardCheck , rustWrapperOrchardCheck
, rustWrapperOrchardNoteDecode , rustWrapperOrchardNoteDecode
, rustWrapperOrchardNoteDecodeFvk
, rustWrapperOrchardNoteDecodeIvk
, rustWrapperOrchardNoteDecodeSK , rustWrapperOrchardNoteDecodeSK
, rustWrapperReadOrchardCommitmentTree , rustWrapperReadOrchardCommitmentTree
, rustWrapperReadOrchardFrontier , rustWrapperReadOrchardFrontier
@ -93,6 +97,35 @@ genOrchardReceiver i scope osk =
(fromIntegral i) (fromIntegral i)
(scope == External) (scope == External)
-- | Derives an Orchard receiver for the given full viewing key and index
genOrchardReceiverFvk ::
Int -- ^ The index of the address to be created
-> Scope -- ^ `External` for wallet addresses, `Internal` for change addresses
-> BS.ByteString -- ^ The full viewing key
-> Maybe OrchardReceiver
genOrchardReceiverFvk i scope osk =
if BS.length k /= 43
then Nothing
else Just $ OrchardReceiver k
where
k =
withPureBorshVarBuffer $
rustWrapperGenOrchardReceiverFvk osk (fromIntegral i) (scope == External)
-- | Derives an Orchard receiver for the given incoming viewing key and index
genOrchardReceiverIvk ::
Int -- ^ The index of the address to be created
-> BS.ByteString -- ^ The full viewing key
-> Maybe OrchardReceiver
genOrchardReceiverIvk i osk =
if BS.length k /= 43
then Nothing
else Just $ OrchardReceiver k
where
k =
withPureBorshVarBuffer $
rustWrapperGenOrchardReceiverIvk osk (fromIntegral i)
-- | Checks if given bytestring is a valid encoded unified address -- | Checks if given bytestring is a valid encoded unified address
isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress
isValidUnifiedAddress str = isValidUnifiedAddress str =
@ -185,6 +218,30 @@ decryptOrchardAction key encAction =
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperOrchardNoteDecode (o_key key) encAction rustWrapperOrchardNoteDecode (o_key key) encAction
-- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@.
decryptOrchardActionFvk ::
UnifiedFullViewingKey -> Scope -> OrchardAction -> Maybe DecodedNote
decryptOrchardActionFvk key scope encAction =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction =
withPureBorshVarBuffer $
rustWrapperOrchardNoteDecodeFvk (o_key key) encAction (scope == External)
-- | Attempts to decode the given @OrchardAction@ using the given @UnifiedFullViewingKey@.
decryptOrchardActionIvk ::
UnifiedIncomingViewingKey -> OrchardAction -> Maybe DecodedNote
decryptOrchardActionIvk key encAction =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction =
withPureBorshVarBuffer $
rustWrapperOrchardNoteDecodeIvk (i_o_key key) encAction
getSaplingFromUA :: BS.ByteString -> Maybe T.Text getSaplingFromUA :: BS.ByteString -> Maybe T.Text
getSaplingFromUA uadd = do getSaplingFromUA uadd = do
let a = isValidUnifiedAddress uadd let a = isValidUnifiedAddress uadd

View file

@ -34,9 +34,14 @@ import C.Zcash
, rustWrapperReadSaplingWitness , rustWrapperReadSaplingWitness
, rustWrapperSaplingCheck , rustWrapperSaplingCheck
, rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingChgPaymentAddress
, rustWrapperSaplingChgReceiverFvk
, rustWrapperSaplingDecodeEsk , rustWrapperSaplingDecodeEsk
, rustWrapperSaplingDecodeFvk
, rustWrapperSaplingDecodeIvk
, rustWrapperSaplingNoteDecode , rustWrapperSaplingNoteDecode
, rustWrapperSaplingPaymentAddress , rustWrapperSaplingPaymentAddress
, rustWrapperSaplingReceiverFvk
, rustWrapperSaplingReceiverIvk
, rustWrapperSaplingSpendingkey , rustWrapperSaplingSpendingkey
, rustWrapperSaplingVkDecode , rustWrapperSaplingVkDecode
, rustWrapperTxParse , rustWrapperTxParse
@ -46,7 +51,7 @@ import Data.Aeson
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C import qualified Data.ByteString.Char8 as C
import Data.HexString (HexString(..), fromText, hexString, toBytes, toText) import Data.HexString (HexString(..), fromText, hexString, toBytes, toText)
import Data.Int (Int8) import Data.Int (Int64, Int8)
import qualified Data.Text as T import qualified Data.Text as T
import Data.Word import Data.Word
import Foreign.Rust.Marshall.Variable import Foreign.Rust.Marshall.Variable
@ -95,6 +100,27 @@ decodeSaplingOutput key out =
decodedAction = decodedAction =
withPureBorshVarBuffer $ rustWrapperSaplingNoteDecode key out withPureBorshVarBuffer $ rustWrapperSaplingNoteDecode key out
-- | Attempt to decode the given @ShieldedOutput@ using the given Sapling full viewing key
decodeSaplingOutputFvk ::
BS.ByteString -> ShieldedOutput -> Scope -> Int64 -> Maybe DecodedNote
decodeSaplingOutputFvk fvk so scope pos =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction =
withPureBorshVarBuffer $
rustWrapperSaplingDecodeFvk fvk so (scope == External) pos
-- | Attempt to decode the given @ShieldedOutput@ using the given Sapling incoming viewing key
decodeSaplingOutputIvk :: BS.ByteString -> ShieldedOutput -> Maybe DecodedNote
decodeSaplingOutputIvk fvk so =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction = withPureBorshVarBuffer $ rustWrapperSaplingDecodeIvk fvk so
instance FromJSON RawTxResponse where instance FromJSON RawTxResponse where
parseJSON = parseJSON =
withObject "RawTxResponse" $ \obj -> do withObject "RawTxResponse" $ \obj -> do
@ -184,6 +210,28 @@ genSaplingPaymentAddress i extspk =
(getBytes extspk) (getBytes extspk)
(fromIntegral (i * 111))) (fromIntegral (i * 111)))
-- | Attempts to generate a Sapling receiver using an full viewing key and a diversifier index
genSaplingReceiverFvk :: Int -> BS.ByteString -> Maybe SaplingReceiver
genSaplingReceiverFvk i fvk =
if BS.length res == 43
then Just $ SaplingReceiver res
else Nothing
where
res =
withPureBorshVarBuffer
(rustWrapperSaplingReceiverFvk fvk (fromIntegral (i * 111)))
-- | Attempts to generate a Sapling receiver using an incoming viewing key and a diversifier index
genSaplingReceiverIvk :: Int -> BS.ByteString -> Maybe SaplingReceiver
genSaplingReceiverIvk i ivk =
if BS.length res == 43
then Just $ SaplingReceiver res
else Nothing
where
res =
withPureBorshVarBuffer
(rustWrapperSaplingReceiverIvk ivk (fromIntegral (i * 111)))
-- | Generate an internal Sapling address -- | Generate an internal Sapling address
genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver
genSaplingInternalAddress sk = genSaplingInternalAddress sk =
@ -194,6 +242,15 @@ genSaplingInternalAddress sk =
res = res =
withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk) withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk)
-- | Generate a change Sapling receiver from Full Viewing Key
genSaplingInternalAddressFvk :: BS.ByteString -> Maybe SaplingReceiver
genSaplingInternalAddressFvk fvk =
if BS.length res == 43
then Just $ SaplingReceiver res
else Nothing
where
res = withPureBorshVarBuffer (rustWrapperSaplingChgReceiverFvk fvk)
getSaplingNodeValue :: BS.ByteString -> Maybe HexString getSaplingNodeValue :: BS.ByteString -> Maybe HexString
getSaplingNodeValue cmu = getSaplingNodeValue cmu =
if BS.length (hexBytes n) > 1 if BS.length (hexBytes n) > 1

View file

@ -20,33 +20,36 @@ module ZcashHaskell.Transparent where
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Crypto.Hash import Crypto.Hash
import Crypto.Secp256k1 import Crypto.Secp256k1
import qualified Data.Binary as Bi
import qualified Data.ByteArray as BA import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58) import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58)
import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as Short
import Data.Char (chr) import Data.Char (chr)
import Data.HexString import Data.HexString
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Data.Word import Data.Word
import Haskoin.Address (Address(..)) import Haskoin.Address (Address(..))
import qualified Haskoin.Crypto.Hash as H import qualified Haskoin.Crypto.Hash as H (Hash256(..))
import Haskoin.Crypto.Keys.Extended import Haskoin.Crypto.Keys.Extended
import ZcashHaskell.Types import ZcashHaskell.Types
( AccountId
-- ( AccountId , CoinType(..)
-- , CoinType(..) , ExchangeAddress(..)
-- , Scope(..) , RawData(..)
-- , Seed(..) , Scope(..)
-- , ToBytes(..) , Seed(..)
-- , TransparentAddress(..) , ToBytes(..)
-- , TransparentReceiver(..) , TransparentAddress(..)
-- , TransparentSpendingKey(..) , TransparentReceiver(..)
-- , TransparentType(..) , TransparentSpendingKey(..)
-- , ZcashNet(..) , TransparentType(..)
-- , getTransparentPrefix , ZcashNet(..)
-- , getValue , getTransparentPrefix
-- ) , getValue
)
import ZcashHaskell.Utils (decodeBech32, encodeBech32m) import ZcashHaskell.Utils (decodeBech32, encodeBech32m)
-- | Required for `TransparentReceiver` encoding and decoding -- | Required for `TransparentReceiver` encoding and decoding
@ -103,7 +106,76 @@ genTransparentReceiver i scope xprvk = do
ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j
_anyOtherKind -> throwIO $ userError "Unsupported transparent address type" _anyOtherKind -> throwIO $ userError "Unsupported transparent address type"
-- | Generate a transparent receiver -- | Generate a transparent receiver from a Full Transparent Node
genTransparentReceiverFvk ::
Int -- ^ The index of the address to be created
-> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses
-> BS.ByteString -- ^ The transparent public key
-> IO (Maybe TransparentReceiver)
genTransparentReceiverFvk i scope pubkey = do
ioCtx <- createContext
let s =
case scope of
External -> 0
Internal -> 1
let path = Deriv :/ s :/ fromIntegral i :: SoftPath
let fp' = textToFingerprint "f09f8fbe"
case fp' of
Left e -> fail "couldn't get fingerprint"
Right fp -> do
let x = importPubKey ioCtx $ BS.takeEnd 33 pubkey
case x of
Nothing -> fail "couldn't get pubkey"
Just pk -> do
let prepKey =
XPubKey
3
fp
0
(Bi.decode $ BSL.fromStrict $ BS.take 32 pubkey)
pk
let childPubKey = derivePubPath ioCtx path prepKey
let x = xPubAddr ioCtx childPubKey
case x of
PubKeyAddress k ->
return $ Just $ TransparentReceiver P2PKH $ fromBinary k
ScriptAddress j ->
return $ Just $ TransparentReceiver P2SH $ fromBinary j
_anyOtherKind -> return Nothing
-- | Generate a transparent receiver from a Incoming Transparent Node
genTransparentReceiverIvk ::
Int -- ^ The index of the address to be created
-> BS.ByteString -- ^ The transparent public key
-> IO (Maybe TransparentReceiver)
genTransparentReceiverIvk i pubkey = do
ioCtx <- createContext
let path = Deriv :/ fromIntegral i :: SoftPath
let fp' = textToFingerprint "f09f8fbe"
case fp' of
Left e -> fail "couldn't get fingerprint"
Right fp -> do
let x = importPubKey ioCtx $ BS.takeEnd 33 pubkey
case x of
Nothing -> fail "couldn't get pubkey"
Just pk -> do
let prepKey =
XPubKey
3
fp
0
(Bi.decode $ BSL.fromStrict $ BS.take 32 pubkey)
pk
let childPubKey = derivePubPath ioCtx path prepKey
let x = xPubAddr ioCtx childPubKey
case x of
PubKeyAddress k ->
return $ Just $ TransparentReceiver P2PKH $ fromBinary k
ScriptAddress j ->
return $ Just $ TransparentReceiver P2SH $ fromBinary j
_anyOtherKind -> return Nothing
-- | Generate a transparent spending key
genTransparentSecretKey :: genTransparentSecretKey ::
Int -- ^ The index of the address to be created Int -- ^ The index of the address to be created
-> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses -> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses

View file

@ -685,13 +685,19 @@ data ValidAddress
| Exchange !ExchangeAddress | Exchange !ExchangeAddress
deriving stock (Eq, Prelude.Show) deriving stock (Eq, Prelude.Show)
-- | A type to handle user-entered viewing keys
data ValidVk
= FullVk !UnifiedFullViewingKey
| IncomingVk !UnifiedIncomingViewingKey
deriving stock (Eq, Prelude.Show, Read)
-- | Type to represent a Unified Full Viewing Key -- | Type to represent a Unified Full Viewing Key
data UnifiedFullViewingKey = UnifiedFullViewingKey data UnifiedFullViewingKey = UnifiedFullViewingKey
{ net :: !Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@. { net :: !Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@.
, o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
, s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Full Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
, t_key :: !BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , t_key :: !BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct UnifiedFullViewingKey deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct UnifiedFullViewingKey
@ -702,7 +708,7 @@ data UnifiedIncomingViewingKey = UnifiedIncomingViewingKey
, i_o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Incoming Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , i_o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Incoming Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
, i_s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Incoming Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , i_s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Incoming Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
, i_t_key :: !BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316) , i_t_key :: !BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct

View file

@ -135,7 +135,7 @@ createTransaction ::
-> IO (Either TxError HexString) -> IO (Either TxError HexString)
createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = do createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing znet bh build = do
txResult <- txResult <-
withBorshBufferOfInitSize 51200 $ withBorshBufferOfInitSize 102400 $
rustWrapperCreateTx rustWrapperCreateTx
(hexBytes sapAnchor) (hexBytes sapAnchor)
(hexBytes orchAnchor) (hexBytes orchAnchor)

View file

@ -22,10 +22,13 @@
import C.Zcash (rustWrapperUADecode) import C.Zcash (rustWrapperUADecode)
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Crypto.Secp256k1
import Data.Aeson import Data.Aeson
import qualified Data.Binary as Bi
import Data.Bool (Bool(True)) import Data.Bool (Bool(True))
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy as BSL
import Data.Either (isRight) import Data.Either (isRight)
import Data.Foldable (sequenceA_) import Data.Foldable (sequenceA_)
import Data.HexString import Data.HexString
@ -46,7 +49,8 @@ import Test.Hspec
import Test.Hspec.QuickCheck import Test.Hspec.QuickCheck
import Test.QuickCheck import Test.QuickCheck
import ZcashHaskell.Keys import ZcashHaskell.Keys
( deriveUfvk ( deriveFullTransparentNode
, deriveUfvk
, deriveUivk , deriveUivk
, generateWalletSeedPhrase , generateWalletSeedPhrase
, getWalletSeed , getWalletSeed
@ -56,9 +60,15 @@ import ZcashHaskell.Sapling
( decodeSaplingAddress ( decodeSaplingAddress
, decodeSaplingOutput , decodeSaplingOutput
, decodeSaplingOutputEsk , decodeSaplingOutputEsk
, decodeSaplingOutputFvk
, decodeSaplingOutputIvk
, deriveSaplingFvk
, encodeSaplingAddress , encodeSaplingAddress
, genSaplingInternalAddress , genSaplingInternalAddress
, genSaplingInternalAddressFvk
, genSaplingPaymentAddress , genSaplingPaymentAddress
, genSaplingReceiverFvk
, genSaplingReceiverIvk
, genSaplingSpendingKey , genSaplingSpendingKey
, getSaplingFrontier , getSaplingFrontier
, getSaplingNotePosition , getSaplingNotePosition
@ -109,6 +119,9 @@ import ZcashHaskell.Types
, TransparentType(..) , TransparentType(..)
, UnifiedAddress(..) , UnifiedAddress(..)
, UnifiedFullViewingKey(..) , UnifiedFullViewingKey(..)
, UnifiedIncomingViewingKey(..)
, ValidAddress(..)
, ValidVk(..)
, ZcashNet(..) , ZcashNet(..)
, ZebraTxResponse(..) , ZebraTxResponse(..)
, decodeHexText , decodeHexText
@ -1176,24 +1189,207 @@ main = do
let sK = genSaplingSpendingKey (fromJust seed) MainNetCoin 0 let sK = genSaplingSpendingKey (fromJust seed) MainNetCoin 0
it "Generate FVK" $ do it "Generate FVK" $ do
tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0 tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0
case oK of FullVk fvk <- deriveUfvk MainNet oK sK (Just tK)
Nothing -> assertFailure "Failed to generate Orchard SK" net fvk `shouldBe` 1
Just o ->
case sK of
Nothing -> assertFailure "Failed to generate Sapling SK"
Just s -> do
fvk <- deriveUfvk MainNet o s tK
decodeUfvk (E.encodeUtf8 fvk) `shouldNotBe` Nothing
it "Generate IVK" $ do it "Generate IVK" $ do
tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0 tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0
case oK of IncomingVk ivk <- deriveUivk MainNet oK sK (Just tK)
Nothing -> assertFailure "Failed to generate Orchard SK" i_net ivk `shouldBe` 1
Just o -> describe "Use viewing keys" $ do
case sK of describe "FVKs" $ do
Nothing -> assertFailure "Failed to generate Sapling SK" let fvk =
Just s -> do decodeUfvk
ivk <- deriveUivk MainNet o s tK "uviewtest1jna46ql5qns5rlg99jgs6mhf0j9tk8zxvqsm472scgvmj0vs0rqv2kvdf626gftx7dgn2tltyf0s200gvjlsdvz5celpue9wxxw78txswqmayxc3pfrt5fs5frvr3ep0jrjg8euahqzc63yx9sy4z8lql4ev6q3asptl9rhsfzzrup2g5slwnlvy3dgft44jw3l08xtzypjmsrwxskgnp5s03xlc2kg5520a25pa6fdjxhzutam4wkwr6mh4zeq3qndpks8dk0y90y7gucgsp0j5k2xnhh90m3krk5glz4794dj93pf59h85dqms6337f85ccvpxhays94kvsj2hyjsltf52tygqs8y0vp2yf39drxl687the6xkp8nxkfffc3kqlkhw53t5plplde0vk9rwv340ys04gg48fs0pxfp35rvt2f2pvxjmgmln6lp5k2yzkm0r87k89p6xqv68a6uyfpsauswh9fsckfqey02pjedz5gs934qa"
decodeUivk (E.encodeUtf8 ivk) `shouldNotBe` Nothing let addrFromWallet =
"utest1act45pg36s7345emmp8lpxx560d4tjy2ef7vzzqvdqmt8d5gjxn7x4aarskvqzqccgyjrv5gc4mr6nf2elz6ylpu8hq3rgm0gj43jkl3elrugr49swlk0pv5edvcgnmhrqswkck6kvswvr89h2q0m6gtwktxzdkjp80c86nlp7x02kd0ttpsylsjddk488nmagtj85xclluug793h8n"
let cua =
"utest1zcjhrp39ux52ype4j6055fngdufet0h2d6hx564s48cgnzthm3xma3eca4x8sh5a89jk88nerngvy5uq9tyxgxq552k64rs93ell63f8sd2rurhn34lr6fjznw64uf473mehpn39gy2k0m86r3gpp5lcdh2senl7kwgl6hku03n0gpqvcfc7c48kjv9z49yp52etntgwkltsz3zvj0m"
let oa =
OrchardAction
(hexString
"33d6bf1d78f6414b725b5b43bfbb92b460d81cf04a352831d74bbc3c3aa86c2a")
(hexString
"ce6d2090bf747ddce410ad967ea98e727120710f9814a6d77ebdd593c6f9660b")
(hexString
"567be1d3c5e2b8aba20fba30edd357810eb40647f1f5f9bb4e23691a9c174235")
(hexString
"b4ee13a27e1b7f2674c7b3924c2178b4c010c05750b78b65cb741b10476d1c24")
(hexString
"e8c37d9a2ea88f2ef266fed77753744da9afedf2530940b15ad882cc187a69d6116c5c34cdaf94e1f178ec50ef95d49b46ab6a7206c2a6b57d9d55f65cc7f5f57ccb91d71e827af5286897a4c8ca9c3fc2dad60208adbc8bd3d81eb5febf4c3c75e69b83627fdff730a946bb2e6e6703c4c676675e34ece1b073ce61490298a93503fa10f2b86fbd1274825c72e26b2340d1ed338d3b254803614b2fc9778155c988516d6ce1f334aeca076063c06588961c0d7dfa1bafd59cbca1782fcd863fce1f25abdf151c9718dce3708f8cede130d9e785ed4798aebfca8fec386f4a4d38a75c7b054c453676ad9ce7de32f4c4687f6932686f52e381bd41508a468ca684ea2755d56522a7b4138f58d0b10e1195a6fe27393ca382d0525a74acde5e0f6f2cb8ff3d74221f2a02fe2fd1874a2cf6f542bfa8f0107a21728249ca549bbba66a5422fd9aeaaeff09d1edfecb711c15290c9e8d031baf41fe47a5da3f9a614016f45886fb4d3ea2795802bf7785c1c9e3de15dc18300891f6abb9569851a7c4bf4cd054f8f593969b42c80968bf732ebaa90abf1a7614d1d325e8ed930161e0fbf82333c96cea455743c3e3e3cd1cdd754383a6f2364eacc162059e31885045eee7f2d60b068e78e29490a4df0f34e690b3bb17923669b828d245727bf11b0fe1e0fc4b97b62be37f7a7944db5b5b13221b0000f74c3cdcf9c246e60e77327c4f853f4037eeb2feb9f5d84961e4321c54c30d193866897872b31113a5f819dc2a264d2ef7b91edd16504ad75b6ee622dad79dadaadcd005215932d66232738a7dd8d2")
(hexString
"0b7be09c8f69e068d3277f345a0a4bf11057ff4c46e7657bb8a61a1c2b4c0d61e8e561131f935053f02cb46360f13179dc4647a243fabdd9ac59ba9ac2529a3b7af0ea5020d2b4c17466ebf309448491")
(hexString
"81939f32c4ca40c0a4c9334cffd68339e207afc322b533cb9cbb7b156758dc94")
(hexString
"44e3d8c4fbac16fc0edfd634b396b0a4822fc9a2cfdc8ec293a5c6ffc2001e96d2840c303463e3bc9add8c9263c56569e5b7ac14cf8d84316735f4b77493c136")
let so =
ShieldedOutput
(hexString
"dae2e33897847cc968d4719efc6ff6ea69c7ea8f8567883ba891c3d99b7fc7f3")
(hexString
"3d7c752b89af7dd63430d458d54c60643533ed6db9c57f55131eb7303daf7844")
(hexString
"8f7bd84a946a9068cb2030dd8d1f7446bba2ff22a5cce5ac97cc5c46f1c9bba9")
(hexString
"bc7b212e7c0a598a1237493a014688ee1232f631928e50583df6a1980adefb867dca6f97aa468f3c3a3882db8f359fc0d29205a808173645c22ba8a815e307fec633ffd01d34830e639e4dda548cd6e3ea32054206ac77e60ae09b0bf911066caf2be0bc8e3936edfa3e05c74f2e8a8bf5bbd8867eb83fd7fe24107612f6a0eaa586c6997b19fbe43c3b7d3f8cd5f40ae1a5431cf9d0d336516fb22ce4e295f77326ada3b453b9813a4a63981a52b9ba65af43cc6ec198b384417247b8f1bb15199fba5e90e435c946d03f97c62f7b40fbfef85e456dcb8fdaec94585a600cb40b2ed6c23c08eba5115719944528c156bea996b993d6e1730a17c6cf036deee43a18bd6afbd6e0f55f6c35ab5557df8e1356866514784ab4dd5ff158f0c5bfb6011adefae26c6dcee7472b7086680a72859e231829b7cf408683ced04171065e8740abde2b528c790fbec34d80e15ca869aaa100640c7ab167d7538a48174ea7caac91297455f85a41a03ab760565dd899b6b10b7034b221f4390373bd2dc1e3766629f7d5b606449ef07057995da06a63b6bb07aa117fd26e7154e55636ab8df21c3e3665574899eb1186d2106a55cc20b7eee2bc9b5970acddbac4a74f2f5599d7b42c21e65f340363748b208e61800ce6317e43c7e55757e1c9210bc716a93edb5ca2ed26ead4c14510086fb6752a8bfa089787360cca5c6b053744ad1487f7032ba027ba7758ce842a372b2f7dd418ecbd6353ca199629faa8848dfb7f228610aa2f76324eb7092ca05c6e2e7c7e9972cf33009c716ad43adc492a25fb8d1d67e72f")
(hexString
"ebe23f6ad66a16f9e5faece7139ae55aae93631b8b36ba2b5d21885f29b39dab224549afef0137c612d0c7a8ec597e98ea5a08334392079e5ea5c061cef2ebe296e71b6845e6bea09291bba505a76a03")
(hexString
"ad238bb5d88ccc438998bbcda831b499775f76c706f67bd9e56b8c3d6a6479afcf562a459c8db54003b641f1ac83d2dfa83baf8e04d6c2ab335eec290b3f581b0063cb088b53eb30b372ce80ac86a904448b9766d02624caa42fd9828ba4912e075b786fcbed921bc4cd25555031b2cd92509894d8ade8f4da92007b010b506c5664802e4e80c3363cd6e16c2a67abdcb4f3aef7638095091ab74cf79b6dcb919f7b38547e9576a7512f0f128504bb3461b8fee69ab7774320f56b2af13a6c9d")
it "Orchard external receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
genOrchardReceiverFvk 0 External (o_key k) `shouldNotBe` Nothing
it "Orchard internal receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
genOrchardReceiverFvk 0 Internal (o_key k) `shouldNotBe` Nothing
it "Decrypt Orchard action with FVK" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
a_nullifier <$>
decryptOrchardActionFvk k External oa `shouldBe`
Just (hexString "00")
it "Sapling external receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> genSaplingReceiverFvk 0 (s_key k) `shouldNotBe` Nothing
it "Sapling internal receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
genSaplingInternalAddressFvk (s_key k) `shouldNotBe` Nothing
it "Decode external Sapling with fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
a_nullifier <$>
decodeSaplingOutputFvk (s_key k) so External 234878 `shouldBe`
Just (hexString "00")
it "Transparent external receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
recv <- genTransparentReceiverFvk 0 External (t_key k)
recv `shouldNotBe` Nothing
it "Transparent internal receiver from fvk" $ do
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
recv <- genTransparentReceiverFvk 0 Internal (t_key k)
recv `shouldNotBe` Nothing
it "Generate UA from FVK" $ do
let parsedUA = parseAddress addrFromWallet
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
case parsedUA of
Nothing -> assertFailure "Failed to parse UA"
Just va -> do
case va of
Unified ua -> do
let orec = genOrchardReceiverFvk 0 External (o_key k)
let srec = genSaplingReceiverFvk 0 (s_key k)
trec <- genTransparentReceiverFvk 0 External (t_key k)
let myUa = UnifiedAddress TestNet orec srec trec
myUa `shouldBe` ua
_any -> assertFailure "Address to UA"
it "Generate change UA from FVK" $ do
let parsedCua = parseAddress cua
case fvk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
case parsedCua of
Nothing -> assertFailure "Failed to parse UA"
Just va -> do
case va of
Unified ua -> do
let orec = genOrchardReceiverFvk 0 Internal (o_key k)
let srec = genSaplingInternalAddressFvk (s_key k)
trec <- genTransparentReceiverFvk 0 Internal (t_key k)
let myUa = UnifiedAddress TestNet orec srec trec
myUa `shouldBe` ua
_any -> assertFailure "Address to UA"
describe "IVKs" $ do
let ivk =
decodeUivk
"uivktest10yy38f5v5sz5hnne93flkrpfqr4geyfuaq97u4fe9zkrng20ppeqsu7a6fkfttn0phjj9kutlnn4nzvfls233xujcv3fw6ax9w8pqzsw792py78dl8p4cyhv3eyusu589382qwf4pwz3vs7n6pv4qj7j6apg5nvw4yl4u5g033tpr2a7jjzt3f3khjm09wj7er7myr8vy04595nj6pqcdp3ndp98ckg82y0w08d8wnx7wynrzhmqrxh9pf0m50tj332vjld08uz027xwyegakyzem745y462khv9xzlc4h5rxwsqfklvak3x26excpcjn54e3cd2zttxujnuzv3rrtrpuld9e2"
let addrFromWallet =
"utest1act45pg36s7345emmp8lpxx560d4tjy2ef7vzzqvdqmt8d5gjxn7x4aarskvqzqccgyjrv5gc4mr6nf2elz6ylpu8hq3rgm0gj43jkl3elrugr49swlk0pv5edvcgnmhrqswkck6kvswvr89h2q0m6gtwktxzdkjp80c86nlp7x02kd0ttpsylsjddk488nmagtj85xclluug793h8n"
let oa =
OrchardAction
(hexString
"33d6bf1d78f6414b725b5b43bfbb92b460d81cf04a352831d74bbc3c3aa86c2a")
(hexString
"ce6d2090bf747ddce410ad967ea98e727120710f9814a6d77ebdd593c6f9660b")
(hexString
"567be1d3c5e2b8aba20fba30edd357810eb40647f1f5f9bb4e23691a9c174235")
(hexString
"b4ee13a27e1b7f2674c7b3924c2178b4c010c05750b78b65cb741b10476d1c24")
(hexString
"e8c37d9a2ea88f2ef266fed77753744da9afedf2530940b15ad882cc187a69d6116c5c34cdaf94e1f178ec50ef95d49b46ab6a7206c2a6b57d9d55f65cc7f5f57ccb91d71e827af5286897a4c8ca9c3fc2dad60208adbc8bd3d81eb5febf4c3c75e69b83627fdff730a946bb2e6e6703c4c676675e34ece1b073ce61490298a93503fa10f2b86fbd1274825c72e26b2340d1ed338d3b254803614b2fc9778155c988516d6ce1f334aeca076063c06588961c0d7dfa1bafd59cbca1782fcd863fce1f25abdf151c9718dce3708f8cede130d9e785ed4798aebfca8fec386f4a4d38a75c7b054c453676ad9ce7de32f4c4687f6932686f52e381bd41508a468ca684ea2755d56522a7b4138f58d0b10e1195a6fe27393ca382d0525a74acde5e0f6f2cb8ff3d74221f2a02fe2fd1874a2cf6f542bfa8f0107a21728249ca549bbba66a5422fd9aeaaeff09d1edfecb711c15290c9e8d031baf41fe47a5da3f9a614016f45886fb4d3ea2795802bf7785c1c9e3de15dc18300891f6abb9569851a7c4bf4cd054f8f593969b42c80968bf732ebaa90abf1a7614d1d325e8ed930161e0fbf82333c96cea455743c3e3e3cd1cdd754383a6f2364eacc162059e31885045eee7f2d60b068e78e29490a4df0f34e690b3bb17923669b828d245727bf11b0fe1e0fc4b97b62be37f7a7944db5b5b13221b0000f74c3cdcf9c246e60e77327c4f853f4037eeb2feb9f5d84961e4321c54c30d193866897872b31113a5f819dc2a264d2ef7b91edd16504ad75b6ee622dad79dadaadcd005215932d66232738a7dd8d2")
(hexString
"0b7be09c8f69e068d3277f345a0a4bf11057ff4c46e7657bb8a61a1c2b4c0d61e8e561131f935053f02cb46360f13179dc4647a243fabdd9ac59ba9ac2529a3b7af0ea5020d2b4c17466ebf309448491")
(hexString
"81939f32c4ca40c0a4c9334cffd68339e207afc322b533cb9cbb7b156758dc94")
(hexString
"44e3d8c4fbac16fc0edfd634b396b0a4822fc9a2cfdc8ec293a5c6ffc2001e96d2840c303463e3bc9add8c9263c56569e5b7ac14cf8d84316735f4b77493c136")
let so =
ShieldedOutput
(hexString
"dae2e33897847cc968d4719efc6ff6ea69c7ea8f8567883ba891c3d99b7fc7f3")
(hexString
"3d7c752b89af7dd63430d458d54c60643533ed6db9c57f55131eb7303daf7844")
(hexString
"8f7bd84a946a9068cb2030dd8d1f7446bba2ff22a5cce5ac97cc5c46f1c9bba9")
(hexString
"bc7b212e7c0a598a1237493a014688ee1232f631928e50583df6a1980adefb867dca6f97aa468f3c3a3882db8f359fc0d29205a808173645c22ba8a815e307fec633ffd01d34830e639e4dda548cd6e3ea32054206ac77e60ae09b0bf911066caf2be0bc8e3936edfa3e05c74f2e8a8bf5bbd8867eb83fd7fe24107612f6a0eaa586c6997b19fbe43c3b7d3f8cd5f40ae1a5431cf9d0d336516fb22ce4e295f77326ada3b453b9813a4a63981a52b9ba65af43cc6ec198b384417247b8f1bb15199fba5e90e435c946d03f97c62f7b40fbfef85e456dcb8fdaec94585a600cb40b2ed6c23c08eba5115719944528c156bea996b993d6e1730a17c6cf036deee43a18bd6afbd6e0f55f6c35ab5557df8e1356866514784ab4dd5ff158f0c5bfb6011adefae26c6dcee7472b7086680a72859e231829b7cf408683ced04171065e8740abde2b528c790fbec34d80e15ca869aaa100640c7ab167d7538a48174ea7caac91297455f85a41a03ab760565dd899b6b10b7034b221f4390373bd2dc1e3766629f7d5b606449ef07057995da06a63b6bb07aa117fd26e7154e55636ab8df21c3e3665574899eb1186d2106a55cc20b7eee2bc9b5970acddbac4a74f2f5599d7b42c21e65f340363748b208e61800ce6317e43c7e55757e1c9210bc716a93edb5ca2ed26ead4c14510086fb6752a8bfa089787360cca5c6b053744ad1487f7032ba027ba7758ce842a372b2f7dd418ecbd6353ca199629faa8848dfb7f228610aa2f76324eb7092ca05c6e2e7c7e9972cf33009c716ad43adc492a25fb8d1d67e72f")
(hexString
"ebe23f6ad66a16f9e5faece7139ae55aae93631b8b36ba2b5d21885f29b39dab224549afef0137c612d0c7a8ec597e98ea5a08334392079e5ea5c061cef2ebe296e71b6845e6bea09291bba505a76a03")
(hexString
"ad238bb5d88ccc438998bbcda831b499775f76c706f67bd9e56b8c3d6a6479afcf562a459c8db54003b641f1ac83d2dfa83baf8e04d6c2ab335eec290b3f581b0063cb088b53eb30b372ce80ac86a904448b9766d02624caa42fd9828ba4912e075b786fcbed921bc4cd25555031b2cd92509894d8ade8f4da92007b010b506c5664802e4e80c3363cd6e16c2a67abdcb4f3aef7638095091ab74cf79b6dcb919f7b38547e9576a7512f0f128504bb3461b8fee69ab7774320f56b2af13a6c9d")
it "Orchard external receiver from ivk" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> genOrchardReceiverIvk 0 (i_o_key k) `shouldNotBe` Nothing
it "Decrypt Orchard action with IVK" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> decryptOrchardActionIvk k oa `shouldNotBe` Nothing
it "Sapling external receiver from ivk" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> genSaplingReceiverIvk 0 (i_s_key k) `shouldNotBe` Nothing
it "Decode external Sapling with ivk" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k ->
decodeSaplingOutputIvk (i_s_key k) so `shouldNotBe` Nothing
it "Transparent external receiver from ivk" $ do
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
recv <- genTransparentReceiverIvk 0 (i_t_key k)
recv `shouldNotBe` Nothing
it "Generate UA from IVK" $ do
let parsedUA = parseAddress addrFromWallet
case ivk of
Nothing -> assertFailure "Failed to parse VK"
Just k -> do
case parsedUA of
Nothing -> assertFailure "Failed to parse UA"
Just va -> do
case va of
Unified ua -> do
let orec = genOrchardReceiverIvk 0 (i_o_key k)
let srec = genSaplingReceiverIvk 0 (i_s_key k)
trec <- genTransparentReceiverIvk 0 (i_t_key k)
let myUa = UnifiedAddress TestNet orec srec trec
myUa `shouldBe` ua
_any -> assertFailure "Address to UA"
-- | Properties -- | Properties
prop_PhraseLength :: Property prop_PhraseLength :: Property

View file

@ -5,7 +5,7 @@ cabal-version: 3.0
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
name: zcash-haskell name: zcash-haskell
version: 0.7.8.1 version: 0.8.1.0
synopsis: Utilities to interact with the Zcash blockchain synopsis: Utilities to interact with the Zcash blockchain
description: Please see the README on the repo at <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme> description: Please see the README on the repo at <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme>
category: Blockchain category: Blockchain