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/),
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]
### Changed

View file

@ -3,13 +3,13 @@ constraints: any.Cabal ==3.10.3.0,
any.Cabal-syntax ==3.10.3.0,
any.HUnit ==1.6.2.0,
any.OneTuple ==0.4.2,
any.QuickCheck ==2.14.3,
any.QuickCheck ==2.15.0.1,
QuickCheck -old-random +templatehaskell,
any.StateVar ==1.2.2,
any.aeson ==2.2.3.0,
aeson +ordered-keymap,
any.alex ==3.5.1.0,
any.ansi-terminal ==1.1.1,
any.alex ==3.5.2.0,
any.ansi-terminal ==1.1.2,
ansi-terminal -example,
any.ansi-terminal-types ==1.1,
any.appar ==0.1.8,
@ -25,7 +25,7 @@ constraints: any.Cabal ==3.10.3.0,
attoparsec -developer,
any.attoparsec-aeson ==2.2.2.0,
any.base ==4.18.2.1,
any.base-orphans ==0.9.2,
any.base-orphans ==0.9.3,
any.base16 ==1.0,
any.base16-bytestring ==1.0.2.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.borsh ==0.3.0,
any.byteorder ==1.0.4,
any.bytes ==0.17.3,
any.bytes ==0.17.4,
any.bytestring ==0.11.5.3,
any.c2hs ==0.28.8,
c2hs +base3 -regression,
@ -52,28 +52,25 @@ constraints: any.Cabal ==3.10.3.0,
cereal -bytestring-builder,
any.character-ps ==0.1,
any.colour ==2.3.6,
any.comonad ==5.0.8,
any.comonad ==5.0.9,
comonad +containers +distributive +indexed-traversable,
any.conduit ==1.3.6,
any.conduit-extra ==1.3.6,
any.conduit-extra ==1.3.7,
any.containers ==0.6.7,
any.contravariant ==1.5.5,
contravariant +semigroups +statevar +tagged,
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,
any.crypton-connection ==0.4.1,
any.crypton-connection ==0.4.3,
any.crypton-x509 ==1.7.7,
any.crypton-x509-store ==1.6.9,
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,
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-class ==0.1.2.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-default ==0.8.0.0,
any.data-default-class ==0.2.0.0,
any.data-fix ==0.3.4,
any.deepseq ==1.4.8.1,
any.directory ==1.3.8.4,
@ -81,9 +78,9 @@ constraints: any.Cabal ==3.10.3.0,
distributive +semigroups +tagged,
any.dlist ==1.0,
dlist -werror,
any.entropy ==0.4.1.10,
any.entropy ==0.4.1.11,
entropy -donotgetentropy,
any.envy ==2.1.3.0,
any.envy ==2.1.4.0,
any.exceptions ==0.10.7,
any.filepath ==1.4.300.1,
any.foreign-rust ==0.1.0,
@ -92,56 +89,61 @@ constraints: any.Cabal ==3.10.3.0,
any.ghc-bignum ==1.3,
any.ghc-boot-th ==9.6.5,
any.ghc-prim ==0.10.0,
any.half ==0.3.1,
any.happy ==2.0.2,
any.happy-lib ==2.0.2,
any.hashable ==1.4.7.0,
hashable -arch-native +integer-gmp -random-initial-seed,
any.haskell-lexer ==1.1.1,
any.half ==0.3.2,
any.happy ==2.1.4,
any.happy-lib ==2.1.4,
any.hashable ==1.5.0.0,
hashable -arch-native -random-initial-seed,
any.haskell-lexer ==1.1.2,
any.haskoin-core ==1.1.0,
any.hexstring ==0.12.1.0,
any.hourglass ==0.2.12,
any.hsc2hs ==0.68.10,
hsc2hs -in-ghc-tree,
any.hspec ==2.11.9,
any.hspec-core ==2.11.9,
any.hspec-discover ==2.11.9,
any.hspec ==2.11.10,
any.hspec-core ==2.11.10,
any.hspec-discover ==2.11.10,
any.hspec-expectations ==0.8.4,
any.http-client ==0.7.17,
any.http-client ==0.7.18,
http-client +network-uri,
any.http-client-tls ==0.3.6.3,
any.http-conduit ==2.3.9,
any.http-client-tls ==0.3.6.4,
any.http-conduit ==2.3.9.1,
http-conduit +aeson,
any.http-types ==0.12.4,
any.indexed-traversable ==0.1.4,
any.indexed-traversable-instances ==0.1.2,
any.integer-conversion ==0.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,
any.iproute ==1.7.14,
any.language-c ==0.9.3,
language-c -allwarnings +iecfpextension +usebytestrings,
any.iproute ==1.7.15,
any.language-c ==0.10.0,
language-c +iecfpextension +usebytestrings,
any.memory ==0.18.0,
memory +support_bytestring +support_deepseq,
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.murmur3 ==1.0.5,
any.network ==3.2.4.0,
any.network ==3.2.7.0,
network -devel,
any.network-uri ==2.6.4.2,
any.old-locale ==1.0.0.7,
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.pem ==0.2.4,
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.process ==1.6.19.0,
any.quickcheck-io ==0.2.0,
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-compat ==0.95.2.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.scientific ==0.3.8.0,
scientific -integer-simple,
any.secp256k1-haskell ==1.4.0,
any.secp256k1-haskell ==1.4.2,
any.semialign ==1.3.1,
semialign +semigroupoids,
any.semigroupoids ==6.0.1,
@ -161,42 +163,44 @@ constraints: any.Cabal ==3.10.3.0,
any.socks ==0.6.1,
any.sop-core ==0.5.0.2,
any.split ==0.2.5,
any.splitmix ==0.1.0.5,
any.splitmix ==0.1.1,
splitmix -optimised-mixer,
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,
any.strict ==0.5.1,
any.string-conversions ==0.4.0.1,
any.tagged ==0.8.8,
any.tagged ==0.8.9,
tagged +deepseq +transformers,
any.tasty ==1.5.3,
tasty +unix,
any.template-haskell ==2.20.0.0,
any.text ==2.0.2,
any.text-iso8601 ==0.1.1,
any.text-short ==0.1.6,
text-short -asserts,
any.tf-random ==0.5,
any.th-abstraction ==0.7.0.0,
any.th-compat ==0.1.5,
any.th-abstraction ==0.7.1.0,
any.th-compat ==0.1.6,
any.these ==1.2.1,
any.time ==1.12.2,
any.time-compat ==1.9.7,
any.tls ==2.1.0,
any.time-compat ==1.9.8,
any.tls ==2.1.7,
tls -devel,
any.transformers ==0.6.1.0,
any.transformers-compat ==0.7.2,
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
any.typed-process ==0.2.12.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.unordered-containers ==0.2.20,
unordered-containers -debug,
any.utf8-string ==1.0.2,
any.uuid-types ==1.0.6,
any.vector ==0.13.1.0,
any.vector ==0.13.2.0,
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,
any.vector-stream ==0.1.0.1,
any.void ==0.7.3,
@ -205,4 +209,4 @@ constraints: any.Cabal ==3.10.3.0,
any.witherable ==0.5,
any.zlib ==0.7.1.0,
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::{
SaplingDomain,
Zip212Enforcement
Zip212Enforcement,
try_sapling_note_decryption
},
bundle::{
GrothProofBytes,
@ -82,7 +83,8 @@ use sapling_crypto::{
},
zip32::{
sapling_find_address,
DiversifierKey
DiversifierKey,
IncomingViewingKey as SaplingIncomingViewingKey
}
};
@ -157,7 +159,7 @@ use orchard::{
Flags
},
Action,
keys::{SpendAuthorizingKey, SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope},
keys::{SpendAuthorizingKey, SpendingKey, FullViewingKey, IncomingViewingKey, PreparedIncomingViewingKey, Scope},
note::{Rho, RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
note_encryption::OrchardDomain,
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]
pub extern "C" fn rust_wrapper_orchard_note_decrypt(
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]
pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk(
key: *const u8,
@ -1370,31 +1575,22 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress(
out_len: &mut usize
){
let extspk: Vec<u8> = marshall_from_haskell_var(extspk, extspk_len, RW);
if div_ix == 0 {
let sp_key = ExtendedSpendingKey::from_bytes(&extspk);
match sp_key {
Ok(sp_key_x) => {
let (_def_div, def_address) = sp_key_x.default_address();
let (_def_div, def_address) =
if div_ix == 0 {
sp_key_x.default_address()
} else {
let dfvk = sp_key_x.to_diversifiable_full_viewing_key();
dfvk.find_address(DiversifierIndex::from(div_ix)).unwrap()
};
marshall_to_haskell_var(&def_address.to_bytes().to_vec(), out, out_len, RW);
},
Err(_e) => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
} else {
let expsk = ExpandedSpendingKey::from_spending_key(&extspk);
let fvk = SaplingFullViewingKey::from_expanded_spending_key(&expsk);
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);
}
}
}
}
#[no_mangle]
@ -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);
}
#[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]
pub extern "C" fn rust_wrapper_derive_orchard_spending_key(
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]
pub extern "C" fn rust_wrapper_bech32_encode(
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
{ toBorshVar* `BS.ByteString'&
, 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
{ toBorshVar* `SaplingFrontier'&
, 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
-- This file is part of Zcash-Haskell.
--
@ -15,9 +17,11 @@
module ZcashHaskell.Keys where
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 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.Encoding as E
import Data.Word (Word8(..))
@ -44,6 +48,9 @@ import ZcashHaskell.Types
, Seed(..)
, ToBytes(..)
, TransparentSpendingKey(..)
, UnifiedFullViewingKey(..)
, UnifiedIncomingViewingKey(..)
, ValidVk(..)
, ZcashNet(..)
, uniFullViewingKeyHrp
, uniIncomingViewingKeyHrp
@ -70,11 +77,11 @@ getWalletSeed p =
deriveFullTransparentNode :: TransparentSpendingKey -> IO BS.ByteString
deriveFullTransparentNode sk = do
ioCtx <- createContext
let tPubKey = deriveXPubKey ioCtx sk
let tPubKeyBytes = decodeBase58 $ xPubExport btc ioCtx tPubKey
case tPubKeyBytes of
Nothing -> fail "Unable to get transparent key bytes"
Just pb -> return $ BS.takeEnd 65 pb
let (XPubKey d p i c k) = deriveXPubKey ioCtx sk
let tPubKeyBytes = BSL.toStrict (Bi.encode c) <> exportPubKey ioCtx True k
if BS.length tPubKeyBytes == 65
then return tPubKeyBytes
else fail "Unable to get transparent key bytes"
-- | Derive a transparent incoming root node for unified incoming viewing keys
deriveIncomingTransparentNode :: TransparentSpendingKey -> IO BS.ByteString
@ -82,72 +89,113 @@ deriveIncomingTransparentNode sk = do
ioCtx <- createContext
let path = Deriv :/ 0 :: DerivPath
let childPrvKey = derivePath ioCtx path sk
let tPubKey = deriveXPubKey ioCtx childPrvKey
let tPubKeyBytes = decodeBase58 $ xPubExport btc ioCtx tPubKey
case tPubKeyBytes of
Nothing -> fail "Unable to get transparent key bytes"
Just pb -> return $ BS.takeEnd 65 pb
let (XPubKey d p i c k) = deriveXPubKey ioCtx childPrvKey
let tPubKeyBytes = BSL.toStrict (Bi.encode c) <> exportPubKey ioCtx True k
if BS.length tPubKeyBytes == 65
then return tPubKeyBytes
else fail "Unable to get transparent key bytes"
-- | Derive a Unified Full Viewing Key
deriveUfvk ::
ZcashNet
-> OrchardSpendingKey
-> SaplingSpendingKey
-> TransparentSpendingKey
-> IO T.Text
-> Maybe OrchardSpendingKey
-> Maybe SaplingSpendingKey
-> Maybe TransparentSpendingKey
-> IO ValidVk
deriveUfvk net okey skey tkey = do
tSec <- deriveFullTransparentNode tkey
let oSec = deriveOrchardFvk okey
let sSec = deriveSaplingFvk skey
tSec <- maybe (return BS.empty) deriveFullTransparentNode tkey
let oSec = deriveOrchardFvk =<< okey
let sSec = deriveSaplingFvk =<< skey
case oSec of
Nothing -> fail "Unable to derive Orchard viewing key"
Just oSec' -> do
case sSec of
Nothing -> fail "Unable to derive Sapling viewing key"
Just sSec' ->
return $ encodeVK (hexBytes oSec') (hexBytes sSec') tSec net True
return $
FullVk $
UnifiedFullViewingKey
(case net of
MainNet -> 1
TestNet -> 2)
(hexBytes oSec')
(maybe BS.empty hexBytes sSec)
tSec
-- | Derive a Unified Incoming Viewing Key
deriveUivk ::
ZcashNet
-> OrchardSpendingKey
-> SaplingSpendingKey
-> TransparentSpendingKey
-> IO T.Text
-> Maybe OrchardSpendingKey
-> Maybe SaplingSpendingKey
-> Maybe TransparentSpendingKey
-> IO ValidVk
deriveUivk net okey skey tkey = do
tSec <- deriveIncomingTransparentNode tkey
let oSec = deriveOrchardIvk okey
let sSec = deriveSaplingIvk skey
tSec <- maybe (return BS.empty) deriveIncomingTransparentNode tkey
let oSec = deriveOrchardIvk =<< okey
let sSec = deriveSaplingIvk =<< skey
case oSec of
Nothing -> fail "Unable to derive Orchard viewing key"
Just oSec' -> do
case sSec of
Nothing -> fail "Unable to derive Sapling viewing key"
Just sSec' ->
return $ encodeVK (hexBytes oSec') (hexBytes sSec') tSec net False
return $
IncomingVk $
UnifiedIncomingViewingKey
(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)
encodeVK ::
BS.ByteString -- ^ Orchard FVK
-> BS.ByteString -- ^ Sapling FVK
-> BS.ByteString -- ^ Transparent root node
-> ZcashNet -- ^ Network
-> Bool -- ^ Full?
ValidVk -- ^ The viewing key
-> T.Text
encodeVK ovk svk tvk net full = encodeBech32m (E.encodeUtf8 hr) b
encodeVK vk = encodeBech32m (E.encodeUtf8 hr) b
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
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 =
if full
then case net of
case vk of
FullVk _ ->
case znet of
MainNet -> uniFullViewingKeyHrp
TestNet -> uniTestFullViewingKeyHrp
else case net of
IncomingVk _ ->
case znet of
MainNet -> uniIncomingViewingKeyHrp
TestNet -> uniTestIncomingViewingKeyHrp
sReceiver = packReceiver 0x02 $ Just svk
oReceiver = packReceiver 0x03 $ Just ovk
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
packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString
packReceiver typeCode receiver' =

View file

@ -22,11 +22,15 @@ import C.Zcash
, rustWrapperCreateOrchardFvk
, rustWrapperCreateOrchardIvk
, rustWrapperGenOrchardReceiver
, rustWrapperGenOrchardReceiverFvk
, rustWrapperGenOrchardReceiverIvk
, rustWrapperGenOrchardSpendKey
, rustWrapperGetOrchardRootTest
, rustWrapperOrchardAddNodeTest
, rustWrapperOrchardCheck
, rustWrapperOrchardNoteDecode
, rustWrapperOrchardNoteDecodeFvk
, rustWrapperOrchardNoteDecodeIvk
, rustWrapperOrchardNoteDecodeSK
, rustWrapperReadOrchardCommitmentTree
, rustWrapperReadOrchardFrontier
@ -93,6 +97,35 @@ genOrchardReceiver i scope osk =
(fromIntegral i)
(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
isValidUnifiedAddress :: BS.ByteString -> Maybe UnifiedAddress
isValidUnifiedAddress str =
@ -185,6 +218,30 @@ decryptOrchardAction key encAction =
withPureBorshVarBuffer $
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 uadd = do
let a = isValidUnifiedAddress uadd

View file

@ -34,9 +34,14 @@ import C.Zcash
, rustWrapperReadSaplingWitness
, rustWrapperSaplingCheck
, rustWrapperSaplingChgPaymentAddress
, rustWrapperSaplingChgReceiverFvk
, rustWrapperSaplingDecodeEsk
, rustWrapperSaplingDecodeFvk
, rustWrapperSaplingDecodeIvk
, rustWrapperSaplingNoteDecode
, rustWrapperSaplingPaymentAddress
, rustWrapperSaplingReceiverFvk
, rustWrapperSaplingReceiverIvk
, rustWrapperSaplingSpendingkey
, rustWrapperSaplingVkDecode
, rustWrapperTxParse
@ -46,7 +51,7 @@ import Data.Aeson
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
import Data.HexString (HexString(..), fromText, hexString, toBytes, toText)
import Data.Int (Int8)
import Data.Int (Int64, Int8)
import qualified Data.Text as T
import Data.Word
import Foreign.Rust.Marshall.Variable
@ -95,6 +100,27 @@ decodeSaplingOutput key out =
decodedAction =
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
parseJSON =
withObject "RawTxResponse" $ \obj -> do
@ -184,6 +210,28 @@ genSaplingPaymentAddress i extspk =
(getBytes extspk)
(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
genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver
genSaplingInternalAddress sk =
@ -194,6 +242,15 @@ genSaplingInternalAddress sk =
res =
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 cmu =
if BS.length (hexBytes n) > 1

View file

@ -20,33 +20,36 @@ module ZcashHaskell.Transparent where
import Control.Exception (throwIO)
import Crypto.Hash
import Crypto.Secp256k1
import qualified Data.Binary as Bi
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
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.HexString
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import Data.Word
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 ZcashHaskell.Types
-- ( AccountId
-- , CoinType(..)
-- , Scope(..)
-- , Seed(..)
-- , ToBytes(..)
-- , TransparentAddress(..)
-- , TransparentReceiver(..)
-- , TransparentSpendingKey(..)
-- , TransparentType(..)
-- , ZcashNet(..)
-- , getTransparentPrefix
-- , getValue
-- )
( AccountId
, CoinType(..)
, ExchangeAddress(..)
, RawData(..)
, Scope(..)
, Seed(..)
, ToBytes(..)
, TransparentAddress(..)
, TransparentReceiver(..)
, TransparentSpendingKey(..)
, TransparentType(..)
, ZcashNet(..)
, getTransparentPrefix
, getValue
)
import ZcashHaskell.Utils (decodeBech32, encodeBech32m)
-- | Required for `TransparentReceiver` encoding and decoding
@ -103,7 +106,76 @@ genTransparentReceiver i scope xprvk = do
ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j
_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 ::
Int -- ^ The index of the address to be created
-> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses

View file

@ -685,13 +685,19 @@ data ValidAddress
| Exchange !ExchangeAddress
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
data UnifiedFullViewingKey = UnifiedFullViewingKey
{ 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)
, 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)
} deriving stock (Eq, Prelude.Show, GHC.Generic)
} deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
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_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)
} deriving stock (Eq, Prelude.Show, GHC.Generic)
} deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct

View file

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

View file

@ -22,10 +22,13 @@
import C.Zcash (rustWrapperUADecode)
import Control.Exception (throwIO)
import Control.Monad.IO.Class (liftIO)
import Crypto.Secp256k1
import Data.Aeson
import qualified Data.Binary as Bi
import Data.Bool (Bool(True))
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy as BSL
import Data.Either (isRight)
import Data.Foldable (sequenceA_)
import Data.HexString
@ -46,7 +49,8 @@ import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck
import ZcashHaskell.Keys
( deriveUfvk
( deriveFullTransparentNode
, deriveUfvk
, deriveUivk
, generateWalletSeedPhrase
, getWalletSeed
@ -56,9 +60,15 @@ import ZcashHaskell.Sapling
( decodeSaplingAddress
, decodeSaplingOutput
, decodeSaplingOutputEsk
, decodeSaplingOutputFvk
, decodeSaplingOutputIvk
, deriveSaplingFvk
, encodeSaplingAddress
, genSaplingInternalAddress
, genSaplingInternalAddressFvk
, genSaplingPaymentAddress
, genSaplingReceiverFvk
, genSaplingReceiverIvk
, genSaplingSpendingKey
, getSaplingFrontier
, getSaplingNotePosition
@ -109,6 +119,9 @@ import ZcashHaskell.Types
, TransparentType(..)
, UnifiedAddress(..)
, UnifiedFullViewingKey(..)
, UnifiedIncomingViewingKey(..)
, ValidAddress(..)
, ValidVk(..)
, ZcashNet(..)
, ZebraTxResponse(..)
, decodeHexText
@ -1176,24 +1189,207 @@ main = do
let sK = genSaplingSpendingKey (fromJust seed) MainNetCoin 0
it "Generate FVK" $ do
tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0
case oK of
Nothing -> assertFailure "Failed to generate Orchard SK"
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
FullVk fvk <- deriveUfvk MainNet oK sK (Just tK)
net fvk `shouldBe` 1
it "Generate IVK" $ do
tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0
case oK of
Nothing -> assertFailure "Failed to generate Orchard SK"
Just o ->
case sK of
Nothing -> assertFailure "Failed to generate Sapling SK"
Just s -> do
ivk <- deriveUivk MainNet o s tK
decodeUivk (E.encodeUtf8 ivk) `shouldNotBe` Nothing
IncomingVk ivk <- deriveUivk MainNet oK sK (Just tK)
i_net ivk `shouldBe` 1
describe "Use viewing keys" $ do
describe "FVKs" $ do
let fvk =
decodeUfvk
"uviewtest1jna46ql5qns5rlg99jgs6mhf0j9tk8zxvqsm472scgvmj0vs0rqv2kvdf626gftx7dgn2tltyf0s200gvjlsdvz5celpue9wxxw78txswqmayxc3pfrt5fs5frvr3ep0jrjg8euahqzc63yx9sy4z8lql4ev6q3asptl9rhsfzzrup2g5slwnlvy3dgft44jw3l08xtzypjmsrwxskgnp5s03xlc2kg5520a25pa6fdjxhzutam4wkwr6mh4zeq3qndpks8dk0y90y7gucgsp0j5k2xnhh90m3krk5glz4794dj93pf59h85dqms6337f85ccvpxhays94kvsj2hyjsltf52tygqs8y0vp2yf39drxl687the6xkp8nxkfffc3kqlkhw53t5plplde0vk9rwv340ys04gg48fs0pxfp35rvt2f2pvxjmgmln6lp5k2yzkm0r87k89p6xqv68a6uyfpsauswh9fsckfqey02pjedz5gs934qa"
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
prop_PhraseLength :: Property

View file

@ -5,7 +5,7 @@ cabal-version: 3.0
-- see: https://github.com/sol/hpack
name: zcash-haskell
version: 0.7.8.1
version: 0.8.1.0
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>
category: Blockchain