use std::{ marker::PhantomData, io::Write, fmt::{Debug, Display, Formatter} }; 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 zcash_address::{ Network, unified::{Address, Encoding, Ufvk, Container, Fvk}, ZcashAddress }; use orchard::{ Action, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope}, note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note_encryption::OrchardDomain, primitives::redpallas::{VerificationKey, SpendAuth, Signature}, value::ValueCommitment }; use zcash_note_encryption; use bech32::{ decode, u5 }; 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 Haction { nf: Vec, rk: Vec, cmx: Vec, eph_key: Vec, enc_txt: Vec, out_txt: Vec, cv: Vec, auth: Vec } 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 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(); } } } 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,) -> bool { let input: String = marshall_from_haskell_var(input, input_len, RW); Address::decode(&input).is_ok() //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 (hrp, bytes) = bech32::decode_without_checksum(&input).unwrap(); let rd = RawData {hrp: hrp.into(), bytes: bytes.iter().map(|&x| bech32::u5::to_u8(x)).collect()}; marshall_to_haskell_var(&rd, out, out_len, RW); } #[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_ua_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)).unwrap(), VerificationKey::try_from(to_array(note_input.rk)).unwrap(), ExtractedNoteCommitment::from_bytes(&to_array(note_input.cmx)).unwrap(), TransmittedNoteCiphertext {epk_bytes: to_array(note_input.eph_key), enc_ciphertext: to_array(note_input.enc_txt), out_ciphertext: to_array(note_input.out_txt)}, ValueCommitment::from_bytes(&to_array(note_input.cv)).unwrap(), Signature::from(to_array(note_input.auth))); 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); } } }