// Copyright 2022-2024 Vergara Technologies LLC // // This file is part of Zcash-Haskell. // use std::{ marker::PhantomData, io::{ Write, Cursor }, }; use nonempty::NonEmpty; use rand_core::OsRng; use f4jumble; use borsh::{BorshDeserialize, BorshSerialize}; use haskell_ffi::{ error::Result, from_haskell::{marshall_from_haskell_var, marshall_from_haskell_fixed}, to_haskell::{marshall_to_haskell_var, marshall_to_haskell_fixed}, FromHaskell, HaskellSize, ToHaskell }; use secp256k1::SecretKey; use jubjub::Fr; use incrementalmerkletree::{ Hashable, Level, Position, frontier::{ CommitmentTree, Frontier, NonEmptyFrontier }, witness::IncrementalWitness }; use zip32::{ Scope as SaplingScope, ChildIndex }; use sapling_crypto::{ NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH, Node, MerklePath, PaymentAddress, Anchor as SaplingAnchor, value::ValueCommitment as SaplingValueCommitment, note::{ ExtractedNoteCommitment as SaplingNoteCommitment, Note as SaplingNote, Rseed }, keys::{ PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, ExpandedSpendingKey, FullViewingKey as SaplingFullViewingKey }, note_encryption::{ SaplingDomain, Zip212Enforcement }, bundle::{ GrothProofBytes, OutputDescription, SpendDescription, Authorized as SaplingAuthorized, Bundle as SaplingBundle }, value::NoteValue as SaplingNoteValue, circuit::{ SpendParameters, OutputParameters }, zip32::{ sapling_find_address, DiversifierKey } }; use bip0039::{Count, Mnemonic, English}; use zcash_primitives::{ merkle_tree::{ read_commitment_tree, write_commitment_tree, read_incremental_witness, write_incremental_witness, read_frontier_v1, read_nonempty_frontier_v1 }, legacy::{ Script, TransparentAddress }, transaction::{ Transaction, fees::zip317::FeeRule, builder::{ Builder, Error, BuildConfig }, components::{ amount::{ Amount, NonNegativeAmount }, transparent::{ Bundle as TransparentBundle, TxIn, TxOut, OutPoint, Authorized } } }, memo::MemoBytes, consensus::{ BranchId::Nu5, MainNetwork, TestNetwork, BlockHeight } }; use zcash_address::{ Network, unified::{Address, Encoding, Ufvk, Container, Fvk, Receiver}, ZcashAddress }; use zcash_client_backend::encoding::decode_payment_address; use zcash_client_backend::keys::sapling::{ spending_key, ExtendedFullViewingKey, ExtendedSpendingKey, DiversifiableFullViewingKey }; use zcash_primitives::zip32::DiversifierIndex; use orchard::{ Address as OrchardAddress, Bundle as OrchardBundle, bundle::{ Authorized as OrchardAuthorized, Flags }, Action, keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, note::{Rho, RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note_encryption::OrchardDomain, primitives::redpallas::{VerificationKey, SpendAuth, Signature}, tree::{ MerklePath as OrchardMerklePath, MerkleHashOrchard, Anchor as OrchardAnchor }, value::{ ValueCommitment, NoteValue } }; use bech32::{ Hrp, Bech32, Bech32m }; use wagyu_zcash_parameters::load_sapling_parameters; pub enum RW {} pub const RW: PhantomData = PhantomData; #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct RawData { hrp: Vec, bytes: Vec } impl ToHaskell for RawData { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } //impl FromHaskell for RawData { //fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { //let x = RawData::deserialize(buf)?; //Ok(x) //} //} #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HrawTx { bytes: Vec, s: bool, o: bool } impl ToHaskell for HrawTx { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HshieldedOutput { pub cv: Hhex, cmu: Hhex, eph_key: Hhex, enc_txt: Hhex, out_txt: Hhex, proof: Hhex } impl FromHaskell for HshieldedOutput { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = HshieldedOutput::deserialize(buf)?; Ok(x) } } impl ToHaskell for HshieldedOutput { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl HshieldedOutput { fn from_object(s: &OutputDescription) -> HshieldedOutput { 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()} } } pub fn pack(sp: &[OutputDescription]) -> Vec { let mut r = Vec::new(); for s in sp { r.push(HshieldedOutput::from_object(s)); } return r } pub fn to_output_description(&mut self) -> Result> { let cv = SaplingValueCommitment::from_bytes_not_small_order(&to_array(self.cv.bytes.clone())).unwrap(); let cmu = SaplingNoteCommitment::from_bytes(&to_array(self.cmu.bytes.clone())).unwrap(); let eph_key = zcash_note_encryption::EphemeralKeyBytes::from(to_array(self.eph_key.bytes.clone())); let x = OutputDescription::from_parts(cv, cmu, eph_key, to_array(self.enc_txt.bytes.clone()), to_array(self.out_txt.bytes.clone()), to_array(self.proof.bytes.clone())); return Ok(x) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hhex { bytes: Vec } impl ToHaskell for Hhex { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Haction { nf: Hhex, rk: Hhex, cmx: Hhex, eph_key: Hhex, enc_txt: Hhex, out_txt: Hhex, cv: Hhex, auth: Hhex } impl FromHaskell for Haction { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = Haction::deserialize(buf)?; Ok(x) } } impl Haction { pub fn pack(sp: &NonEmpty>>) -> Vec { 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(Debug, BorshSerialize, BorshDeserialize)] pub struct Hnote { note: u64, recipient: Vec, memo: Vec, nullifier: Vec, rho: Vec, rseed: Hrseed } impl ToHaskell for Hnote { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hrseed { kind: u8, bytes: Vec } impl FromHaskell for Hrseed { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = Hrseed::deserialize(buf)?; Ok(x) } } impl ToHaskell for Hrseed { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hua { net: u8, o_rec: Vec, s_rec: Vec, t_rec: Vec, to_rec: Vec } impl ToHaskell for Hua { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl Hua { fn add_rec(&mut self, rec: &Receiver) { if let Receiver::Orchard(x) = rec { self.o_rec = x.to_vec(); } if let Receiver::Sapling(y) = rec { self.s_rec = y.to_vec(); } if let Receiver::P2pkh(z) = rec { self.t_rec = z.to_vec(); } if let Receiver::P2sh(w) = rec { self.to_rec = w.to_vec(); } } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Htx { txid: Vec, locktime: u32, expiry: u32, t_bundle: HTBundle, s_bundle: HSBundle, o_bundle: HOBundle } impl ToHaskell for Htx { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTBundle { empty: bool, vin: Vec, vout: Vec, coinbase: bool } impl ToHaskell for HTBundle { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl HTBundle { pub fn from_bundle(b: &TransparentBundle) -> HTBundle { HTBundle {empty: false, vin: b.vin.iter().map(HTxIn::pack).collect() , vout: b.vout.iter().map(HTxOut::pack).collect(), coinbase: b.is_coinbase()} } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTxIn { outpoint: Houtpoint, script: Vec, sequence: u32 } impl ToHaskell for HTxIn { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl HTxIn { pub fn pack(t: &TxIn) -> HTxIn { return HTxIn { outpoint: Houtpoint::pack(&t.prevout), script: t.script_sig.0.clone(), sequence: t.sequence} } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTxOut { amt: i64, script: Vec } impl FromHaskell for HTxOut { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = HTxOut::deserialize(buf)?; Ok(x) } } impl ToHaskell for HTxOut { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl HTxOut { pub fn pack(t: &TxOut) -> HTxOut { return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() } } pub fn unpack(&self) -> TxOut { TxOut { value: NonNegativeAmount::from_nonnegative_i64(self.amt).unwrap(), script_pubkey: Script(self.script.clone())} } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Houtpoint { hash: Vec, index: u32 } impl FromHaskell for Houtpoint { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = Houtpoint::deserialize(buf)?; Ok(x) } } impl ToHaskell for Houtpoint { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl Houtpoint { pub fn pack(o: &OutPoint) -> Houtpoint { return Houtpoint {hash: o.hash().to_vec() , index: o.n() } } pub fn unpack(&self) -> OutPoint { OutPoint::new(to_array(self.hash.clone()), self.index) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HtransparentInput { sk: Vec, utxo: Houtpoint, coin: HTxOut } impl FromHaskell for HtransparentInput { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = HtransparentInput::deserialize(buf)?; Ok(x) } } impl ToHaskell for HtransparentInput { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HSBundle { empty: bool, spends: Vec, outputs: Vec , value: i64, sig: Vec } impl ToHaskell for HSBundle { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl HSBundle { pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { let sig = <[u8; 64]>::from(sb.authorization().binding_sig); return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: sig.to_vec()} } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hspend { cv: Hhex, anchor: Hhex, nullifier: Hhex, rk: Hhex, proof: Hhex, authsig: Hhex } impl ToHaskell for Hspend { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl Hspend { pub fn pack(sp: &[SpendDescription]) -> Vec { let mut r = Vec::new(); for s in sp { let rk = s.rk().clone(); let sig = s.spend_auth_sig().clone(); 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: <[u8; 32]>::from(rk).to_vec()}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes: <[u8; 64]>::from(sig).to_vec()}}); } return r } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HsaplingInput { sk: Vec, note: Hnote, iw: Vec } impl FromHaskell for HsaplingInput { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = HsaplingInput::deserialize(buf)?; Ok(x) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HorchardInput { sk: Vec, note: Hnote, iw: Vec } impl FromHaskell for HorchardInput { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = HorchardInput::deserialize(buf)?; Ok(x) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Houtput { kind: u8, ovk: Vec, to: Vec, amt: u64, memo: Vec, chg: bool } impl FromHaskell for Houtput { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = Houtput::deserialize(buf)?; Ok(x) } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HOBundle { empty: bool, actions: Vec, flags: Hflags, value: i64, anchor: Hhex, proof: Hhex, bindingsig: Hhex } impl ToHaskell for HOBundle { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl HOBundle { pub fn from_bundle(b: &OrchardBundle) -> 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(Debug, BorshSerialize, BorshDeserialize)] pub struct Hflags { spends: bool, outputs: bool } impl ToHaskell for Hflags { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl Hflags { pub fn pack(f: &Flags) -> Hflags { return Hflags {spends: f.spends_enabled(), outputs: f.outputs_enabled()} } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hufvk { net: u8, orchard: Vec, sapling: Vec, transparent: Vec } impl ToHaskell for Hufvk { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl Hufvk { fn add_key_section(&mut self, fvk: &Fvk) { if let Fvk::Orchard(v) = fvk { self.orchard = v.to_vec(); } if let Fvk::Sapling(w) = fvk { self.sapling = w.to_vec(); } if let Fvk::P2pkh(x) = fvk { self.transparent = x.to_vec(); } } } #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hsvk { vk: Vec, ovk: Vec } impl ToHaskell for Hsvk { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } #[derive(BorshSerialize, BorshDeserialize)] pub struct Hfrontier { position: u64, leaf: Hhex, ommers: Vec } impl ToHaskell for Hfrontier { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } impl FromHaskell for Hfrontier { fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { let x = Hfrontier::deserialize(buf)?; Ok(x) } } fn to_array(v: Vec) -> [T; N] { v.try_into().unwrap_or_else(|v: Vec| panic!("Expected a Vec of length {} but it was {}", N, v.len())) } #[no_mangle] pub extern "C" fn rust_wrapper_f4jumble( input: *const u8, input_len: usize, out: *mut u8, out_len: &mut usize) { let input: Vec = marshall_from_haskell_var(input, input_len, RW); let result = f4jumble::f4jumble(&input).unwrap(); marshall_to_haskell_var(&result, out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_f4unjumble( input: *const u8, input_len: usize, out: *mut u8, out_len: &mut usize) { let input: Vec = marshall_from_haskell_var(input, input_len, RW); let result = f4jumble::f4jumble_inv(&input).unwrap(); marshall_to_haskell_var(&result, out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_ua_decode( input: *const u8, input_len: usize, out: *mut u8, out_len: &mut usize) { let input: String = marshall_from_haskell_var(input, input_len, RW); let dec_addy = Address::decode(&input); match dec_addy { Ok((n, ua)) => { let x = match n { Network::Main => 1, Network::Test => 2, Network::Regtest => 3 }; let mut hk = Hua { net: x, o_rec: vec![0], s_rec: vec![0], t_rec: vec![0], to_rec: vec![0] }; let recvs = ua.items(); recvs.iter().for_each(|k| hk.add_rec(k)); marshall_to_haskell_var(&hk, out, out_len, RW); } Err(_e) => { let hk0 = Hua { net: 0, o_rec: vec![0], s_rec: vec![0], t_rec: vec![0], to_rec: vec![0]}; marshall_to_haskell_var(&hk0, out, out_len, RW); } } //marshall_to_haskell_var(&result, out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_shielded_decode( input: *const u8, input_len: usize) -> bool { let input: String = marshall_from_haskell_var(input, input_len, RW); ZcashAddress::try_from_encoded(&input).is_ok() } #[no_mangle] pub extern "C" fn rust_wrapper_bech32decode( input: *const u8, input_len: usize, out: *mut u8, out_len: &mut usize ) { let input: String = marshall_from_haskell_var(input, input_len, RW); let decoded_bytes = bech32::decode(&input); match decoded_bytes { Ok((hrp, bytes)) => { let rd = RawData {hrp: hrp.as_bytes().to_vec(), bytes}; marshall_to_haskell_var(&rd, out, out_len, RW); } Err(_e) => { let rd1 = RawData {hrp: "fail".into(), bytes: vec![0]}; marshall_to_haskell_var(&rd1, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_bech32m_encode( hr: *const u8, hr_len: usize, b: *const u8, b_len: usize, out: *mut u8, out_len: &mut usize ) { let hr: String = marshall_from_haskell_var(hr, hr_len, RW); let hrp = Hrp::parse(&hr).unwrap(); let b: Vec = marshall_from_haskell_var(b, b_len, RW); let string = bech32::encode::(hrp, &b).unwrap(); marshall_to_haskell_var(&string, out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_svk_decode( input: *const u8, input_len: usize ) -> bool { let input: Vec = marshall_from_haskell_var(input, input_len, RW); let svk = ExtendedFullViewingKey::read(&*input); match svk { Ok(_k) => { true } Err(e) => { print!("{:?}", e); false } } } #[no_mangle] pub extern "C" fn rust_wrapper_svk_check_address( key_input: *const u8, key_input_len: usize, address_input: *const u8, address_input_len: usize ) -> bool { let key_input: Vec = marshall_from_haskell_var(key_input, key_input_len, RW); let address_input: Vec = marshall_from_haskell_var(address_input, address_input_len, RW); let svk = ExtendedFullViewingKey::read(&*key_input); let sa = PaymentAddress::from_bytes(&to_array(address_input)).unwrap(); match svk { Ok(k) => { let (_div_index, def_address) = k.default_address(); sa == def_address } Err(_e) => { false } } } #[no_mangle] pub extern "C" fn rust_wrapper_ufvk_check_address( key_input: *const u8, key_input_len: usize, address_input: *const u8, address_input_len: usize ) -> bool { let key: String = marshall_from_haskell_var(key_input, key_input_len, RW); let addy: String = marshall_from_haskell_var(address_input, address_input_len, RW); let dec_key = Ufvk::decode(&key); let dec_addy = Address::decode(&addy); match dec_key { Ok((n, ufvk)) => { let i = ufvk.items(); if let Fvk::Orchard(k) = i[0] { let orch_key = FullViewingKey::from_bytes(&k).unwrap(); let orch_addy = orch_key.address_at(0u32, Scope::External).to_raw_address_bytes(); match dec_addy { Ok((n, recs)) => { let j = recs.items(); j[0] == Receiver::Orchard(orch_addy) }, Err(_e) => { false } } } else { false } }, Err(_e) => { false } } } #[no_mangle] pub extern "C" fn rust_wrapper_ufvk_decode( input: *const u8, input_len: usize, out: *mut u8, out_len: &mut usize ) { let input: String = marshall_from_haskell_var(input, input_len, RW); let dec_key = Ufvk::decode(&input); match dec_key { Ok((n, ufvk)) => { let x = match n { Network::Main => 1, Network::Test => 2, Network::Regtest => 3 }; let mut hk = Hufvk { net: x, orchard: vec![0], sapling: vec![0], transparent: vec![0] }; let fvks = ufvk.items(); fvks.iter().for_each(|k| hk.add_key_section(k)); marshall_to_haskell_var(&hk, out, out_len, RW); } Err(_e) => { let hk0 = Hufvk { net: 0, orchard: vec![0], sapling: vec![0], transparent: vec![0] }; marshall_to_haskell_var(&hk0, out, out_len, RW); } } } #[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, pos: u64, out: *mut u8, out_len: &mut usize ){ let sk: Vec = marshall_from_haskell_var(key, key_len, RW); let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW); let esk = ExtendedSpendingKey::from_bytes(&sk); let main_domain = SaplingDomain::new(Zip212Enforcement::On); //let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(2000000)); let scope = if external { SaplingScope::External } else { SaplingScope::Internal }; match esk { Ok(k) => { let action = note_input.to_output_description(); 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 = zcash_note_encryption::try_note_decryption(&main_domain, &pivk, &action2); match result { Some((n, r, m)) => { let nullifier = n.nf(&nk, pos); 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: nullifier.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); } } }, Err(_e) => { 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_v2( key: *const u8, key_len: usize, note: *const u8, note_len: usize, out: *mut u8, out_len: &mut usize ){ let evk: Vec = marshall_from_haskell_var(key, key_len, RW); let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW); let svk = ExtendedFullViewingKey::read(&*evk); match svk { Ok(k) => { let domain = SaplingDomain::new(Zip212Enforcement::On); let action2 = note_input.to_output_description(); match action2 { Ok(action3) => { let fvk = k.to_diversifiable_full_viewing_key().to_ivk(SaplingScope::External); 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: 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); } } } Err(_e) => { 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, key_len: usize, note: *const u8, note_len: usize, out: *mut u8, out_len: &mut usize ){ let fvk_input: Vec = marshall_from_haskell_var(key, key_len, RW); let note_input: Haction = marshall_from_haskell_var(note, note_len, RW); let action: Action> = 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 = fvk.to_ivk(Scope::External); 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: 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); } } }, 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_sk( key: *const u8, key_len: usize, note: *const u8, note_len: usize, external: bool, out: *mut u8, out_len: &mut usize ){ let sk_input: Vec = marshall_from_haskell_var(key, key_len, RW); let note_input: Haction = marshall_from_haskell_var(note, note_len, RW); let action: Action> = 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 sk_array = to_array(sk_input); let domain = OrchardDomain::for_action(&action); let dec_sk = SpendingKey::from_bytes(sk_array).unwrap(); let fvk = FullViewingKey::from(&dec_sk); 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); } } } #[no_mangle] pub extern "C" fn rust_wrapper_tx_read( tx: *const u8, tx_len: usize, out: *mut u8, out_len: &mut usize ){ let tx_input: Vec = marshall_from_haskell_var(tx, tx_len, RW); let mut tx_reader = Cursor::new(tx_input); let parsed_tx = Transaction::read(&mut tx_reader, Nu5); match parsed_tx { Ok(t) => { let tb = t.transparent_bundle(); let sb = t.sapling_bundle(); let ob = t.orchard_bundle(); let h1 = Htx { txid: t.txid().as_ref().to_vec() , locktime: t.lock_time() , expiry: u32::from(t.expiry_height()) , t_bundle: match tb { 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) => { 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); } } } #[no_mangle] pub extern "C" fn rust_wrapper_tx_parse( tx: *const u8, tx_len: usize, out: *mut u8, out_len: &mut usize ){ let tx_input: Vec = marshall_from_haskell_var(tx, tx_len, RW); let mut tx_reader = Cursor::new(tx_input); let parsed_tx = Transaction::read(&mut tx_reader, Nu5); match parsed_tx { Ok(t) => { let s_bundle = t.sapling_bundle(); match s_bundle { Some(b) => { let s_output = HshieldedOutput::pack(b.shielded_outputs()); marshall_to_haskell_var(&s_output, out, out_len, RW); }, None => { let mut z = Vec::new(); z.push(vec![0]); marshall_to_haskell_var(&z, out, out_len, RW); } } }, Err(_e) => { let mut y = Vec::new(); y.push(vec![0]); marshall_to_haskell_var(&y, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_gen_seed_phrase( out: *mut u8, out_len: &mut usize ){ let mnemonic: Mnemonic = Mnemonic::generate(Count::Words24); let seed = mnemonic.phrase().as_bytes().to_vec(); marshall_to_haskell_var(&seed, out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_recover_seed( input: *const u8, input_len: usize, out: *mut u8, out_len: &mut usize ){ let phrase: String = marshall_from_haskell_var(input, input_len, RW); let mnemonic = >::from_phrase(phrase); match mnemonic { Ok(m) => { let s = m.to_seed("").to_vec(); marshall_to_haskell_var(&s, out, out_len, RW); }, Err(_e) => { marshall_to_haskell_var(&vec![0], out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_sapling_spendingkey( seed: *const u8, seed_len: usize, coin_type: u32, acc_id: u32, out: *mut u8, out_len: &mut usize ){ let s: Vec = marshall_from_haskell_var(seed, seed_len, RW); let sk = spending_key(&s, coin_type, zcash_primitives::zip32::AccountId::try_from(acc_id).unwrap()); marshall_to_haskell_var(&sk.to_bytes().to_vec(), out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_sapling_paymentaddress( extspk: *const u8, extspk_len: usize, div_ix: u32, out: *mut u8, out_len: &mut usize ){ let extspk: Vec = 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(); 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] pub extern "C" fn rust_wrapper_sapling_chgpaymentaddress( extspk: *const u8, extspk_len: usize, out: *mut u8, out_len: &mut usize ){ let vexspk: Vec = marshall_from_haskell_var(extspk, extspk_len, RW); let vexspkp = &vexspk; let extspku8 : &[u8] = &vexspkp; let extspk = match ExtendedSpendingKey::from_bytes(&extspku8) { Ok( k ) => k, Err( _e ) => { // error recovering ExtendedSpendingKey marshall_to_haskell_var(&vec![0], out, out_len, RW); return } }; let dfvk = extspk.to_diversifiable_full_viewing_key(); let ( _divIx, cPmtAddress ) = dfvk.change_address(); marshall_to_haskell_var(&cPmtAddress.to_bytes().to_vec(), out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_derive_orchard_spending_key( seed: *const u8, seed_len: usize, coin_type: u32, acc_id: u32, out: *mut u8, out_len: &mut usize ){ let s: Vec = marshall_from_haskell_var(seed, seed_len, RW); let sk = SpendingKey::from_zip32_seed(&s, coin_type, zip32::AccountId::try_from(acc_id).unwrap()); match sk { Ok(key) => { marshall_to_haskell_var(&key.to_bytes().to_vec(), out, out_len, RW); }, Err(_e) => { marshall_to_haskell_var(&vec![0], out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_derive_orchard_receiver( spend_key: *const u8, spend_key_len: usize, add_id: u32, scope: bool, out: *mut u8, out_len: &mut usize ){ let sk_in: Vec = marshall_from_haskell_var(spend_key, spend_key_len, RW); let sk = SpendingKey::from_bytes(sk_in[0..32].try_into().unwrap()).unwrap(); let fvk = FullViewingKey::from(&sk); let sc = if scope { Scope::External } else {Scope::Internal}; let o_rec = fvk.address_at(add_id, sc); marshall_to_haskell_var(&o_rec.to_raw_address_bytes().to_vec(), out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_bech32_encode( hr: *const u8, hr_len: usize, b: *const u8, b_len: usize, out: *mut u8, out_len: &mut usize ) { let hr: String = marshall_from_haskell_var(hr, hr_len, RW); let hrp = Hrp::parse(&hr).unwrap(); let b: Vec = marshall_from_haskell_var(b, b_len, RW); let string = bech32::encode::(hrp, &b).unwrap(); marshall_to_haskell_var(&string, out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_read_sapling_frontier( tree: *const u8, tree_len: usize, out: *mut u8, out_len: &mut usize ){ let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW); let tree_reader = Cursor::new(tree_in); let comm_tree: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); //let comm_tree: Frontier = read_frontier_v1(tree_reader).unwrap(); let frontier: Frontier = comm_tree.to_frontier(); match frontier.value() { Some(f1) => { let (pos, leaf, omm) = f1.clone().into_parts(); let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| Hhex { bytes: x.to_bytes().to_vec()}).collect()}; marshall_to_haskell_var(&f, out, out_len, RW); }, None => { let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_read_sapling_commitment_tree( tree: *const u8, tree_len: usize, node: *const u8, node_len: usize, out: *mut u8, out_len: &mut usize ){ let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); let leaf = Node::from_bytes(to_array(tree_in.leaf.bytes)).unwrap(); let mut comm_tree = NonEmptyFrontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| Node::from_bytes(to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let node_in: Vec = marshall_from_haskell_var(node, node_len, RW); let sap_note_comm = SaplingNoteCommitment::from_bytes(&to_array(node_in)); if sap_note_comm.is_some().into() { let n = Node::from_cmu(&sap_note_comm.unwrap()); comm_tree.append(n); let (pos, leaf, omm) = comm_tree.into_parts(); let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| Hhex { bytes: x.to_bytes().to_vec()}).collect()}; marshall_to_haskell_var(&f, out, out_len, RW); } else { let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } #[no_mangle] pub extern "C" fn rust_wrapper_read_sapling_witness( tree: *const u8, tree_len: usize, out: *mut u8, out_len: &mut usize ){ let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); let leaf = Node::from_bytes(to_array(tree_in.leaf.bytes)).unwrap(); let frontier: Frontier = Frontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| Node::from_bytes(to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let ct: CommitmentTree = CommitmentTree::from_frontier(&frontier); let inc_wit = IncrementalWitness::from_tree(ct); let mut out_bytes: Vec = Vec::new(); let result = write_incremental_witness(&inc_wit, &mut out_bytes); match result { Ok(()) => { let h = Hhex { bytes: out_bytes}; marshall_to_haskell_var(&h, out, out_len, RW); }, Err(_e) => { let h0 = Hhex { bytes: vec![0]}; marshall_to_haskell_var(&h0, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_read_sapling_position( wit: *const u8, wit_len: usize, ) -> u64 { let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); let wit_reader = Cursor::new(wit_in); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let pos = iw.witnessed_position(); return u64::from(pos); } #[no_mangle] pub extern "C" fn rust_wrapper_update_sapling_witness( wit: *const u8, wit_len: usize, cmus: *const u8, cmus_len: usize, out: *mut u8, out_len: &mut usize ) { let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); let wit_reader = Cursor::new(wit_in); let mut iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let cmu: Vec> = marshall_from_haskell_var(cmus, cmus_len, RW); for c in cmu { let sap_note_comm = SaplingNoteCommitment::from_bytes(&to_array(c)); if sap_note_comm.is_some().into() { let n = Node::from_cmu(&sap_note_comm.unwrap()); iw.append(n); } } let mut out_bytes: Vec = Vec::new(); let result = write_incremental_witness(&iw, &mut out_bytes); match result { Ok(()) => { let h = Hhex { bytes: out_bytes}; marshall_to_haskell_var(&h, out, out_len, RW); }, Err(_e) => { let h0 = Hhex { bytes: vec![0]}; marshall_to_haskell_var(&h0, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_frontier( tree: *const u8, tree_len: usize, out: *mut u8, out_len: &mut usize ){ let tree_in: Vec = marshall_from_haskell_var(tree, tree_len, RW); let tree_reader = Cursor::new(tree_in); let comm_tree = read_commitment_tree(tree_reader); //let comm_tree = read_frontier_v1(tree_reader); //let frontier: Frontier = comm_tree.to_frontier(); match comm_tree { Ok::, _>(f1) => { let frontier = f1.to_frontier(); match frontier.value() { Some(f2) => { let (pos, leaf, omm) = f2.clone().into_parts(); let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| Hhex { bytes: x.to_bytes().to_vec()}).collect()}; marshall_to_haskell_var(&f, out, out_len, RW); }, None => { let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } }, Err(_e) => { let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_tree_anchor( tree: *const u8, tree_len: usize, out: *mut u8, out_len: &mut usize ){ let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); let leaf = MerkleHashOrchard::from_bytes(&to_array(tree_in.leaf.bytes)).unwrap(); let comm_tree: NonEmptyFrontier = NonEmptyFrontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| MerkleHashOrchard::from_bytes(&to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let root = comm_tree.root(None); let h = Hhex { bytes: root.to_bytes().to_vec() }; marshall_to_haskell_var(&h, out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_witness_anchor( wit: *const u8, wit_len: usize, out: *mut u8, out_len: &mut usize ) { let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); let wit_reader = Cursor::new(wit_in); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let root = iw.root(); let h = Hhex { bytes: root.to_bytes().to_vec() }; marshall_to_haskell_var(&h, out, out_len, RW); } #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_node( cmx: *const u8, cmx_len: usize, out: *mut u8, out_len: &mut usize ){ let node_in: Vec = marshall_from_haskell_var(cmx, cmx_len, RW); let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(node_in)); if orchard_note_comm.is_some().into() { let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); let h = Hhex { bytes: n.to_bytes().to_vec()}; marshall_to_haskell_var(&h, out, out_len, RW); } else { let h0 = Hhex { bytes: vec![0] }; marshall_to_haskell_var(&h0, out, out_len, RW); } } #[no_mangle] pub extern "C" fn rust_wrapper_combine_orchard_nodes( level: u8, left: *const u8, left_len: usize, right: *const u8, right_len: usize, out: *mut u8, out_len: &mut usize ){ let left_in: Vec = marshall_from_haskell_var(left, left_len, RW); let right_in: Vec = marshall_from_haskell_var(right, right_len, RW); if left_in.len() == 1 { let n = MerkleHashOrchard::combine(Level::new(level), &MerkleHashOrchard::empty_leaf(), &MerkleHashOrchard::empty_leaf()); let h = Hhex { bytes: n.to_bytes().to_vec() }; marshall_to_haskell_var(&h, out, out_len, RW); } else { let left_node = MerkleHashOrchard::from_bytes(&to_array(left_in)); if left_node.is_some().into() { if right_in.len() > 1 { let right_node = MerkleHashOrchard::from_bytes(&to_array(right_in)); if right_node.is_some().into() { let n = MerkleHashOrchard::combine(Level::new(level), &left_node.unwrap(), &right_node.unwrap()); let h = Hhex { bytes: n.to_bytes().to_vec() }; marshall_to_haskell_var(&h, out, out_len, RW); } else { let h0 = Hhex { bytes: vec![0] }; marshall_to_haskell_var(&h0, out, out_len, RW); } } else { let n = MerkleHashOrchard::combine(Level::new(level), &left_node.unwrap(), &MerkleHashOrchard::empty_leaf()); let h = Hhex { bytes: n.to_bytes().to_vec() }; marshall_to_haskell_var(&h, out, out_len, RW); } } else { let h0 = Hhex { bytes: vec![0] }; marshall_to_haskell_var(&h0, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_commitment_tree( tree: *const u8, tree_len: usize, node: *const u8, node_len: usize, out: *mut u8, out_len: &mut usize ){ let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); //let tree_reader = Cursor::new(tree_in); //let mut comm_tree: CommitmentTree = read_commitment_tree(tree_reader).unwrap(); //let mut comm_tree: Frontier = read_frontier_v1(tree_reader).unwrap(); let leaf = MerkleHashOrchard::from_bytes(&to_array(tree_in.leaf.bytes)).unwrap(); let mut comm_tree: NonEmptyFrontier = NonEmptyFrontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| MerkleHashOrchard::from_bytes(&to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let node_in: Vec = marshall_from_haskell_var(node, node_len, RW); let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(node_in)); if orchard_note_comm.is_some().into() { let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); comm_tree.append(n); let (pos, leaf, omm) = comm_tree.into_parts(); let f = Hfrontier { position: ::from(pos), leaf: Hhex { bytes: leaf.to_bytes().to_vec()}, ommers: omm.iter().map(|&x| Hhex { bytes: x.to_bytes().to_vec()}).collect()}; marshall_to_haskell_var(&f, out, out_len, RW); } else { let f0 = Hfrontier { position: 0, leaf: Hhex { bytes: vec![0]}, ommers: vec![Hhex { bytes: vec![0]}]}; marshall_to_haskell_var(&f0, out, out_len, RW); } } #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_witness( tree: *const u8, tree_len: usize, out: *mut u8, out_len: &mut usize ){ let tree_in: Hfrontier = marshall_from_haskell_var(tree, tree_len, RW); let leaf = MerkleHashOrchard::from_bytes(&to_array(tree_in.leaf.bytes)).unwrap(); let frontier: Frontier = Frontier::from_parts(Position::from(tree_in.position), leaf, tree_in.ommers.iter().map(|x| MerkleHashOrchard::from_bytes(&to_array(x.bytes.clone())).unwrap() ).collect()).unwrap(); let ct: CommitmentTree = CommitmentTree::from_frontier(&frontier); let inc_wit = IncrementalWitness::from_tree(ct); let mut out_bytes: Vec = Vec::new(); let result = write_incremental_witness(&inc_wit, &mut out_bytes); match result { Ok(()) => { let h = Hhex { bytes: out_bytes}; marshall_to_haskell_var(&h, out, out_len, RW); }, Err(_e) => { let h0 = Hhex { bytes: vec![0]}; marshall_to_haskell_var(&h0, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_position( wit: *const u8, wit_len: usize, ) -> u64 { let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); let wit_reader = Cursor::new(wit_in); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let path = iw.path(); match path { Some(p) => { let pos = p.position(); return u64::from(pos); }, None => { return 0; } } } #[no_mangle] pub extern "C" fn rust_wrapper_update_orchard_witness( wit: *const u8, wit_len: usize, cm: *const u8, cm_len: usize, out: *mut u8, out_len: &mut usize ) { let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); let wit_reader = Cursor::new(wit_in); let mut iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let cmu: Vec> = marshall_from_haskell_var(cm, cm_len, RW); for c in cmu { let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(c)); if orchard_note_comm.is_some().into() { let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); iw.append(n); } } let mut out_bytes: Vec = Vec::new(); let result = write_incremental_witness(&iw, &mut out_bytes); match result { Ok(()) => { let h = Hhex { bytes: out_bytes}; marshall_to_haskell_var(&h, out, out_len, RW); }, Err(_e) => { let h0 = Hhex { bytes: vec![0]}; marshall_to_haskell_var(&h0, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_decode_sapling_address( sapling: *const u8, sapling_len: usize, out: *mut u8, out_len: &mut usize){ let sapling_address_from_haskell : Vec = marshall_from_haskell_var(sapling, sapling_len, RW); let sapling_address = std::str::from_utf8(&sapling_address_from_haskell).unwrap(); let mut netid = 0; match sapling_address.find("1") { Some(ix) => { let netstr = &sapling_address[0..ix]; if netstr == "zs" { netid = 1 } else { if netstr == "ztestsapling" { netid = 2 } } match decode_payment_address(netstr, sapling_address) { Ok( t )=> { let address_to_bytes = t.to_bytes(); let mut out_bytes_temp : [u8;44] = [0;44]; out_bytes_temp[0] = netid; let mut iy = 1; for ix in 0..43 { out_bytes_temp[iy] = address_to_bytes[ix]; iy += 1; } let out_bytes: Vec = out_bytes_temp.to_vec(); marshall_to_haskell_var(&out_bytes, out, out_len, RW); } Err(_e) => { let h = vec![0]; marshall_to_haskell_var(&h, out, out_len, RW); } } } None => { let h = vec![0]; marshall_to_haskell_var(&h, out, out_len, RW); } } } #[no_mangle] pub extern "C" fn rust_wrapper_create_transaction( sap_wit: *const u8, sap_wit_len: usize, orch_wit: *const u8, orch_wit_len: usize, t_input: *const u8, t_input_len: usize, s_input: *const u8, s_input_len: usize, o_input: *const u8, o_input_len: usize, out_list: *const u8, out_list_len: usize, net: bool, bl_height: u32, build: bool, out: *mut u8, out_len: &mut usize){ //let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); //let sap_wit_reader = Cursor::new(sap_wit_in); //let sap_iw = read_commitment_tree::>, SAPLING_DEPTH>(sap_wit_reader); let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); let sap_anchor = if sap_input.is_empty() { None } else { let si = &sap_input[0]; let swit_reader = Cursor::new(&si.iw); let iw: IncrementalWitness = read_incremental_witness(swit_reader).unwrap(); Some(SaplingAnchor::from(iw.root())) }; //let sap_anchor = match sap_iw { //Ok(s_iw) => { //Some(SaplingAnchor::from(s_iw.root())) //}, //Err(_e) => { //None //} //}; //println!("{:?}", sap_anchor); //let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); //let orch_wit_reader = Cursor::new(orch_wit_in); //let orch_iw = read_commitment_tree::>, 32>(orch_wit_reader); let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); //let orch_anchor = match orch_iw { //Ok(o_iw) => { //Some(OrchardAnchor::from(o_iw.root())) //}, //Err(_e) => { //None //} //}; let orch_anchor = if orch_input.is_empty() { None } else { let oi = &orch_input[0]; let wit_reader = Cursor::new(&oi.iw); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); Some(OrchardAnchor::from(iw.root())) }; let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); for t_in in trans_input { if t_in.sk.len() > 1 { println!("t inp: {:?}", t_in); let k = SecretKey::from_slice(&t_in.sk).unwrap(); if net { match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { //println!("added t-input in main"); continue; }, Err(_e) => { println!("Error reading transparent input"); } } } else { match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { //println!("added t-input in test"); continue; }, Err(_e) => { println!("Error reading transparent input"); } } } } } for s_in in sap_input { if s_in.sk.len() > 1 { println!("s inp: {:?}", s_in); let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); match sp_key { Ok(sk) => { let pay_addr = PaymentAddress::from_bytes(&to_array(s_in.note.recipient)).unwrap(); let rseed = if s_in.note.rseed.kind == 1 { Rseed::BeforeZip212(Fr::from_bytes(&to_array(s_in.note.rseed.bytes)).unwrap()) } else { Rseed::AfterZip212(to_array(s_in.note.rseed.bytes)) }; let note = SaplingNote::from_parts(pay_addr, SaplingNoteValue::from_raw(s_in.note.note), rseed); let wit_reader = Cursor::new(s_in.iw); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let merkle_path = iw.path().unwrap(); if net { let mb = main_builder.add_sapling_spend::(&sk, note, merkle_path); match mb { Ok(()) => { continue; }, Err(_e) => { let x = Hhex {bytes: vec![5]}; marshall_to_haskell_var(&x, out, out_len, RW); } } } else { let tb = test_builder.add_sapling_spend::(&sk, note, merkle_path); match tb { Ok(()) => { continue; }, Err(_e) => { let x = Hhex {bytes: vec![5]}; marshall_to_haskell_var(&x, out, out_len, RW); } } } }, Err(_e) => { let x = Hhex {bytes: vec![5]}; marshall_to_haskell_var(&x, out, out_len, RW); } } } } for o_in in orch_input { if o_in.sk.len() > 1 { println!("o inp: {:?}", o_in); let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); let pay_addr = OrchardAddress::from_raw_address_bytes(&to_array(o_in.note.recipient)).unwrap(); let rho = Rho::from_bytes(&to_array(o_in.note.rho)).unwrap(); let rseed = RandomSeed::from_bytes(to_array(o_in.note.rseed.bytes), &rho).unwrap(); let val = NoteValue::from_raw(o_in.note.note); let note = Note::from_parts(pay_addr, val, rho, rseed).unwrap(); let wit_reader = Cursor::new(o_in.iw); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); let merkle_path = OrchardMerklePath::from(iw.path().unwrap()); if net { let mb = main_builder.add_orchard_spend::(&sp_key, note, merkle_path); match mb { Ok(()) => { continue; }, Err(_e) => { let x = Hhex {bytes: vec![7]}; marshall_to_haskell_var(&x, out, out_len, RW); } } } else { let tb = test_builder.add_orchard_spend::(&sp_key, note, merkle_path); match tb { Ok(()) => { continue; }, Err(_e) => { let x = Hhex {bytes: vec![7]}; marshall_to_haskell_var(&x, out, out_len, RW); } } } } } let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); for output in outputs { match output.kind { 1 => { let recipient = TransparentAddress::PublicKeyHash(to_array(output.to)); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); println!("t out: {:?} {:?}", val, output.chg); if net { let _mb = main_builder.add_transparent_output(&recipient, val); } else { let _tb = test_builder.add_transparent_output(&recipient, val); } }, 2 => { let recipient = TransparentAddress::ScriptHash(to_array(output.to)); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); println!("t out: {:?} {:?}", val, output.chg); if net { let _mb = main_builder.add_transparent_output(&recipient, val); } else { let _tb = test_builder.add_transparent_output(&recipient, val); } }, 3 => { let ovk = Some(ExpandedSpendingKey::from_spending_key(&output.ovk).ovk); let recipient = PaymentAddress::from_bytes(&to_array(output.to)).unwrap(); let val = NonNegativeAmount::from_u64(output.amt).unwrap(); let memo = MemoBytes::from_bytes(&output.memo).unwrap(); println!("s out: {:?} {:?}", val, output.chg); if net { let _mb = main_builder.add_sapling_output::(ovk, recipient, val, memo); } else { let _tb = test_builder.add_sapling_output::(ovk, recipient, val, memo); } }, 4 => { let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); let ovk = if output.chg { Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) } else { Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) }; let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); let val = output.amt; println!("o out: {:?} {:?}", val, output.chg); let memo = MemoBytes::from_bytes(&output.memo).unwrap(); if net { let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); } else { let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); } }, _ => { continue; } } } if build { let fee_result = if net { main_builder.get_fee(&FeeRule::standard()) } else { test_builder.get_fee(&FeeRule::standard()) }; println!("fee: {:?}", fee_result); let (spend_params_in, output_params_in) = load_sapling_parameters(); //let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); let spend_params_reader = Cursor::new(spend_params_in); let spend_prover = SpendParameters::read(spend_params_reader, false).unwrap(); //let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); let output_params_reader = Cursor::new(output_params_in); let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); let result = if net { main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) } else { test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) }; match result { Ok(r) => { let mut out_bytes: Vec = Vec::new(); let _t = r.transaction().write_v5(&mut out_bytes); let h = Hhex {bytes: out_bytes}; marshall_to_haskell_var(&h, out, out_len, RW); }, Err(e) => { match e { Error::InsufficientFunds(y) => { let x = Hhex {bytes: vec![0]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::Fee(_y2) => { let x = Hhex {bytes: vec![2]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::Balance(y3) => { let x = Hhex {bytes: vec![3]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::TransparentBuild(y4) => { let x = Hhex {bytes: vec![4]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::SaplingBuild(y5) => { let x = Hhex {bytes: vec![5]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::OrchardBuild(y7) => { let x = Hhex {bytes: vec![6]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::OrchardSpend(y8) => { let x = Hhex {bytes: vec![7]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::OrchardRecipient(y9) => { let x = Hhex {bytes: vec![8]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::SaplingBuilderNotAvailable => { let x = Hhex {bytes: vec![9]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::OrchardBuilderNotAvailable => { let x = Hhex {bytes: vec![10]}; marshall_to_haskell_var(&x, out, out_len, RW); } } } } } else { let result = if net { main_builder.get_fee(&FeeRule::standard()) } else { test_builder.get_fee(&FeeRule::standard()) }; match result { Ok(r) => { let x = Hhex {bytes: r.to_i64_le_bytes().to_vec()}; marshall_to_haskell_var(&x, out, out_len, RW); }, Err(e) => { let x = Hhex {bytes: vec![2]}; marshall_to_haskell_var(&x, out, out_len, RW); } } } }