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 shield — send,
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
- Build
zingo-cli from this repo (cd zingo-lib && cargo build --release -p zingo-cli).
- 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>
- Run
shield (or send <any address> <amount>, send_all <address>) — the proposal step succeeds and
shows a fee estimate.
- 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.
zingo-clipanics 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-clifrom this repo's vendoredzingo-lib(insidecrosslink_monolith, not the upstreamzingolabs/zingolibrepo) and attempting to spend from a wallet's transparent balance — viashield,send,send_all, orquicksend— panics duringconfirm(i.e. during actual transaction/proof construction, afterthe proposal step already succeeded):
The panic crashes the background worker thread; the resulting closed channel then crashes the
zingo-climain thread too (a
SendErrorunwrap inzingo-cli/src/lib.rs:162), taking down the whole process. Theprocess 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::WalletReadhas an optional trait method, gated behind thetransparent-inputsfeature, whose default implementation is a bareunimplemented!():zingolib/Cargo.tomlenables thetransparent-inputsfeature (zingolib/Cargo.toml:43),so this code path is compiled in and reachable. However,
zingolib's implementation ofWalletReadforLightWallet(zingolib/src/wallet/zcb_traits.rs:66)does not override
get_transparent_address_metadata— it implements neighboring methods(
get_transparent_receiversat line 245,get_transparent_balancesat line 272) but not this one, so itfalls 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
shield—send,send_all, andquicksendwould hit the identical panic for a wallet whose spendable balance is transparent.Consistent with this:
balanceitself works fine and correctly reports a real, fully-confirmedtransparent balance —
(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 completelyhealthy (correct balance, correct sync) while being fully unable to move any of it.
This most likely surfaced from the recent
zingo-libCargo.lockbump ons1_dev(commit
c6a2091, "chore: update zingo-lib's Cargo.lock") — consistent withzcash_client_backendbeingbumped to a version that added this required override without
zingolib'sLightWalletimpl being updatedto provide it.
Steps to reproduce
zingo-clifrom this repo (cd zingo-lib && cargo build --release -p zingo-cli).mining coinbase output — nothing shielded yet):
shield(orsend <any address> <amount>,send_all <address>) — the proposal step succeeds andshows a fee estimate.
confirm— panics as shown above, process exits.Environment
ShieldedLabs/crosslink_monolith, branchs1_devzingo-clibuilt viacargo build --release -p zingo-clifromzingo-lib/tmE7tpsvVC411sSZg6bTxH9jDE7wE5D37pe127.0.0.1:18234Suggested fix
Implement
get_transparent_address_metadataonzingolib'sWalletRead for LightWallet(
zingolib/src/wallet/zcb_traits.rs, alongside the existingget_transparent_receivers/get_transparent_balancesmethods), returning the derivation metadata (TransparentAddressMetadata) foraddresses 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 inturn 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) buildzingo-clifrom upstreamzingolabs/zingolibinstead of this repo's fork, pointed at the same Zaino server.