// Copyright 2022-2024 Vergara Technologies LLC // // This file is part of Zcash-Haskell. // use std::{ marker::PhantomData, io::{ Write, Cursor }, }; 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 zip32; use zcash_primitives::{ zip32::Scope as SaplingScope, zip339::{Count, Mnemonic}, transaction::components::sapling::{ GrothProofBytes, OutputDescription, }, sapling::{ PaymentAddress, keys::PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, note_encryption::SaplingDomain }, transaction::Transaction, consensus::{ BranchId::Nu5, MainNetwork, BlockHeight } }; use zcash_address::{ Network, unified::{Address, Encoding, Ufvk, Container, Fvk, Receiver}, ZcashAddress }; use zcash_client_backend::keys::sapling::ExtendedFullViewingKey; use orchard::{ Action, keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note_encryption::OrchardDomain, primitives::redpallas::{VerificationKey, SpendAuth, Signature}, value::ValueCommitment }; use bech32::{ Hrp, Bech32m }; pub enum RW {} pub const RW: PhantomData = PhantomData; #[derive(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(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(BorshSerialize, BorshDeserialize)] pub struct HshieldedOutput { cv: Vec, cmu: Vec, eph_key: Vec, enc_txt: Vec, out_txt: Vec, proof: Vec } 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) -> Result { 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() }; Ok(o) } } #[derive(BorshSerialize, BorshDeserialize)] pub struct Hhex { bytes: Vec } #[derive(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) } } #[derive(BorshSerialize, BorshDeserialize)] pub struct Hnote { note: u64, recipient: Vec, memo: Vec } impl ToHaskell for Hnote { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; Ok(()) } } #[derive(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(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(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(()) } } 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_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_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_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 note_input: Vec = marshall_from_haskell_var(note,note_len,RW); let mut note_reader = Cursor::new(note_input); let svk = ExtendedFullViewingKey::read(&*evk); match svk { Ok(k) => { let domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000)); let action2 = OutputDescription::read(&mut note_reader); 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 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] 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_nullifier(*action.nullifier()); 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 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); } None => { let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] }; marshall_to_haskell_var(&hn0, 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); } } } #[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 tx_bytes: Vec = tx_input.clone(); let mut tx_reader = Cursor::new(tx_input); let s_o = false; let o_a = false; 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 mut s_output = Vec::new(); for s_each_out in b.shielded_outputs().iter() { let mut out_bytes = Vec::new(); let _ = s_each_out.write_v4(&mut out_bytes); s_output.push(out_bytes); } 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::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 = 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_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); } } }