diff --git a/Cargo.lock b/Cargo.lock index 043dd2eb..29c11693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7467,6 +7467,7 @@ dependencies = [ "zcash_address 0.10.1", "zcash_client_backend", "zcash_client_sqlite", + "zcash_encoding 0.3.0", "zcash_keys 0.12.0", "zcash_note_encryption", "zcash_primitives 0.26.1", @@ -7636,10 +7637,10 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca38087e6524e5f51a5b0fb3fc18f36d7b84bf67b2056f494ca0c281590953d" +source = "git+https://github.com/distractedm1nd/librustzcash.git?rev=cf39300ffa47aca9f2dec1f55727d17a5301f2fe#cf39300ffa47aca9f2dec1f55727d17a5301f2fe" dependencies = [ "core2 0.3.3", + "hex", "nonempty 0.11.0", ] diff --git a/Cargo.toml b/Cargo.toml index d547a098..80a99575 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ zcash_protocol = "0.7" orchard = "0.11" sapling = { package = "sapling-crypto", version = "0.5" } transparent = { package = "zcash_transparent", version = "0.6" } +zcash_encoding = "0.3.0" zcash_keys = { version = "0.12", features = ["transparent-inputs", "sapling", "orchard", "transparent-key-encoding"] } zcash_primitives = "0.26" zcash_proofs = "0.26" @@ -135,6 +136,7 @@ anyhow = "1.0" tonic = "0.14" [patch.crates-io] +zcash_encoding ={git = "https://github.com/distractedm1nd/librustzcash.git", rev = "cf39300ffa47aca9f2dec1f55727d17a5301f2fe", package = "zcash_encoding"} age = { git = "https://github.com/str4d/rage.git", rev = "92f437bc2a061312fa6633bb70c91eef91142fd4" } abscissa_core = { git = "https://github.com/iqlusioninc/abscissa.git", rev = "fdb60678fb3a883decf63d6d3ebc512abd20406f" } diff --git a/zallet/Cargo.toml b/zallet/Cargo.toml index 0e678f4e..5c4be240 100644 --- a/zallet/Cargo.toml +++ b/zallet/Cargo.toml @@ -143,6 +143,7 @@ uuid.workspace = true which = { workspace = true, optional = true } xdg.workspace = true zaino-common.workspace = true +zcash_encoding.workspace = true zaino-fetch.workspace = true zaino-proto.workspace = true zaino-state.workspace = true diff --git a/zallet/src/components/json_rpc/methods/get_raw_transaction.rs b/zallet/src/components/json_rpc/methods/get_raw_transaction.rs index d3f7efc9..843284b3 100644 --- a/zallet/src/components/json_rpc/methods/get_raw_transaction.rs +++ b/zallet/src/components/json_rpc/methods/get_raw_transaction.rs @@ -5,6 +5,7 @@ use schemars::JsonSchema; use serde::Serialize; use transparent::bundle::{TxIn, TxOut}; use zaino_state::{FetchServiceError, FetchServiceSubscriber, LightWalletIndexer, ZcashIndexer}; +use zcash_encoding::ReverseHex; use zcash_protocol::{ TxId, consensus::{self, BlockHeight}, @@ -15,7 +16,7 @@ use crate::components::{ database::DbConnection, json_rpc::{ server::LegacyCode, - utils::{JsonZec, JsonZecBalance, parse_txid, value_from_zat_balance, value_from_zatoshis}, + utils::{JsonZec, JsonZecBalance, value_from_zat_balance, value_from_zatoshis}, }, }; @@ -475,7 +476,7 @@ pub(crate) async fn call( verbose: Option, blockhash: Option, ) -> Response { - let _txid = parse_txid(txid_str)?; + let _txid = TxId::from_bytes(ReverseHex::decode(txid_str).expect("valid txid")); let verbose = verbose.is_some_and(|v| v != 0); // TODO: We can't support this via the current Zaino API; wait for `ChainIndex`. @@ -572,7 +573,7 @@ pub(crate) async fn call( let (vjoinsplit, join_split_pub_key, join_split_sig) = match tx.sprout_bundle() { Some(bundle) if !bundle.joinsplits.is_empty() => ( bundle.joinsplits.iter().map(JoinSplit::encode).collect(), - Some(TxId::from_bytes(bundle.joinsplit_pubkey).to_string()), + Some(ReverseHex::decode(bundle.joinsplit_pubkey)), Some(hex::encode(bundle.joinsplit_sig)), ), _ => (vec![], None, None), @@ -614,8 +615,7 @@ pub(crate) async fn call( in_active_chain: None, hex: tx_hex, txid: txid_str.to_ascii_lowercase(), - authdigest: TxId::from_bytes(tx.auth_commitment().as_bytes().try_into().unwrap()) - .to_string(), + authdigest: ReverseHex::encode(&tx.auth_commitment().as_bytes().try_into().unwrap()), size, overwintered, version: tx.version().header() & 0x7FFFFFFF, @@ -709,10 +709,10 @@ impl JoinSplit { impl SaplingSpend { fn encode(spend: &SpendDescription) -> Self { Self { - cv: TxId::from_bytes(spend.cv().to_bytes()).to_string(), - anchor: TxId::from_bytes(spend.anchor().to_bytes()).to_string(), - nullifier: TxId::from_bytes(spend.nullifier().0).to_string(), - rk: TxId::from_bytes(<[u8; 32]>::from(*spend.rk())).to_string(), + cv: ReverseHex::encode(&spend.cv().to_bytes()), + anchor: ReverseHex::encode(&spend.anchor().to_bytes()), + nullifier: ReverseHex::encode(&spend.nullifier().0), + rk: ReverseHex::encode(&<[u8; 32]>::from(*spend.rk())), proof: hex::encode(spend.zkproof()), spend_auth_sig: hex::encode(<[u8; 64]>::from(*spend.spend_auth_sig())), } @@ -722,9 +722,9 @@ impl SaplingSpend { impl SaplingOutput { fn encode(output: &OutputDescription) -> Self { Self { - cv: TxId::from_bytes(output.cv().to_bytes()).to_string(), - cmu: TxId::from_bytes(output.cmu().to_bytes()).to_string(), - ephemeral_key: TxId::from_bytes(output.ephemeral_key().0).to_string(), + cv: ReverseHex::encode(&output.cv().to_bytes()), + cmu: ReverseHex::encode(&output.cmu().to_bytes()), + ephemeral_key: ReverseHex::encode(&output.ephemeral_key().0), enc_ciphertext: hex::encode(output.enc_ciphertext()), out_ciphertext: hex::encode(output.out_ciphertext()), proof: hex::encode(output.zkproof()), diff --git a/zallet/src/components/json_rpc/methods/view_transaction.rs b/zallet/src/components/json_rpc/methods/view_transaction.rs index 7b689ed7..2c507099 100644 --- a/zallet/src/components/json_rpc/methods/view_transaction.rs +++ b/zallet/src/components/json_rpc/methods/view_transaction.rs @@ -15,6 +15,7 @@ use zcash_address::{ }; use zcash_client_backend::data_api::WalletRead; use zcash_client_sqlite::{AccountUuid, error::SqliteClientError}; +use zcash_encoding::ReverseHex; use zcash_keys::encoding::AddressCodec; use zcash_note_encryption::{try_note_decryption, try_output_recovery_with_ovk}; use zcash_protocol::{ @@ -30,7 +31,7 @@ use crate::components::{ database::DbConnection, json_rpc::{ server::LegacyCode, - utils::{JsonZec, parse_txid, value_from_zatoshis}, + utils::{JsonZec, value_from_zatoshis}, }, }; @@ -275,7 +276,7 @@ pub(crate) async fn call( chain: FetchServiceSubscriber, txid_str: &str, ) -> Response { - let txid = parse_txid(txid_str)?; + let txid = TxId::from_bytes(ReverseHex::decode(txid_str).expect("valid txid")); // Fetch this early so we can detect if the wallet is not ready yet. // TODO: Replace with Zaino `ChainIndex` so we can operate against a chain snapshot. diff --git a/zallet/src/components/json_rpc/utils.rs b/zallet/src/components/json_rpc/utils.rs index 090ac65a..6e118338 100644 --- a/zallet/src/components/json_rpc/utils.rs +++ b/zallet/src/components/json_rpc/utils.rs @@ -9,7 +9,6 @@ use schemars::{JsonSchema, json_schema}; use serde::Serialize; use zcash_client_sqlite::AccountUuid; use zcash_protocol::{ - TxId, consensus::BlockHeight, value::{COIN, ZatBalance, Zatoshis}, }; @@ -43,16 +42,6 @@ pub(super) async fn ensure_wallet_is_unlocked(keystore: &KeyStore) -> RpcResult< } } -// TODO: Move this to `zcash_protocol`. -// https://github.com/zcash/librustzcash/issues/1934 -pub(crate) fn parse_txid(txid_str: &str) -> RpcResult { - let mut bytes = [0; 32]; - hex::decode_to_slice(txid_str, &mut bytes) - .map_err(|_| LegacyCode::InvalidParameter.with_static("invalid txid"))?; - bytes.reverse(); - Ok(TxId::from_bytes(bytes)) -} - /// Parses the `seedfp` parameter present in many wallet RPCs. #[cfg(zallet_build = "wallet")] pub(super) fn parse_seedfp_parameter(seedfp: &str) -> RpcResult { diff --git a/zallet/src/components/sync.rs b/zallet/src/components/sync.rs index e0541a4d..8c6e1063 100644 --- a/zallet/src/components/sync.rs +++ b/zallet/src/components/sync.rs @@ -22,6 +22,7 @@ use zcash_client_backend::{ scanning::ScanError, wallet::WalletTransparentOutput, }; +use zcash_encoding::ReverseHex; use zcash_keys::encoding::AddressCodec; use zcash_primitives::transaction::Transaction; use zcash_protocol::{ @@ -38,9 +39,7 @@ use super::{ chain_view::ChainView, database::{Database, DbConnection}, }; -use crate::{ - components::json_rpc::utils::parse_txid, config::ZalletConfig, error::Error, network::Network, -}; +use crate::{config::ZalletConfig, error::Error, network::Network}; mod cache; @@ -649,8 +648,10 @@ async fn data_requests( }; for txid_str in chain.get_address_tx_ids(request).await? { - let txid = parse_txid(&txid_str) - .expect("TODO: Zaino's API should have caught this error for us"); + let txid = TxId::from_bytes( + ReverseHex::decode(&txid_str) + .expect("TODO: Zaino's API should have caught this error for us"), + ); let tx = match chain.get_raw_transaction(txid_str, Some(1)).await? { // TODO: Zaino should have a Rust API for fetching tx details,