My librustzcash "Hello World" project

Anyone willing to give some hints on adding stuff to librustzcash ? I’m finding the Rust part really hard & could use some help.

So far I have :-

  • built librustzcash
  • got FFI working from Perl
  • built a Perl-librustzcash thing that gets the zaddr from a full viewkey (yaay!! Hello World!)
  • I can also get ‘ak’ & ‘nk’ from the viewkey, other simple stuff

The part I’m stuck on is adding a function to rustzcash.rs to call try_sapling_note_decryption() (in librustzcash/zcash_primatives/src/note_encryption.rs) & accessing its return values.

Very basic Rust stuff that has me horribly confused - functions with Types, functions with Some/None :frowning:

Here’s what I have so far :-

// ChileBob’s failing attempt to be clever
//
#[no_mangle]
pub extern “C” fn librustzcash_decrypt_note (
height: u32,
ivk: *const [c_uchar; 32],
epk: *const [c_uchar; 32],
cmu: *const [c_uchar; 32],
enc_ciphertext: *const [c_uchar; 580],
ret_addr: *mut [c_uchar; 43],
ret_value: *mut [c_uchar; 8],
ret_memo: *mut [c_uchar; 512],
) → u32 {

// NOTE: Added to ‘use’ section at top to import function from zcash_primatives::note_encryption
// note_encryption::{sapling_ka_agree, try_sapling_note_decryption},

// try_sapling_note_decryption(
// height,
// &jubjub::Scalar::from_bytes(&*ivk).unwrap(),
// &jubjub::SubgroupPoint::from_bytes(&*epk).unwrap(),
// &bls12_381::Scalar::from_bytes(&*cmu).unwrap(),
// &*enc_ciphertext
// );
//
// Pain points :-
//
// - function has a Type, where does that come from?
// <P: consensus::Parameters>
// not a function arg (compiler objects to 6 args, expecting 5)
//
// - values returned as a tuple within Option - ‘Some’, how to access?
// try_sapling_note_decrypt() : Option<(Note, PaymentAddress, MemoBytes)>

// if the decrypt works, update FFI values for :-
// - ret_addr (43-byte array, sapling address)
// - ret_value (8-bytes, value in zatoshis, convert from u64?)
// - ret_memo (512-bytes, memo contents, null padded)

0       // set/clear return value, 1 = success, 0 = fail 

}

Some/none is a result type, functions can return other types (everything is under some type header) or nothing but you have to define what type goes in and comes out.
You can do it! :crab:

I really hate that damned crab…

blender

1 Like

Im a total newb and it seems like you already have a better grasp on functions than I do. Its just tricky because its radically safer. The devs on the zfnd discord have a lil rust thread and they might be able to get you squared away, I’ll find the link

2 Likes

Same here, total newb - your pointer to function Types helps, thanks!

I asked a couple of short (and probably very dumb) questions on discord #public-community-dev & got answers, but I really don’t like to bother the devs with this trivial stuff. They have more important things to do than teach me Rust

Edit: Only two compiler errors left - thank-you nuttycom!!

Edit: Now reached that wonderful point where the compiler doesn’t complain but the code still dies - I’m also calling this progress!!!

Edit: Still stuck getting the ‘cmu’ & ‘epk’ from an array of bytes…

So this is where I am now, Rust has stopped complaining and if I reverse the byte-order for ‘cmu’ & ‘epk’ given by ‘zcash-cli decoderawtransaction’ the code doesn’t die !

All this is great but it fails to decode test data (one of these outputs should be decryptable) :-

1 Like

Typically the unwrap call is kind of discouraged because of the else → panic trait unless you’re sure it would be there but sometimes the method doesn’t exist particulary for pointers because its just an address in memory and if you printed it with the debug flag it would be like 0x561764a2d000 or something.
(Sry im a lil distracted, house is a disaster gotta clean tomorrow and thinking about a circuit for a little project for morse code which I may be overthinking its gonna be cool


House is still a disaster )

Still trying to figure this out…pretty sure I’m force feeding junk to my jubjub.

dilbert

(Edit: Moved to #General 'cos I’m really stuck)

3 Likes

What are you trying to do?

Using librustzcash to decode shielded outputs with a viewkey - ‘payment detector’

I’m getting the output data from ‘zcash-cli decoderawtransaction’, using librustzcash to get the ivk from the fvk & trying that key on the output to see if it belongs to the key with librustzcash try_sapling note _decrypt().

You wanna get the plain note out of it?

Yes indeed !! That’s the plan

Callable from Perl?

that part already works already - I have perl code that uses librustzcash via FFI::Platypus

What function signature do you want?

Here’s the function I’ve been trying to write : librustzcash_decrypt_note()

OK, I’ll try to help you tomorrow.

1 Like

Have you tried a Box taking the pointer? Ive not played with box types much but I think thays someting they do.
(Also I think that match should return an expression maybe)

Reading pointers is not the problem as rust is getting the bytes I send by FFI, its more like the bytes from zcashd are not what rust expects - maybe a different encoding? Dunno, I have limited understanding of what happens at this level.

The Rust FFI, although well designed, is fraught with unexpected issues. You also have to worry about ownership, because passing stuff across the FFI boundary means the rust compiler can’t understand it, so ownership and lifetimes become very complex and hard to debug.

I’ve found that the easiest way to pass things around is strings. You take whatever data you want to send, do a base-64 encode into a string, and then pass the string into/from rust. Then, on the other side, you decode the base64 string into bytes. This way, only strings pass the FFI boundary, and that makes everything much easier.

See how this is implemented in Zecwallet here, where strings are passed/returned across the Javascript <-> Rust boundary via FFI.

1 Like