Skip to content

WalletRead::get_transparent_address_metadata unimplemented — zingo-cli panics on any spend from a transparent balance #49

Description

@USCMig

zingo-cli panics on any transaction spending transparent inputs (shield, send, send_all, quicksend)

Background

I was originally planning on running the crosslink gui node, but had issues with staking so moved to headless. To make staking "easier" I used the zingo delegator wallet address as the mining rewards address and it currently has 18,000 cTAZ sitting in it. Now to utilize the staking options in the new CLI push, I have to send those funds back to the internal wallet address, but ran into the bug below.

Summary

Building zingo-cli from this repo's vendored zingo-lib (inside crosslink_monolith, not the upstream
zingolabs/zingolib repo) and attempting to spend from a wallet's transparent balance — via shield, send,
send_all, or quicksend — panics during confirm (i.e. during actual transaction/proof construction, after
the proposal step already succeeded):

thread '<unnamed>' panicked at /home/blockheads/crosslink_monolith/librustzcash/zcash_client_backend/src/data_api.rs:1866:9:
not implemented: WalletRead::get_transparent_address_metadata must be overridden for wallets to use the `transparent-inputs` feature
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Error executing command confirm: receiving on a closed channel
2026-07-20T20:29:44.426802Z ERROR zingo_cli: Error executing command confirm: receiving on a closed channel

thread 'main' panicked at zingo-cli/src/lib.rs:162:55:
called `Result::unwrap()` on an `Err` value: SendError { .. }

The panic crashes the background worker thread; the resulting closed channel then crashes the zingo-cli
main thread too (a SendError unwrap in zingo-cli/src/lib.rs:162), taking down the whole process. The
process exits before broadcasting anything, so no funds are moved or at risk from this — but the CLI is
unusable for this purpose as-is.

Root cause

zcash_client_backend::data_api::WalletRead has an optional trait method, gated behind the
transparent-inputs feature, whose default implementation is a bare unimplemented!():

// librustzcash/zcash_client_backend/src/data_api.rs:1859-1868
#[cfg(feature = "transparent-inputs")]
fn get_transparent_address_metadata(
    &self,
    _account: Self::AccountId,
    _address: &TransparentAddress,
) -> Result<Option<TransparentAddressMetadata>, Self::Error> {
    unimplemented!(
        "WalletRead::get_transparent_address_metadata must be overridden for wallets to use the `transparent-inputs` feature"
    )
}

zingolib/Cargo.toml enables the transparent-inputs feature (zingolib/Cargo.toml:43),
so this code path is compiled in and reachable. However, zingolib's implementation of WalletRead for
LightWallet (zingolib/src/wallet/zcb_traits.rs:66)
does not override get_transparent_address_metadata — it implements neighboring methods
(get_transparent_receivers at line 245, get_transparent_balances at line 272) but not this one, so it
falls through to the panicking default.

The method is invoked from the transaction-building path whenever a transparent input needs signing metadata
(essentially, its HD derivation info) in order to be spent — see
zcash_client_backend/src/data_api/wallet.rs:1175-1197.
That's why this reproduces for any command that spends transparent inputs, not just shieldsend,
send_all, and quicksend would hit the identical panic for a wallet whose spendable balance is transparent.

Consistent with this: balance itself works fine and correctly reports a real, fully-confirmed
transparent balance —

confirmed_transparent_balance: 1_822_514_065_000
unconfirmed_transparent_balance: 0
total_transparent_balance: 1_822_514_065_000

(all other pools 0) — because balance display only needs the already-implemented read-side methods. Only the
spend path, which needs get_transparent_address_metadata, is broken. So a wallet can appear completely
healthy (correct balance, correct sync) while being fully unable to move any of it.

This most likely surfaced from the recent zingo-lib Cargo.lock bump on s1_dev
(commit c6a2091, "chore: update zingo-lib's Cargo.lock") — consistent with zcash_client_backend being
bumped to a version that added this required override without zingolib's LightWallet impl being updated
to provide it.

Steps to reproduce

  1. Build zingo-cli from this repo (cd zingo-lib && cargo build --release -p zingo-cli).
  2. Restore/connect a wallet whose balance is entirely transparent (e.g. an address that's only ever received
    mining coinbase output — nothing shielded yet):
    zingo-cli --seed "<seed>" --chain testnet --server http://127.0.0.1:18234 --data-dir <dir>
    
  3. Run shield (or send <any address> <amount>, send_all <address>) — the proposal step succeeds and
    shows a fee estimate.
  4. Run confirm — panics as shown above, process exits.

Environment

  • Repo: ShieldedLabs/crosslink_monolith, branch s1_dev
  • zingo-cli built via cargo build --release -p zingo-cli from zingo-lib/
  • Wallet: transparent-only balance, 18,225.14065 ctaz confirmed at time of testing, address tmE7tpsvVC411sSZg6bTxH9jDE7wE5D37pe
  • Connected to this node's embedded Zaino gRPC server at 127.0.0.1:18234

Suggested fix

Implement get_transparent_address_metadata on zingolib's WalletRead for LightWallet
(zingolib/src/wallet/zcb_traits.rs, alongside the existing get_transparent_receivers /
get_transparent_balances methods), returning the derivation metadata (TransparentAddressMetadata) for
addresses the wallet already tracks — likely derivable from the same account/address-index bookkeeping those
neighboring methods already use. Given this touches transparent-input signing, getting it correct matters;
worth having someone familiar with zingolib's internal address-index bookkeeping implement and test it rather
than a quick patch.

Practical impact

This blocks moving funds out of any transparent-only zingolib wallet via this repo's zingo-cli, which in
turn blocks funding the Crosslink devnet's internal staking wallet from an external Zingo wallet by that
route. Until fixed, alternatives are: (a) use an existing Zingo wallet app built from a properly-implemented
zingolib (upstream zingolabs/zingolib, not this repo's vendored copy) to do the same send, or (b) build
zingo-cli from upstream zingolabs/zingolib instead of this repo's fork, pointed at the same Zaino server.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions