Compare commits

..

18 commits

Author SHA1 Message Date
1f8a4cbd2b Branch rvv040 - Get Sapling Human Readable representation - In proccess
Bug - Bech32m error calculating checksum - to resolve
2024-04-09 20:23:16 -04:00
817c52dacf
Merge pull request 'Add Sapling output decrypting with spending key' (#55) from rav001 into dev040
Reviewed-on: #55
2024-04-08 18:01:25 +00:00
e00cf21709
Add Sapling output decrypting with spending key 2024-04-08 12:55:30 -05:00
2709d42266
Merge pull request 'Correct the parsing of the tx hash in TxIn' (#54) from rav001 into dev040
Reviewed-on: #54
2024-04-05 19:14:13 +00:00
5814cb7c9e
Correct the parsing of the tx hash in TxIn 2024-04-05 14:12:39 -05:00
a06fd08172
Merge pull request 'Fix TxIn parsing' (#53) from rav001 into dev040
Reviewed-on: #53
2024-04-05 17:52:02 +00:00
3c2e8fc994
Fix TxIn parsing 2024-04-05 12:47:17 -05:00
f375467fee
Merge pull request 'Implement identification of transparent output' (#52) from rav001 into dev040
Reviewed-on: #52
2024-04-03 21:21:08 +00:00
09411adfff
Implement test to validate transparent outputs 2024-04-03 16:16:42 -05:00
2d39576dc8
Merge branch 'dev040' into rav001 2024-04-02 14:06:07 -05:00
938ccb4b97
Add test to identify transparent outputs (#50)
Reviewed-on: #50
Co-authored-by: Rene Vergara <rene@vergara.network>
Co-committed-by: Rene Vergara <rene@vergara.network>
2024-04-02 19:03:50 +00:00
8b1965b46f
Merge branch 'dev040' into rav001 2024-04-02 14:03:11 -05:00
2f18c15c86
Add test to identify transparent outputs 2024-04-02 13:42:45 -05:00
204275a9b6
Merge pull request 'Implements FFI for transactions from Zebra' (#49) from rav001 into dev040
Reviewed-on: #49
2024-04-02 17:09:42 +00:00
98fb89b7b0
Merge branch 'dev040' into rav001 2024-04-02 11:36:14 -05:00
6ad879c155
Implements FFI for transaction deserialization 2024-04-02 11:23:05 -05:00
474aadb5cd
Merge pull request 'Update Zebra block deserialization for block time' (#48) from rvv040 into dev040
Reviewed-on: #48
2024-04-01 13:09:47 +00:00
dbae2150da
Advance bundle FFI 2024-03-29 13:54:21 -05:00
11 changed files with 484 additions and 109 deletions

View file

@ -5,6 +5,16 @@ 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.5.3.0]
### Added
- Function to decode Sapling outputs with a spending key
### Fixed
- Parsing of `TxIn` for FFI
## [0.5.2.0] ## [0.5.2.0]
### Added ### Added

View file

@ -961,36 +961,6 @@ dependencies = [
"zcash_note_encryption", "zcash_note_encryption",
] ]
[[package]]
name = "orchard"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42"
dependencies = [
"aes",
"bitvec",
"blake2b_simd",
"ff",
"fpe",
"group",
"halo2_gadgets",
"halo2_proofs",
"hex",
"incrementalmerkletree",
"lazy_static",
"memuse",
"nonempty",
"pasta_curves",
"rand",
"reddsa",
"serde",
"subtle",
"tracing",
"zcash_note_encryption",
"zcash_spec",
"zip32",
]
[[package]] [[package]]
name = "pairing" name = "pairing"
version = "0.23.0" version = "0.23.0"
@ -1314,7 +1284,9 @@ dependencies = [
"borsh 0.10.3", "borsh 0.10.3",
"f4jumble", "f4jumble",
"haskell-ffi", "haskell-ffi",
"orchard 0.7.1", "incrementalmerkletree",
"nonempty",
"orchard",
"proc-macro2", "proc-macro2",
"zcash_address 0.2.0", "zcash_address 0.2.0",
"zcash_client_backend", "zcash_client_backend",
@ -1743,7 +1715,7 @@ dependencies = [
"incrementalmerkletree", "incrementalmerkletree",
"memuse", "memuse",
"nom", "nom",
"orchard 0.6.0", "orchard",
"percent-encoding", "percent-encoding",
"prost", "prost",
"rayon", "rayon",
@ -1806,7 +1778,7 @@ dependencies = [
"lazy_static", "lazy_static",
"memuse", "memuse",
"nonempty", "nonempty",
"orchard 0.6.0", "orchard",
"rand", "rand",
"rand_core", "rand_core",
"sha2 0.10.6", "sha2 0.10.6",
@ -1816,15 +1788,6 @@ dependencies = [
"zcash_note_encryption", "zcash_note_encryption",
] ]
[[package]]
name = "zcash_spec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b"
dependencies = [
"blake2b_simd",
]
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.6.0" version = "1.6.0"

View file

@ -11,12 +11,14 @@ f4jumble = "0.1"
zcash_address = "0.2.0" zcash_address = "0.2.0"
borsh = "0.10" borsh = "0.10"
bech32 = "0.11" bech32 = "0.11"
orchard = "0.7.0" orchard = "0.6.0"
zcash_note_encryption = "0.4.0" zcash_note_encryption = "0.4.0"
zcash_primitives = "0.13.0" zcash_primitives = "0.13.0"
zcash_client_backend = "0.10.0" zcash_client_backend = "0.10.0"
zip32 = "0.1.0" zip32 = "0.1.0"
proc-macro2 = "1.0.66" proc-macro2 = "1.0.66"
nonempty = "0.7.0"
incrementalmerkletree = "0.5.0"
[features] [features]

View file

@ -10,6 +10,8 @@ use std::{
}, },
}; };
use nonempty::NonEmpty;
use f4jumble; use f4jumble;
use borsh::{BorshDeserialize, BorshSerialize}; use borsh::{BorshDeserialize, BorshSerialize};
@ -21,6 +23,8 @@ use haskell_ffi::{
FromHaskell, HaskellSize, ToHaskell FromHaskell, HaskellSize, ToHaskell
}; };
use incrementalmerkletree::frontier::CommitmentTree;
use zip32; use zip32;
use zcash_primitives::{ use zcash_primitives::{
@ -32,6 +36,7 @@ use zcash_primitives::{
}, },
zip339::{Count, Mnemonic}, zip339::{Count, Mnemonic},
transaction::components::{ transaction::components::{
amount::Amount,
transparent::{ transparent::{
Bundle as TransparentBundle, Bundle as TransparentBundle,
TxIn, TxIn,
@ -41,10 +46,16 @@ use zcash_primitives::{
}, },
sapling::{ sapling::{
GrothProofBytes, GrothProofBytes,
OutputDescription OutputDescription,
SpendDescription,
Authorized as SaplingAuthorized,
Bundle as SaplingBundle
} }
}, },
sapling::{ sapling::{
Node,
MerklePath,
NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH,
PaymentAddress, PaymentAddress,
keys::{ keys::{
PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey,
@ -57,6 +68,7 @@ use zcash_primitives::{
consensus::{ consensus::{
BranchId::Nu5, BranchId::Nu5,
MainNetwork, MainNetwork,
TestNetwork,
BlockHeight BlockHeight
} }
}; };
@ -78,6 +90,11 @@ use zcash_primitives::zip32::DiversifierIndex;
use zcash_primitives::block::BlockHeader; use zcash_primitives::block::BlockHeader;
use orchard::{ use orchard::{
Bundle as OrchardBundle,
bundle::{
Authorized as OrchardAuthorized,
Flags
},
Action, Action,
keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope},
note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
@ -130,12 +147,12 @@ impl<RW> ToHaskell<RW> for HrawTx {
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
pub struct HshieldedOutput { pub struct HshieldedOutput {
cv: Vec<u8>, cv: Hhex,
cmu: Vec<u8>, cmu: Hhex,
eph_key: Vec<u8>, eph_key: Hhex,
enc_txt: Vec<u8>, enc_txt: Hhex,
out_txt: Vec<u8>, out_txt: Hhex,
proof: Vec<u8> proof: Hhex
} }
impl<RW> FromHaskell<RW> for HshieldedOutput { impl<RW> FromHaskell<RW> for HshieldedOutput {
@ -153,9 +170,15 @@ impl<RW> ToHaskell<RW> for HshieldedOutput {
} }
impl HshieldedOutput { impl HshieldedOutput {
fn from_object(s: OutputDescription<GrothProofBytes>) -> Result<HshieldedOutput> { fn from_object(s: &OutputDescription<GrothProofBytes>) -> HshieldedOutput {
let o = HshieldedOutput { cv: s.cv().to_bytes().to_vec(), cmu: s.cmu().to_bytes().to_vec(), eph_key: s.ephemeral_key().0.to_vec(), enc_txt: s.enc_ciphertext().to_vec(), out_txt: s.out_ciphertext().to_vec(), proof: s.zkproof().to_vec() }; HshieldedOutput { cv: Hhex{ bytes: s.cv().to_bytes().to_vec()}, cmu: Hhex{ bytes: s.cmu().to_bytes().to_vec()}, eph_key: Hhex{ bytes: s.ephemeral_key().0.to_vec()}, enc_txt: Hhex{ bytes: s.enc_ciphertext().to_vec()}, out_txt: Hhex{ bytes: s.out_ciphertext().to_vec()}, proof: Hhex{ bytes: s.zkproof().to_vec()} }
Ok(o) }
pub fn pack(sp: &[OutputDescription<GrothProofBytes>]) -> Vec<HshieldedOutput> {
let mut r = Vec::new();
for s in sp {
r.push(HshieldedOutput::from_object(s));
}
return r
} }
} }
@ -183,6 +206,16 @@ impl<RW> FromHaskell<RW> for Haction {
} }
} }
impl Haction {
pub fn pack(sp: &NonEmpty<Action<Signature<SpendAuth>>>) -> Vec<Haction> {
let mut r = Vec::new();
for s in sp {
r.push(Haction {nf: Hhex { bytes: s.nullifier().to_bytes().to_vec()}, rk: Hhex { bytes: <[u8; 32]>::from(s.rk()).to_vec()}, cmx: Hhex{bytes: s.cmx().to_bytes().to_vec()}, eph_key: Hhex{ bytes: s.encrypted_note().epk_bytes.to_vec()}, enc_txt: Hhex {bytes: s.encrypted_note().enc_ciphertext.to_vec()}, out_txt: Hhex {bytes: s.encrypted_note().out_ciphertext.to_vec()}, cv: Hhex {bytes: s.cv_net().to_bytes().to_vec()}, auth: Hhex { bytes: <[u8; 64]>::from(s.authorization()).to_vec()}});
}
return r
}
}
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
pub struct Hnote { pub struct Hnote {
note: u64, note: u64,
@ -235,7 +268,9 @@ pub struct Htx {
txid: Vec<u8>, txid: Vec<u8>,
locktime: u32, locktime: u32,
expiry: u32, expiry: u32,
t_bundle: HTBundle t_bundle: HTBundle,
s_bundle: HSBundle,
o_bundle: HOBundle
} }
impl<RW> ToHaskell<RW> for Htx { impl<RW> ToHaskell<RW> for Htx {
@ -325,6 +360,105 @@ impl Houtpoint {
} }
} }
#[derive(BorshSerialize, BorshDeserialize)]
pub struct HSBundle {
empty: bool,
spends: Vec<Hspend>,
outputs: Vec<HshieldedOutput> ,
value: i64,
sig: Vec<u8>
}
impl<RW> ToHaskell<RW> for HSBundle {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
Ok(())
}
}
impl HSBundle {
pub fn from_bundle(sb: &SaplingBundle<SaplingAuthorized>) -> HSBundle {
let s = Cursor::new(Vec::new());
sb.authorization().binding_sig.write(s.clone()).unwrap();
return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: s.into_inner() }
}
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Hspend {
cv: Hhex,
anchor: Hhex,
nullifier: Hhex,
rk: Hhex,
proof: Hhex,
authsig: Hhex
}
impl<RW> ToHaskell<RW> for Hspend {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
Ok(())
}
}
impl Hspend {
pub fn pack(sp: &[SpendDescription<SaplingAuthorized>]) -> Vec<Hspend> {
let mut r = Vec::new();
for s in sp {
let rk = Cursor::new(Vec::new());
let authsig = Cursor::new(Vec::new());
s.rk().write(rk.clone()).unwrap();
s.spend_auth_sig().write(authsig.clone()).unwrap();
r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: rk.into_inner()}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes:authsig.into_inner()}});
}
return r
}
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct HOBundle {
empty: bool,
actions: Vec<Haction>,
flags: Hflags,
value: i64,
anchor: Hhex,
proof: Hhex,
bindingsig: Hhex
}
impl<RW> ToHaskell<RW> for HOBundle {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
Ok(())
}
}
impl HOBundle {
pub fn from_bundle(b: &OrchardBundle<OrchardAuthorized, Amount>) -> HOBundle {
return HOBundle {empty: false, actions: Haction::pack(b.actions()), flags: Hflags::pack(b.flags()), value: i64::from(b.value_balance()), anchor: Hhex{ bytes: b.anchor().to_bytes().to_vec()}, proof: Hhex { bytes: b.authorization().proof().as_ref().to_vec()}, bindingsig: Hhex {bytes: <[u8; 64]>::from(b.authorization().binding_signature()).to_vec()}}
}
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Hflags {
spends: bool,
outputs: bool
}
impl<RW> ToHaskell<RW> for Hflags {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
Ok(())
}
}
impl Hflags {
pub fn pack(f: &Flags) -> Hflags {
return Hflags {spends: f.spends_enabled(), outputs: f.outputs_enabled()}
}
}
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
pub struct Hufvk { pub struct Hufvk {
net: u8, net: u8,
@ -474,7 +608,7 @@ pub extern "C" fn rust_wrapper_svk_decode(
let input: Vec<u8> = marshall_from_haskell_var(input, input_len, RW); let input: Vec<u8> = marshall_from_haskell_var(input, input_len, RW);
let svk = ExtendedFullViewingKey::read(&*input); let svk = ExtendedFullViewingKey::read(&*input);
match svk { match svk {
Ok(k) => { Ok(_k) => {
true true
} }
Err(e) => { Err(e) => {
@ -570,6 +704,64 @@ pub extern "C" fn rust_wrapper_ufvk_decode(
} }
} }
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_esk_decrypt(
key: *const u8,
key_len: usize,
note: *const u8,
note_len: usize,
external: bool,
net: bool,
out: *mut u8,
out_len: &mut usize
){
let sk: Vec<u8> = marshall_from_haskell_var(key, key_len, RW);
let note_input: Vec<u8> = marshall_from_haskell_var(note,note_len,RW);
let mut note_reader = Cursor::new(note_input);
let esk = ExtendedSpendingKey::from_bytes(&sk);
let main_domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(419200));
let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(419200));
let scope = if external {
SaplingScope::External
} else {
SaplingScope::Internal
};
match esk {
Ok(k) => {
let action = OutputDescription::read(&mut note_reader);
match action {
Ok(action2) => {
let dfvk = k.to_diversifiable_full_viewing_key();
let ivk = dfvk.to_ivk(scope);
let nk = dfvk.to_nk(scope);
let pivk = SaplingPreparedIncomingViewingKey::new(&ivk);
let result = if net { zcash_note_encryption::try_note_decryption(&main_domain, &pivk, &action2)}
else {zcash_note_encryption::try_note_decryption(&test_domain, &pivk, &action2)};
match result {
Some((n, r, m)) => {
//let nullifier = n.nf(&nk, MerklePath<Node::from_cmu(&n.cmu()), SAPLING_DEPTH>.position());
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec() };
marshall_to_haskell_var(&hn, out, out_len, RW);
},
None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
Err(_e1) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
},
Err(_e) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] };
marshall_to_haskell_var(&hn0, out, out_len, RW);
}
}
}
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2( pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
key: *const u8, key: *const u8,
@ -594,11 +786,11 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action3); let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action3);
match result { match result {
Some((n, r, m)) => { Some((n, r, m)) => {
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec() }; let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec()};
marshall_to_haskell_var(&hn, out, out_len, RW); marshall_to_haskell_var(&hn, out, out_len, RW);
} }
None => { None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
@ -610,7 +802,7 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
} }
} }
Err(_e) => { Err(_e) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
@ -644,17 +836,17 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action); let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
match result { match result {
Some((n, r, m)) => { Some((n, r, m)) => {
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec() }; let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec()};
marshall_to_haskell_var(&hn, out, out_len, RW); marshall_to_haskell_var(&hn, out, out_len, RW);
} }
None => { None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
}, },
None => { None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0]};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
@ -674,19 +866,38 @@ pub extern "C" fn rust_wrapper_tx_read(
match parsed_tx { match parsed_tx {
Ok(t) => { Ok(t) => {
let tb = t.transparent_bundle(); let tb = t.transparent_bundle();
match tb { let sb = t.sapling_bundle();
Some(my_tb) => { let ob = t.orchard_bundle();
let h = Htx {txid: t.txid().as_ref().to_vec(), locktime: t.lock_time(), expiry: u32::from(t.expiry_height()), t_bundle: HTBundle::from_bundle(my_tb) }; let h1 = Htx
marshall_to_haskell_var(&h, out, out_len, RW); { txid: t.txid().as_ref().to_vec()
}, , locktime: t.lock_time()
None => { , expiry: u32::from(t.expiry_height())
let h0 = Htx {txid: t.txid().as_ref().to_vec(), locktime: t.lock_time(), expiry: u32::from(t.expiry_height()), t_bundle: HTBundle {empty: true, vin: vec![HTxIn {outpoint: Houtpoint {hash: vec![0], index: 0}, script: vec![0], sequence: 0}], vout: vec![HTxOut {amt: 0, script: vec![0]}], coinbase: true} }; , t_bundle: match tb {
marshall_to_haskell_var(&h0, out, out_len, RW); Some(tb1) => {HTBundle::from_bundle(tb1)},
} None => {HTBundle {empty: true, vin: vec![HTxIn {outpoint: Houtpoint {hash: vec![0], index: 0}, script: vec![0], sequence: 0}], vout: vec![HTxOut {amt: 0, script: vec![0]}], coinbase: false}}}
, s_bundle: match sb {
Some(sb1) => {HSBundle::from_bundle(sb1)},
None => {HSBundle{empty: true, spends: vec![Hspend{cv:Hhex { bytes: vec![0]} , anchor:Hhex { bytes: vec![0]} , nullifier:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , proof:Hhex { bytes: vec![0]} , authsig:Hhex { bytes: vec![0]} }], outputs: vec![HshieldedOutput {cv: Hhex { bytes: vec![0]}, cmu: Hhex { bytes: vec![0]}, eph_key: Hhex { bytes: vec![0]}, enc_txt: Hhex { bytes: vec![0]}, out_txt: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]}}], value: 0, sig: vec![0]}} }
, o_bundle: match ob {
Some(ob1) => {HOBundle::from_bundle(ob1)},
None => {HOBundle{empty: true, actions: vec![Haction {nf:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , cmx:Hhex { bytes: vec![0]} , eph_key:Hhex { bytes: vec![0]} , enc_txt:Hhex { bytes: vec![0]} , out_txt:Hhex { bytes: vec![0]} , cv:Hhex { bytes: vec![0]} , auth:Hhex { bytes: vec![0]} }], flags: Hflags{ spends:false, outputs:false}, value: 0, anchor: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]} , bindingsig: Hhex { bytes: vec![0]}}}
} }
};
marshall_to_haskell_var(&h1, out, out_len, RW);
}, },
Err(_e) => { Err(_e) => {
let h0 = Htx {txid: vec![0], locktime: 0, expiry: 0, t_bundle: HTBundle {empty: true, vin: vec![HTxIn {outpoint: Houtpoint {hash: vec![0], index: 0}, script: vec![0], sequence: 0}], vout: vec![HTxOut {amt: 0, script: vec![0]}], coinbase: true} }; let h0 = Htx
{txid: vec![0],
locktime: 0,
expiry: 0,
t_bundle: HTBundle
{empty: true,
vin: vec![HTxIn {outpoint: Houtpoint {hash: vec![0], index: 0}, script: vec![0], sequence: 0}],
vout: vec![HTxOut {amt: 0, script: vec![0]}],
coinbase: true},
s_bundle: HSBundle{empty: true, spends: vec![Hspend{cv:Hhex { bytes: vec![0]} , anchor:Hhex { bytes: vec![0]} , nullifier:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , proof:Hhex { bytes: vec![0]} , authsig:Hhex { bytes: vec![0]} }], outputs: vec![HshieldedOutput {cv: Hhex { bytes: vec![0]}, cmu: Hhex { bytes: vec![0]}, eph_key: Hhex { bytes: vec![0]}, enc_txt: Hhex { bytes: vec![0]}, out_txt: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]}}], value: 0, sig: vec![0]},
o_bundle: HOBundle{empty: true, actions: vec![Haction {nf:Hhex { bytes: vec![0]} , rk:Hhex { bytes: vec![0]} , cmx:Hhex { bytes: vec![0]} , eph_key:Hhex { bytes: vec![0]} , enc_txt:Hhex { bytes: vec![0]} , out_txt:Hhex { bytes: vec![0]} , cv:Hhex { bytes: vec![0]} , auth:Hhex { bytes: vec![0]} }], flags: Hflags{ spends:false, outputs:false}, value: 0, anchor: Hhex { bytes: vec![0]}, proof: Hhex { bytes: vec![0]} , bindingsig: Hhex { bytes: vec![0]}}
};
marshall_to_haskell_var(&h0, out, out_len, RW); marshall_to_haskell_var(&h0, out, out_len, RW);
} }
} }
@ -847,7 +1058,7 @@ pub extern "C" fn rust_wrapper_derive_orchard_spending_key(
out_len: &mut usize out_len: &mut usize
){ ){
let s: Vec<u8> = marshall_from_haskell_var(seed, seed_len, RW); let s: Vec<u8> = marshall_from_haskell_var(seed, seed_len, RW);
let sk = SpendingKey::from_zip32_seed(&s, coin_type, zip32::AccountId::try_from(acc_id).unwrap()); let sk = SpendingKey::from_zip32_seed(&s, coin_type, u32::from(zip32::AccountId::try_from(acc_id).unwrap()));
match sk { match sk {
Ok(key) => { Ok(key) => {
marshall_to_haskell_var(&key.to_bytes().to_vec(), out, out_len, RW); marshall_to_haskell_var(&key.to_bytes().to_vec(), out, out_len, RW);

View file

@ -101,6 +101,16 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_sapling_esk_decrypt as rustWrapperSaplingDecodeEsk
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, `Bool'
, `Bool'
, getVarBuffer `Buffer DecodedNote'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_ufvk_decode as rustWrapperUfvkDecode {# fun unsafe rust_wrapper_ufvk_decode as rustWrapperUfvkDecode
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer UnifiedFullViewingKey'& , getVarBuffer `Buffer UnifiedFullViewingKey'&

View file

@ -153,3 +153,21 @@ decryptOrchardAction key encAction =
decodedAction = decodedAction =
withPureBorshVarBuffer $ withPureBorshVarBuffer $
rustWrapperOrchardNoteDecode (o_key key) encAction rustWrapperOrchardNoteDecode (o_key key) encAction
chrToByteString :: [Char] -> C.ByteString
chrToByteString = C.pack
getSaplingFromUA :: BS.ByteString -> T.Text
getSaplingFromUA uadd = do
let a = isValidUnifiedAddress uadd
case a of
Nothing -> "xxx"
Just a -> do
let sraw = s_rec a
case sraw of
Nothing -> "xxx"
Just sraw -> do
let net = ua_net a
case net of
MainNet -> encodeBech32m (chrToByteString sapPaymentAddressHrp) ( getBytes sraw )
TestNet -> encodeBech32m (chrToByteString sapTestPaymentAddressHrp) ( getBytes sraw )

View file

@ -21,6 +21,7 @@ import C.Zcash
( rustWrapperIsShielded ( rustWrapperIsShielded
, rustWrapperSaplingCheck , rustWrapperSaplingCheck
, rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingChgPaymentAddress
, rustWrapperSaplingDecodeEsk
, rustWrapperSaplingNoteDecode , rustWrapperSaplingNoteDecode
, rustWrapperSaplingPaymentAddress , rustWrapperSaplingPaymentAddress
, rustWrapperSaplingSpendingkey , rustWrapperSaplingSpendingkey
@ -29,7 +30,7 @@ import C.Zcash
) )
import Data.Aeson import Data.Aeson
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.HexString (HexString(..), toBytes) import Data.HexString (HexString(..), fromText, toBytes, toText)
import Data.Word import Data.Word
import Foreign.Rust.Marshall.Variable import Foreign.Rust.Marshall.Variable
( withPureBorshVarBuffer ( withPureBorshVarBuffer
@ -43,9 +44,11 @@ import ZcashHaskell.Types
, RawTxResponse(..) , RawTxResponse(..)
, SaplingReceiver(..) , SaplingReceiver(..)
, SaplingSpendingKey(..) , SaplingSpendingKey(..)
, Scope(..)
, Seed(..) , Seed(..)
, ShieldedOutput(..) , ShieldedOutput(..)
, ToBytes(..) , ToBytes(..)
, ZcashNet(..)
, decodeHexText , decodeHexText
, getValue , getValue
) )
@ -58,6 +61,15 @@ isValidShieldedAddress = rustWrapperIsShielded
getShieldedOutputs :: HexString -> [BS.ByteString] getShieldedOutputs :: HexString -> [BS.ByteString]
getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse $ toBytes t getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse $ toBytes t
serializeShieldedOutput :: ShieldedOutput -> BS.ByteString
serializeShieldedOutput so =
hexBytes . fromText $
toText (s_cv so) <>
toText (s_cmu so) <>
toText (s_ephKey so) <>
toText (s_encCipherText so) <>
toText (s_outCipherText so) <> toText (s_proof so)
-- | Check if given bytestring is a valid Sapling viewing key -- | Check if given bytestring is a valid Sapling viewing key
isValidSaplingViewingKey :: BS.ByteString -> Bool isValidSaplingViewingKey :: BS.ByteString -> Bool
isValidSaplingViewingKey k = isValidSaplingViewingKey k =
@ -98,6 +110,26 @@ instance FromJSON RawTxResponse where
a <- o' .: "actions" a <- o' .: "actions"
pure $ RawTxResponse i h sSpend (getShieldedOutputs h) a ht c b pure $ RawTxResponse i h sSpend (getShieldedOutputs h) a ht c b
-- | Attempt to decode the given raw tx with the given Sapling spending key
decodeSaplingOutputEsk ::
SaplingSpendingKey
-> ShieldedOutput
-> ZcashNet
-> Scope
-> Maybe DecodedNote
decodeSaplingOutputEsk key out znet scope =
case a_value decodedAction of
0 -> Nothing
_ -> Just decodedAction
where
decodedAction =
withPureBorshVarBuffer $
rustWrapperSaplingDecodeEsk
(getBytes key)
(serializeShieldedOutput out)
(znet == MainNet)
(scope == External)
-- | Attempts to obtain a sapling SpendingKey using a HDSeed -- | Attempts to obtain a sapling SpendingKey using a HDSeed
genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey
genSaplingSpendingKey seed c i = do genSaplingSpendingKey seed c i = do

View file

@ -129,6 +129,8 @@ data Transaction = Transaction
, tx_conf :: !Int , tx_conf :: !Int
, tx_expiry :: !Int , tx_expiry :: !Int
, tx_transpBundle :: !(Maybe TransparentBundle) , tx_transpBundle :: !(Maybe TransparentBundle)
, tx_saplingBundle :: !(Maybe SaplingBundle)
, tx_orchardBundle :: !(Maybe OrchardBundle)
} deriving (Prelude.Show, Eq, Read) } deriving (Prelude.Show, Eq, Read)
-- | The transparent portion of a Zcash transaction -- | The transparent portion of a Zcash transaction
@ -158,7 +160,7 @@ fromRawTxIn t = H.TxIn op (rti_script t) (rti_seq t)
then H.nullOutPoint then H.nullOutPoint
else H.OutPoint else H.OutPoint
((fromJust . ((fromJust .
H.hexToTxHash . E.decodeUtf8Lenient . rop_hash . rti_outpoint) H.hexToTxHash . toText . fromRawBytes . rop_hash . rti_outpoint)
t) t)
(rop_n $ rti_outpoint t) (rop_n $ rti_outpoint t)
@ -285,6 +287,8 @@ data RawZebraTx = RawZebraTx
, zt_locktime :: !Word32 , zt_locktime :: !Word32
, zt_expiry :: !Word32 , zt_expiry :: !Word32
, zt_tBundle :: !RawTBundle , zt_tBundle :: !RawTBundle
, zt_sBundle :: !RawSBundle
, zt_oBundle :: !RawOBundle
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
@ -301,6 +305,78 @@ data RawTBundle = RawTBundle
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTBundle deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTBundle
-- | Type for a raw deserialized Zebra Sapling bundle
data RawSBundle = RawSBundle
{ zsb_empty :: !Bool
, zsb_spends :: ![ShieldedSpend]
, zsb_outputs :: ![ShieldedOutput]
, zsb_value :: !Int64
, zsb_sig :: !HexString
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawSBundle
data SaplingBundle = SaplingBundle
{ sbSpends :: ![ShieldedSpend]
, sbOutputs :: ![ShieldedOutput]
, sbValue :: !Int64
, sbSig :: !HexString
} deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
fromRawSBundle :: RawSBundle -> Maybe SaplingBundle
fromRawSBundle b =
if zsb_empty b
then Nothing
else Just $
SaplingBundle (zsb_spends b) (zsb_outputs b) (zsb_value b) (zsb_sig b)
-- | Type for a raw deseralized Zebra Orchard bundle
data RawOBundle = RawOBundle
{ zob_empty :: !Bool
, zob_actions :: ![OrchardAction]
, zob_flags :: !OrchardFlags
, zob_value :: !Int64
, zob_anchor :: !HexString
, zob_proof :: !HexString
, zob_sig :: !HexString
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawOBundle
-- | Type for an Orchard Bundle
data OrchardBundle = OrchardBundle
{ obActions :: ![OrchardAction]
, obFlags :: !OrchardFlags
, obValue :: !Int64
, obAnchor :: !HexString
, obProof :: !HexString
, obSig :: !HexString
} deriving stock (Eq, Prelude.Show, GHC.Generic, Read)
fromRawOBundle :: RawOBundle -> Maybe OrchardBundle
fromRawOBundle b =
if zob_empty b
then Nothing
else Just $
OrchardBundle
(zob_actions b)
(zob_flags b)
(zob_value b)
(zob_anchor b)
(zob_proof b)
(zob_sig b)
-- | Type for the Orchard bundle flags
data OrchardFlags = OrchardFlags
{ of_spends :: !Bool
, of_outputs :: !Bool
} 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 OrchardFlags
-- | Type for the response from the `zebrad` RPC method `getinfo` -- | Type for the response from the `zebrad` RPC method `getinfo`
data ZebraGetInfo = ZebraGetInfo data ZebraGetInfo = ZebraGetInfo
{ zgi_build :: !T.Text { zgi_build :: !T.Text
@ -427,7 +503,7 @@ data ShieldedOutput = ShieldedOutput
, s_encCipherText :: !HexString -- ^ The output note encrypted to the recipient , s_encCipherText :: !HexString -- ^ The output note encrypted to the recipient
, s_outCipherText :: !HexString -- ^ A ciphertext enabling the sender to recover the output note , s_outCipherText :: !HexString -- ^ A ciphertext enabling the sender to recover the output note
, s_proof :: !HexString -- ^ Zero-knowledge proof using the Sapling Output circuit , s_proof :: !HexString -- ^ Zero-knowledge proof using the Sapling Output circuit
} 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 ShieldedOutput deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct ShieldedOutput
@ -482,10 +558,10 @@ data RawUA = RawUA
-- | 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)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
@ -493,14 +569,14 @@ data UnifiedFullViewingKey = UnifiedFullViewingKey
-- | Type to represent an Orchard Action as provided by the @getrawtransaction@ RPC method of @zcashd@, and defined in the [Zcash Protocol](https://zips.z.cash/protocol/protocol.pdf) -- | Type to represent an Orchard Action as provided by the @getrawtransaction@ RPC method of @zcashd@, and defined in the [Zcash Protocol](https://zips.z.cash/protocol/protocol.pdf)
data OrchardAction = OrchardAction data OrchardAction = OrchardAction
{ nf :: HexString -- ^ The nullifier of the input note { nf :: !HexString -- ^ The nullifier of the input note
, rk :: HexString -- ^ The randomized validating key for @auth@ , rk :: !HexString -- ^ The randomized validating key for @auth@
, cmx :: HexString -- ^ The x-coordinate of the note commitment for the output note , cmx :: !HexString -- ^ The x-coordinate of the note commitment for the output note
, eph_key :: HexString -- ^ An encoding of an ephemeral Pallas public key , eph_key :: !HexString -- ^ An encoding of an ephemeral Pallas public key
, enc_ciphertext :: HexString -- ^ The output note encrypted to the recipient , enc_ciphertext :: !HexString -- ^ The output note encrypted to the recipient
, out_ciphertext :: HexString -- ^ A ciphertext enabling the sender to recover the output note , out_ciphertext :: !HexString -- ^ A ciphertext enabling the sender to recover the output note
, cv :: HexString -- ^ A value commitment to the net value of the input note minus the output note , cv :: !HexString -- ^ A value commitment to the net value of the input note minus the output note
, auth :: HexString -- ^ A signature authorizing the spend in this Action , auth :: !HexString -- ^ A signature authorizing the spend in this Action
} deriving stock (Eq, Prelude.Show, GHC.Generic, Read) } 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)
@ -521,9 +597,9 @@ instance FromJSON OrchardAction where
-- | Type to represent a decoded note -- | Type to represent a decoded note
data DecodedNote = DecodedNote data DecodedNote = DecodedNote
{ a_value :: Int64 -- ^ The amount of the transaction in _zatoshis_. { a_value :: !Int64 -- ^ The amount of the transaction in _zatoshis_.
, a_recipient :: BS.ByteString -- ^ The recipient Orchard receiver. , a_recipient :: !BS.ByteString -- ^ The recipient Orchard receiver.
, a_memo :: BS.ByteString -- ^ The decoded shielded memo field. , a_memo :: !BS.ByteString -- ^ The decoded shielded memo field.
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)

View file

@ -115,3 +115,4 @@ readZebraTransaction hex =
else Just rawTx else Just rawTx
where where
rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex

File diff suppressed because one or more lines are too long

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.5.2.0 version: 0.5.3.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