From 1cbeac9d593d65938870797fb8dbafa352644d34 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 24 May 2019 15:59:18 +0100 Subject: [PATCH] zcash_client_sqlite: Support sending to t-addrs --- Cargo.lock | 1 + zcash_client_sqlite/Cargo.toml | 1 + zcash_client_sqlite/src/address.rs | 63 +++++++++++++++++++++++++++++ zcash_client_sqlite/src/error.rs | 8 ++++ zcash_client_sqlite/src/lib.rs | 1 + zcash_client_sqlite/src/transact.rs | 30 ++++++++------ 6 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 zcash_client_sqlite/src/address.rs diff --git a/Cargo.lock b/Cargo.lock index a785792..810c246 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -676,6 +676,7 @@ name = "zcash_client_sqlite" version = "0.0.0" dependencies = [ "bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bs58 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "ff 0.4.0", "pairing 0.14.2", "protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 697cb12..042a43b 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] bech32 = "0.7" +bs58 = { version = "0.2", features = ["check"] } ff = { path = "../ff" } pairing = { path = "../pairing" } protobuf = "2" diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs new file mode 100644 index 0000000..f72512f --- /dev/null +++ b/zcash_client_sqlite/src/address.rs @@ -0,0 +1,63 @@ +//! Structs for handling supported address types. + +use pairing::bls12_381::Bls12; +use zcash_client_backend::encoding::{ + decode_payment_address, decode_transparent_address, encode_payment_address, + encode_transparent_address, +}; +use zcash_primitives::{legacy::TransparentAddress, primitives::PaymentAddress}; + +#[cfg(feature = "mainnet")] +use zcash_client_backend::constants::mainnet::{ + B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, +}; + +#[cfg(not(feature = "mainnet"))] +use zcash_client_backend::constants::testnet::{ + B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, +}; + +/// An address that funds can be sent to. +pub enum RecipientAddress { + Shielded(PaymentAddress), + Transparent(TransparentAddress), +} + +impl From> for RecipientAddress { + fn from(addr: PaymentAddress) -> Self { + RecipientAddress::Shielded(addr) + } +} + +impl From for RecipientAddress { + fn from(addr: TransparentAddress) -> Self { + RecipientAddress::Transparent(addr) + } +} + +impl RecipientAddress { + pub fn from_str(s: &str) -> Option { + if let Ok(Some(pa)) = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, s) { + Some(pa.into()) + } else if let Ok(Some(addr)) = + decode_transparent_address(&B58_PUBKEY_ADDRESS_PREFIX, &B58_SCRIPT_ADDRESS_PREFIX, s) + { + Some(addr.into()) + } else { + None + } + } + + pub fn to_string(&self) -> String { + match self { + RecipientAddress::Shielded(pa) => { + encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, pa) + } + RecipientAddress::Transparent(addr) => encode_transparent_address( + &B58_PUBKEY_ADDRESS_PREFIX, + &B58_SCRIPT_ADDRESS_PREFIX, + addr, + ), + } + } +} diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index b0dfb47..12b2d98 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -20,6 +20,7 @@ pub enum ErrorKind { ScanRequired, TableNotEmpty, Bech32(bech32::Error), + Base58(bs58::decode::DecodeError), Builder(builder::Error), Database(rusqlite::Error), Io(std::io::Error), @@ -65,6 +66,7 @@ impl fmt::Display for Error { ErrorKind::ScanRequired => write!(f, "Must scan blocks first"), ErrorKind::TableNotEmpty => write!(f, "Table is not empty"), ErrorKind::Bech32(e) => write!(f, "{}", e), + ErrorKind::Base58(e) => write!(f, "{}", e), ErrorKind::Builder(e) => write!(f, "{:?}", e), ErrorKind::Database(e) => write!(f, "{}", e), ErrorKind::Io(e) => write!(f, "{}", e), @@ -93,6 +95,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: bs58::decode::DecodeError) -> Self { + Error(ErrorKind::Base58(e)) + } +} + impl From for Error { fn from(e: builder::Error) -> Self { Error(ErrorKind::Builder(e)) diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 5424466..9e4d947 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -39,6 +39,7 @@ use zcash_client_backend::constants::testnet::{ HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, }; +pub mod address; pub mod chain; pub mod error; pub mod init; diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 936cc9f..ad95bc9 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -4,12 +4,12 @@ use ff::{PrimeField, PrimeFieldRepr}; use pairing::bls12_381::Bls12; use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::path::Path; -use zcash_client_backend::encoding::{encode_extended_full_viewing_key, encode_payment_address}; +use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{ jubjub::fs::{Fs, FsRepr}, merkle_tree::IncrementalWitness, note_encryption::Memo, - primitives::{Diversifier, Note, PaymentAddress}, + primitives::{Diversifier, Note}, prover::TxProver, sapling::Node, transaction::{ @@ -21,9 +21,9 @@ use zcash_primitives::{ }; use crate::{ + address::RecipientAddress, error::{Error, ErrorKind}, get_target_and_anchor_heights, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, - HRP_SAPLING_PAYMENT_ADDRESS, }; struct SelectedNoteRow { @@ -61,7 +61,7 @@ struct SelectedNoteRow { /// /// let account = 0; /// let extsk = spending_key(&[0; 32][..], COIN_TYPE, account); -/// let to = extsk.default_address().unwrap().1; +/// let to = extsk.default_address().unwrap().1.into(); /// match create_to_address( /// "/path/to/data.db", /// SAPLING_CONSENSUS_BRANCH_ID, @@ -80,7 +80,7 @@ pub fn create_to_address>( consensus_branch_id: u32, prover: impl TxProver, (account, extsk): (u32, &ExtendedSpendingKey), - to: &PaymentAddress, + to: &RecipientAddress, value: Amount, memo: Option, ) -> Result { @@ -221,7 +221,12 @@ pub fn create_to_address>( selected.witness, )?; } - builder.add_sapling_output(ovk, to.clone(), value, memo.clone())?; + match to { + RecipientAddress::Shielded(to) => { + builder.add_sapling_output(ovk, to.clone(), value, memo.clone()) + } + RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value), + }?; let (tx, tx_metadata) = builder.build(consensus_branch_id, prover)?; // We only called add_sapling_output() once. let output_index = match tx_metadata.output_index(0) { @@ -263,7 +268,8 @@ pub fn create_to_address>( } // Save the sent note in the database. - let to_str = encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, to); + // TODO: Decide how to save transparent output information. + let to_str = to.to_string(); if let Some(memo) = memo { let mut stmt_insert_sent_note = data.prepare( "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) @@ -340,7 +346,7 @@ mod tests { ExtendedFullViewingKey::from(&extsk1), ]; init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk0.default_address().unwrap().1; + let to = extsk0.default_address().unwrap().1.into(); // Invalid extsk for the given account should cause an error match create_to_address( @@ -379,7 +385,7 @@ mod tests { let extsk = ExtendedSpendingKey::master(&[]); let extfvks = [ExtendedFullViewingKey::from(&extsk)]; init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk.default_address().unwrap().1; + let to = extsk.default_address().unwrap().1.into(); // We cannot do anything if we aren't synchronised match create_to_address( @@ -407,7 +413,7 @@ mod tests { let extsk = ExtendedSpendingKey::master(&[]); let extfvks = [ExtendedFullViewingKey::from(&extsk)]; init_accounts_table(&db_data, &extfvks).unwrap(); - let to = extsk.default_address().unwrap().1; + let to = extsk.default_address().unwrap().1.into(); // Account balance should be zero assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); @@ -476,7 +482,7 @@ mod tests { // Spend fails because there are insufficient verified notes let extsk2 = ExtendedSpendingKey::master(&[]); - let to = extsk2.default_address().unwrap().1; + let to = extsk2.default_address().unwrap().1.into(); match create_to_address( db_data, 1, @@ -575,7 +581,7 @@ mod tests { // Send some of the funds to another address let extsk2 = ExtendedSpendingKey::master(&[]); - let to = extsk2.default_address().unwrap().1; + let to = extsk2.default_address().unwrap().1.into(); create_to_address( db_data, 1,