Minimal rust syncing wallet app?

I’m trying to build a new wallet app, and would very much like to reuse the rust crates out there that implement the protocol, syncing, etc.
I tried (unsuccessfully) to use warp sync from warp_api_ffi - Rust (hhanh00.github.io), which has a doc that makes writing a minimal demo sync look super easy. Unfortunately, that’s not a real crate on crates.io, and I’ve drained everyone’s knowledge (or willingness to share) at trying to consume this crate from my own simple console app.

So I’m at least temporarily abandoning hope of consuming warp sync, and instead would be content to at least get any sync working. But the zcash-related rust crates are many, often overlapping in concerns, and don’t work with each other at their tip versions. I’m hoping there’s an easy crate to consume for wallets that I just haven’t found.

BTW, my app won’t ultimately be written in rust. Rather, once I get the rust console app working, I’ll expose the necessary APIs from a dynamic link library that I’ll compile and p/invoke from a C# application.

Any suggestions?

2 Likes

It’s super unfortunate that this is still so hard!

I’m not an expert on this but my suggestions is to try zingolib (usage example: GitHub - james-katz/zingolib-wrapper), or check the ECC SDK (which just implemented Spend before Sync, but I never looked into it, I don’t know how it’s abstracted)

Hopefully someone else will have a better answer.

3 Likes

Thanks. I’ll take a look. I can’t use the wrapper because that’s a JS binding which doesn’t apply, but it may provide a useful idea of how to call into the dependency crates (and which ones to use).

The ECC SDK apparently only exists for Android and iOS. I’m looking for a rust crate that I can build into a .dll that targets all the OSs and architectures.
Is the ECC really only focused on phones?

Well the zingolabs lightwallet client crate is awesome. I’m actually syncing down the blockchain!
But it’s clearly written specifically for the zingo wallet, because it is saving to disk under a zingo filename that it apparently offers no control over. And it looks like this zingo crate has a lot of its own implementation – it isn’t just a thin wrapper over other crates that I can call directly into.
So I’m thinking I’ll just have to maintain my own fork of their crate so I can change things about it. Unless they’re interested in taking PRs that generalize their crate for use by other wallets.

2 Likes

Ah right, sorry. I never looked in detail into the SDK, but I know there’s a Rust set of crates which are used by the Android/iOS SDK, so you could look into it to try to do something similar. But in any case zingolib does something similar and might be your better option for now.

#monorepo - Zcash Ecosystem Monorepo

No one likes the idea of an actual monorepo allowing for atomic changes. But, wouldn’t it be nice if everything worked together and changes could atomically affect the whole ecosystem? That problem should be a main driver, whether monorepo is the solution or not.

1 Like

Any luck using zcash-sync ? I’m trying to compile the integration example for 2 days now withou any success.

I’ve been reading the github issues, followed the steps of cloning zwallet and compile zcash-sync as dart_ffi rlib. It compiles Ok, but I have no idea how to use it :rofl:

I’m still learning Rust though, maybe it’s a very simple task and I’m just dumb :man_shrugging:

What error are you getting?

1 Like

The ECC mobile SDKs, and the Rust crates that back them (zcash_client_backend and zcash_client_sqlite) are in large part influenced by the early mobile SDK consumers. As a result, while those two Rust crates provide everything you need to build a pure Rust wallet (I have used them to built a testnet-only Rust CLI wallet for development testing purposes), they do so with a “stateless” API (i.e. one that assumes you’re setting up and tearing down wallet state on every call, like when using Rust across an FFI) that is not particularly intuitive (it lacks the usability affordances that the Android and iOS SDKs provide on top of them).

The APIs have also been undergoing significant churn over multiple releases, as we’ve been working through several years of technical debt to improve how they work and enable Spend-before-Sync. We’ll be cutting new releases of the crates with the Spend-before-Sync changes very soon, but you can see a preview of it in the repository: GitHub - zcash/librustzcash: Rust-language assets for Zcash

2 Likes

I gave up, being unable to find help anywhere. If you managed to get the dart_ffi rlib to build, that may be progress, I don’t remember exactly how far I got. I should pull it out again to try, because I’d love to get warp sync in my C# library.

In the meantime though, I found it much easier to consume zingolib, with a whole team to support it. But their sync is 5X slower than warp sync (this is an area they are actively working on), so I’d still switch if I could.

2 Likes

I was able to get a basic node ffi going with warp sync and build it into an electron app. I got pretty far. The state should still be the same at the head of the main branch of the old metarepo:

I haven’t had time to continue it or keep it up to date but it was able to sync last I checked.

I really want wasm though being a web maximalist and all …

2 Likes

Well never mind, I got it working now :slight_smile:
At first I was getting errors about incompatible orchard version, but then I follwed your steps of cloning the zwallet repo and compiled .rlib file, but had no idea of what to do with.
Then I tried to compile the rust integration example again and got a error complaining about workspace … I had ro change zwallet/Cargo.toml from

[workspace]
members = ["native/*"]

to

[workspace]
members = ["native/zcash-sync/integrations/rust"]

It compiled ok :rocket:

2 Likes

So, I have warp api working and I can do some basic stuff already. But for some reason I cannot get the unified address using warp_api_ffi::api::account::get_unified_address. I don’t know if I’m doing this correctly.

My code just hangs up, the process doesn’t close, doesn’t give a error, nothing … just sites there doing nothing …

My code:

use warp_api_ffi::api::account::{new_account, check_account, get_unified_address};
use warp_api_ffi::api::sync::{coin_sync, skip_to_last_height};
use warp_api_ffi::{CoinConfig, init_coin, set_coin_lwd_url, set_active_account};
use lazy_static::lazy_static;
use std::sync::Mutex;

lazy_static! {
    static ref CANCEL: Mutex<bool> = Mutex::new(false);
}

#[tokio::main]
async fn main() {
    env_logger::init();

    // Initialize the library for Zcash (coin = 0)
    init_coin(0, "./zec.db").unwrap();
    set_coin_lwd_url(0, "https://mainnet.lightwalletd.com:9067");
    
    let id_account = 1;

    if check_account(0, id_account) {
        println!("Srating existing account id {}", id_account);
        set_active_account(0, id_account);
    } else {        
        let id_account = new_account(0, "test", None, None).unwrap();
        let _ = skip_to_last_height(0).await;
        println!("Created new account wuth id {}", id_account);
    }

    println!("Starting wallet synchronization ...");
    coin_sync(0, 
        true, 
        0, 
        100, 
        |p| {            
            println!("{}", p.height)
        },
        &CANCEL).await.unwrap();

    // Grab the database accessor    
    let cc = &CoinConfig::get_active();
    let db = cc.db.as_ref().unwrap().clone();
    let db = db.lock().unwrap();    

    let bal = db.get_balance(id_account).unwrap();
    println!("Balance: {}", bal);

    let info = db.get_account_info(id_account).unwrap();
    // println!("{:?}", info);
    println!("Sapling address: {}", info.address);    

    let ua = get_unified_address(cc.coin, cc.id_account, 7).unwrap();
    println!("Unified address: {}", ua);    

}

Output:

Srating existing account id 1
Starting wallet synchronization ...
2221189
Balance: 40000
Sapling address: zs1z3mwhdyx4ehcy6j8plag634vh0svhw6ryev8yca8supmy4qsfsa3fznw0fscm53xcvpnwn96wq2

And it sits there forever …

What could it be? or I’m just doing it wrong?

I really would like to make it work, for current and future projects …

The function get_unifird_address is a high level function that accesses the db too. Therefore you need to release the db lock before you call it.

1 Like