zcash_client_sqlite: Use `ZcashAddress` for persistence of sent note addresses
Prior to this change, the recipient of a sent transaction would always be shown as the protocol-level address, instead of any unified address intended as the recipient. Now, instead of reencoding the recipient address, we use the original `ZcashAddress` value from the payment request.
This commit is contained in:
parent
86e1181259
commit
b60600a4c3
|
@ -8,8 +8,8 @@ and this library adheres to Rust's notion of
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- `zcash_address::ZcashAddress::{can_receive_memo, can_receive_as}`
|
- `zcash_address::ZcashAddress::{can_receive_memo, can_receive_as, matches_receiver}`
|
||||||
- `zcash_address::unified::Address::{can_receive_memo, has_receiver}`
|
- `zcash_address::unified::Address::{can_receive_memo, has_receiver_of_type, contains_receiver}`
|
||||||
- Module `zcash_address::testing` under the `test-dependencies` feature.
|
- Module `zcash_address::testing` under the `test-dependencies` feature.
|
||||||
- Module `zcash_address::unified::address::testing` under the
|
- Module `zcash_address::unified::address::testing` under the
|
||||||
`test-dependencies` feature.
|
`test-dependencies` feature.
|
||||||
|
|
|
@ -105,7 +105,7 @@ pub struct Address(pub(crate) Vec<Receiver>);
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
/// Returns whether this address has the ability to receive transfers of the given pool type.
|
/// Returns whether this address has the ability to receive transfers of the given pool type.
|
||||||
pub fn has_receiver(&self, pool_type: PoolType) -> bool {
|
pub fn has_receiver_of_type(&self, pool_type: PoolType) -> bool {
|
||||||
self.0.iter().any(|r| match r {
|
self.0.iter().any(|r| match r {
|
||||||
Receiver::Orchard(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Orchard),
|
Receiver::Orchard(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Orchard),
|
||||||
Receiver::Sapling(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Sapling),
|
Receiver::Sapling(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Sapling),
|
||||||
|
@ -115,6 +115,11 @@ impl Address {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether this address contains the given receiver.
|
||||||
|
pub fn contains_receiver(&self, receiver: &Receiver) -> bool {
|
||||||
|
self.0.contains(receiver)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether this address can receive a memo.
|
/// Returns whether this address can receive a memo.
|
||||||
pub fn can_receive_memo(&self) -> bool {
|
pub fn can_receive_memo(&self) -> bool {
|
||||||
self.0
|
self.0
|
||||||
|
|
|
@ -141,6 +141,7 @@ pub use convert::{
|
||||||
};
|
};
|
||||||
pub use encoding::ParseError;
|
pub use encoding::ParseError;
|
||||||
pub use kind::unified;
|
pub use kind::unified;
|
||||||
|
use kind::unified::Receiver;
|
||||||
pub use zcash_protocol::consensus::NetworkType as Network;
|
pub use zcash_protocol::consensus::NetworkType as Network;
|
||||||
use zcash_protocol::{PoolType, ShieldedProtocol};
|
use zcash_protocol::{PoolType, ShieldedProtocol};
|
||||||
|
|
||||||
|
@ -273,7 +274,7 @@ impl ZcashAddress {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
AddressKind::Sprout(_) => false,
|
AddressKind::Sprout(_) => false,
|
||||||
AddressKind::Sapling(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Sapling),
|
AddressKind::Sapling(_) => pool_type == PoolType::Shielded(ShieldedProtocol::Sapling),
|
||||||
AddressKind::Unified(addr) => addr.has_receiver(pool_type),
|
AddressKind::Unified(addr) => addr.has_receiver_of_type(pool_type),
|
||||||
AddressKind::P2pkh(_) => pool_type == PoolType::Transparent,
|
AddressKind::P2pkh(_) => pool_type == PoolType::Transparent,
|
||||||
AddressKind::P2sh(_) => pool_type == PoolType::Transparent,
|
AddressKind::P2sh(_) => pool_type == PoolType::Transparent,
|
||||||
AddressKind::Tex(_) => pool_type == PoolType::Transparent,
|
AddressKind::Tex(_) => pool_type == PoolType::Transparent,
|
||||||
|
@ -291,6 +292,19 @@ impl ZcashAddress {
|
||||||
AddressKind::Tex(_) => false,
|
AddressKind::Tex(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether or not this address contains or corresponds to the given unified address
|
||||||
|
/// receiver.
|
||||||
|
pub fn matches_receiver(&self, receiver: &Receiver) -> bool {
|
||||||
|
match (receiver, &self.kind) {
|
||||||
|
(r, AddressKind::Unified(ua)) => ua.contains_receiver(r),
|
||||||
|
(Receiver::Sapling(r), AddressKind::Sapling(d)) => r == d,
|
||||||
|
(Receiver::P2pkh(r), AddressKind::P2pkh(d)) => r == d,
|
||||||
|
(Receiver::P2pkh(r), AddressKind::Tex(d)) => r == d,
|
||||||
|
(Receiver::P2sh(r), AddressKind::P2sh(d)) => r == d,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "test-dependencies")]
|
#[cfg(feature = "test-dependencies")]
|
||||||
|
|
|
@ -6,6 +6,8 @@ and this library adheres to Rust's notion of
|
||||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- `zcash_protocol::PoolType::{TRANSPARENT, SAPLING, ORCHARD}`
|
||||||
|
|
||||||
## [0.1.1] - 2024-03-25
|
## [0.1.1] - 2024-03-25
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -42,6 +42,12 @@ pub enum PoolType {
|
||||||
Shielded(ShieldedProtocol),
|
Shielded(ShieldedProtocol),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PoolType {
|
||||||
|
pub const TRANSPARENT: PoolType = PoolType::Transparent;
|
||||||
|
pub const SAPLING: PoolType = PoolType::Shielded(ShieldedProtocol::Sapling);
|
||||||
|
pub const ORCHARD: PoolType = PoolType::Shielded(ShieldedProtocol::Orchard);
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for PoolType {
|
impl fmt::Display for PoolType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -25,6 +25,10 @@ and this library adheres to Rust's notion of
|
||||||
- `zcash_client_backend::proto::proposal::Proposal::{from_standard_proposal,
|
- `zcash_client_backend::proto::proposal::Proposal::{from_standard_proposal,
|
||||||
try_into_standard_proposal}` each no longer require a `consensus::Parameters`
|
try_into_standard_proposal}` each no longer require a `consensus::Parameters`
|
||||||
argument.
|
argument.
|
||||||
|
- `zcash_client_backend::wallet::Recipient` variants have changed. Instead of
|
||||||
|
wrapping protocol-address types, the `Recipient` type now wraps a
|
||||||
|
`zcash_address::ZcashAddress`. This simplifies the process of tracking the
|
||||||
|
original address to which value was sent.
|
||||||
|
|
||||||
## [0.12.1] - 2024-03-27
|
## [0.12.1] - 2024-03-27
|
||||||
|
|
||||||
|
|
|
@ -980,8 +980,8 @@ where
|
||||||
memo.clone(),
|
memo.clone(),
|
||||||
)?;
|
)?;
|
||||||
orchard_output_meta.push((
|
orchard_output_meta.push((
|
||||||
Recipient::Unified(
|
Recipient::External(
|
||||||
ua.clone(),
|
payment.recipient_address().clone(),
|
||||||
PoolType::Shielded(ShieldedProtocol::Orchard),
|
PoolType::Shielded(ShieldedProtocol::Orchard),
|
||||||
),
|
),
|
||||||
payment.amount(),
|
payment.amount(),
|
||||||
|
@ -997,8 +997,8 @@ where
|
||||||
memo.clone(),
|
memo.clone(),
|
||||||
)?;
|
)?;
|
||||||
sapling_output_meta.push((
|
sapling_output_meta.push((
|
||||||
Recipient::Unified(
|
Recipient::External(
|
||||||
ua.clone(),
|
payment.recipient_address().clone(),
|
||||||
PoolType::Shielded(ShieldedProtocol::Sapling),
|
PoolType::Shielded(ShieldedProtocol::Sapling),
|
||||||
),
|
),
|
||||||
payment.amount(),
|
payment.amount(),
|
||||||
|
@ -1026,7 +1026,11 @@ where
|
||||||
payment.amount(),
|
payment.amount(),
|
||||||
memo.clone(),
|
memo.clone(),
|
||||||
)?;
|
)?;
|
||||||
sapling_output_meta.push((Recipient::Sapling(addr), payment.amount(), Some(memo)));
|
sapling_output_meta.push((
|
||||||
|
Recipient::External(payment.recipient_address().clone(), PoolType::SAPLING),
|
||||||
|
payment.amount(),
|
||||||
|
Some(memo),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Address::Transparent(to) => {
|
Address::Transparent(to) => {
|
||||||
if payment.memo().is_some() {
|
if payment.memo().is_some() {
|
||||||
|
@ -1034,7 +1038,11 @@ where
|
||||||
} else {
|
} else {
|
||||||
builder.add_transparent_output(&to, payment.amount())?;
|
builder.add_transparent_output(&to, payment.amount())?;
|
||||||
}
|
}
|
||||||
transparent_output_meta.push((to, payment.amount()));
|
transparent_output_meta.push((
|
||||||
|
Recipient::External(payment.recipient_address().clone(), PoolType::TRANSPARENT),
|
||||||
|
to,
|
||||||
|
payment.amount(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1156,7 +1164,10 @@ where
|
||||||
SentTransactionOutput::from_parts(output_index, recipient, value, memo)
|
SentTransactionOutput::from_parts(output_index, recipient, value, memo)
|
||||||
});
|
});
|
||||||
|
|
||||||
let transparent_outputs = transparent_output_meta.into_iter().map(|(addr, value)| {
|
let transparent_outputs =
|
||||||
|
transparent_output_meta
|
||||||
|
.into_iter()
|
||||||
|
.map(|(recipient, addr, value)| {
|
||||||
let script = addr.script();
|
let script = addr.script();
|
||||||
let output_index = build_result
|
let output_index = build_result
|
||||||
.transaction()
|
.transaction()
|
||||||
|
@ -1168,9 +1179,11 @@ where
|
||||||
.find(|(_, tx_out)| tx_out.script_pubkey == script)
|
.find(|(_, tx_out)| tx_out.script_pubkey == script)
|
||||||
})
|
})
|
||||||
.map(|(index, _)| index)
|
.map(|(index, _)| index)
|
||||||
.expect("An output should exist in the transaction for each transparent payment.");
|
.expect(
|
||||||
|
"An output should exist in the transaction for each transparent payment.",
|
||||||
|
);
|
||||||
|
|
||||||
SentTransactionOutput::from_parts(output_index, Recipient::Transparent(addr), value, None)
|
SentTransactionOutput::from_parts(output_index, recipient, value, None)
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut outputs = vec![];
|
let mut outputs = vec![];
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//! light client.
|
//! light client.
|
||||||
|
|
||||||
use incrementalmerkletree::Position;
|
use incrementalmerkletree::Position;
|
||||||
use zcash_keys::address::Address;
|
use zcash_address::ZcashAddress;
|
||||||
use zcash_note_encryption::EphemeralKeyBytes;
|
use zcash_note_encryption::EphemeralKeyBytes;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
consensus::BlockHeight,
|
consensus::BlockHeight,
|
||||||
|
@ -19,7 +19,7 @@ use zcash_primitives::{
|
||||||
};
|
};
|
||||||
use zcash_protocol::value::BalanceError;
|
use zcash_protocol::value::BalanceError;
|
||||||
|
|
||||||
use crate::{address::UnifiedAddress, fees::sapling as sapling_fees, PoolType, ShieldedProtocol};
|
use crate::{fees::sapling as sapling_fees, PoolType, ShieldedProtocol};
|
||||||
|
|
||||||
#[cfg(feature = "orchard")]
|
#[cfg(feature = "orchard")]
|
||||||
use crate::fees::orchard as orchard_fees;
|
use crate::fees::orchard as orchard_fees;
|
||||||
|
@ -68,12 +68,10 @@ impl NoteId {
|
||||||
/// output.
|
/// output.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Recipient<AccountId, N> {
|
pub enum Recipient<AccountId, N> {
|
||||||
Transparent(TransparentAddress),
|
External(ZcashAddress, PoolType),
|
||||||
Sapling(sapling::PaymentAddress),
|
|
||||||
Unified(UnifiedAddress, PoolType),
|
|
||||||
InternalAccount {
|
InternalAccount {
|
||||||
receiving_account: AccountId,
|
receiving_account: AccountId,
|
||||||
external_address: Option<Address>,
|
external_address: Option<ZcashAddress>,
|
||||||
note: N,
|
note: N,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -81,9 +79,7 @@ pub enum Recipient<AccountId, N> {
|
||||||
impl<AccountId, N> Recipient<AccountId, N> {
|
impl<AccountId, N> Recipient<AccountId, N> {
|
||||||
pub fn map_internal_account_note<B, F: FnOnce(N) -> B>(self, f: F) -> Recipient<AccountId, B> {
|
pub fn map_internal_account_note<B, F: FnOnce(N) -> B>(self, f: F) -> Recipient<AccountId, B> {
|
||||||
match self {
|
match self {
|
||||||
Recipient::Transparent(t) => Recipient::Transparent(t),
|
Recipient::External(addr, pool) => Recipient::External(addr, pool),
|
||||||
Recipient::Sapling(s) => Recipient::Sapling(s),
|
|
||||||
Recipient::Unified(u, p) => Recipient::Unified(u, p),
|
|
||||||
Recipient::InternalAccount {
|
Recipient::InternalAccount {
|
||||||
receiving_account,
|
receiving_account,
|
||||||
external_address,
|
external_address,
|
||||||
|
@ -100,9 +96,7 @@ impl<AccountId, N> Recipient<AccountId, N> {
|
||||||
impl<AccountId, N> Recipient<AccountId, Option<N>> {
|
impl<AccountId, N> Recipient<AccountId, Option<N>> {
|
||||||
pub fn internal_account_note_transpose_option(self) -> Option<Recipient<AccountId, N>> {
|
pub fn internal_account_note_transpose_option(self) -> Option<Recipient<AccountId, N>> {
|
||||||
match self {
|
match self {
|
||||||
Recipient::Transparent(t) => Some(Recipient::Transparent(t)),
|
Recipient::External(addr, pool) => Some(Recipient::External(addr, pool)),
|
||||||
Recipient::Sapling(s) => Some(Recipient::Sapling(s)),
|
|
||||||
Recipient::Unified(u, p) => Some(Recipient::Unified(u, p)),
|
|
||||||
Recipient::InternalAccount {
|
Recipient::InternalAccount {
|
||||||
receiving_account,
|
receiving_account,
|
||||||
external_address,
|
external_address,
|
||||||
|
|
|
@ -71,6 +71,8 @@ This version was yanked, use 0.10.1 instead.
|
||||||
- `zcash_client_sqlite::error::SqliteClientError` has new error variants:
|
- `zcash_client_sqlite::error::SqliteClientError` has new error variants:
|
||||||
- `SqliteClientError::UnsupportedPoolType`
|
- `SqliteClientError::UnsupportedPoolType`
|
||||||
- `SqliteClientError::BalanceError`
|
- `SqliteClientError::BalanceError`
|
||||||
|
- The `Bech32DecodeError` variant has been replaced with a more general
|
||||||
|
`DecodingError` type.
|
||||||
|
|
||||||
## [0.8.1] - 2023-10-18
|
## [0.8.1] - 2023-10-18
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,8 @@ use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use shardtree::error::ShardTreeError;
|
use shardtree::error::ShardTreeError;
|
||||||
use zcash_client_backend::{
|
use zcash_address::ParseError;
|
||||||
encoding::{Bech32DecodeError, TransparentCodecError},
|
use zcash_client_backend::PoolType;
|
||||||
PoolType,
|
|
||||||
};
|
|
||||||
use zcash_keys::keys::AddressGenerationError;
|
use zcash_keys::keys::AddressGenerationError;
|
||||||
use zcash_primitives::zip32;
|
use zcash_primitives::zip32;
|
||||||
use zcash_primitives::{consensus::BlockHeight, transaction::components::amount::BalanceError};
|
use zcash_primitives::{consensus::BlockHeight, transaction::components::amount::BalanceError};
|
||||||
|
@ -16,7 +14,10 @@ use crate::wallet::commitment_tree;
|
||||||
use crate::PRUNING_DEPTH;
|
use crate::PRUNING_DEPTH;
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use zcash_primitives::legacy::TransparentAddress;
|
use {
|
||||||
|
zcash_client_backend::encoding::TransparentCodecError,
|
||||||
|
zcash_primitives::legacy::TransparentAddress,
|
||||||
|
};
|
||||||
|
|
||||||
/// The primary error type for the SQLite wallet backend.
|
/// The primary error type for the SQLite wallet backend.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -33,8 +34,8 @@ pub enum SqliteClientError {
|
||||||
/// Illegal attempt to reinitialize an already-initialized wallet database.
|
/// Illegal attempt to reinitialize an already-initialized wallet database.
|
||||||
TableNotEmpty,
|
TableNotEmpty,
|
||||||
|
|
||||||
/// A Bech32-encoded key or address decoding error
|
/// A Zcash key or address decoding error
|
||||||
Bech32DecodeError(Bech32DecodeError),
|
DecodingError(ParseError),
|
||||||
|
|
||||||
/// An error produced in legacy transparent address derivation
|
/// An error produced in legacy transparent address derivation
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
|
@ -42,6 +43,7 @@ pub enum SqliteClientError {
|
||||||
|
|
||||||
/// An error encountered in decoding a transparent address from its
|
/// An error encountered in decoding a transparent address from its
|
||||||
/// serialized form.
|
/// serialized form.
|
||||||
|
#[cfg(feature = "transparent-inputs")]
|
||||||
TransparentAddress(TransparentCodecError),
|
TransparentAddress(TransparentCodecError),
|
||||||
|
|
||||||
/// Wrapper for rusqlite errors.
|
/// Wrapper for rusqlite errors.
|
||||||
|
@ -116,7 +118,6 @@ impl error::Error for SqliteClientError {
|
||||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
match &self {
|
match &self {
|
||||||
SqliteClientError::InvalidMemo(e) => Some(e),
|
SqliteClientError::InvalidMemo(e) => Some(e),
|
||||||
SqliteClientError::Bech32DecodeError(Bech32DecodeError::Bech32Error(e)) => Some(e),
|
|
||||||
SqliteClientError::DbError(e) => Some(e),
|
SqliteClientError::DbError(e) => Some(e),
|
||||||
SqliteClientError::Io(e) => Some(e),
|
SqliteClientError::Io(e) => Some(e),
|
||||||
SqliteClientError::BalanceError(e) => Some(e),
|
SqliteClientError::BalanceError(e) => Some(e),
|
||||||
|
@ -136,9 +137,10 @@ impl fmt::Display for SqliteClientError {
|
||||||
SqliteClientError::InvalidNote => write!(f, "Invalid note"),
|
SqliteClientError::InvalidNote => write!(f, "Invalid note"),
|
||||||
SqliteClientError::RequestedRewindInvalid(h, r) =>
|
SqliteClientError::RequestedRewindInvalid(h, r) =>
|
||||||
write!(f, "A rewind must be either of less than {} blocks, or at least back to block {} for your wallet; the requested height was {}.", PRUNING_DEPTH, h, r),
|
write!(f, "A rewind must be either of less than {} blocks, or at least back to block {} for your wallet; the requested height was {}.", PRUNING_DEPTH, h, r),
|
||||||
SqliteClientError::Bech32DecodeError(e) => write!(f, "{}", e),
|
SqliteClientError::DecodingError(e) => write!(f, "{}", e),
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
SqliteClientError::HdwalletError(e) => write!(f, "{:?}", e),
|
SqliteClientError::HdwalletError(e) => write!(f, "{:?}", e),
|
||||||
|
#[cfg(feature = "transparent-inputs")]
|
||||||
SqliteClientError::TransparentAddress(e) => write!(f, "{}", e),
|
SqliteClientError::TransparentAddress(e) => write!(f, "{}", e),
|
||||||
SqliteClientError::TableNotEmpty => write!(f, "Table is not empty"),
|
SqliteClientError::TableNotEmpty => write!(f, "Table is not empty"),
|
||||||
SqliteClientError::DbError(e) => write!(f, "{}", e),
|
SqliteClientError::DbError(e) => write!(f, "{}", e),
|
||||||
|
@ -175,10 +177,9 @@ impl From<std::io::Error> for SqliteClientError {
|
||||||
SqliteClientError::Io(e)
|
SqliteClientError::Io(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<ParseError> for SqliteClientError {
|
||||||
impl From<Bech32DecodeError> for SqliteClientError {
|
fn from(e: ParseError) -> Self {
|
||||||
fn from(e: Bech32DecodeError) -> Self {
|
SqliteClientError::DecodingError(e)
|
||||||
SqliteClientError::Bech32DecodeError(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +196,7 @@ impl From<hdwallet::error::Error> for SqliteClientError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "transparent-inputs")]
|
||||||
impl From<TransparentCodecError> for SqliteClientError {
|
impl From<TransparentCodecError> for SqliteClientError {
|
||||||
fn from(e: TransparentCodecError) -> Self {
|
fn from(e: TransparentCodecError) -> Self {
|
||||||
SqliteClientError::TransparentAddress(e)
|
SqliteClientError::TransparentAddress(e)
|
||||||
|
|
|
@ -65,7 +65,7 @@ use zcash_client_backend::{
|
||||||
wallet::{Note, NoteId, ReceivedNote, Recipient, WalletTransparentOutput},
|
wallet::{Note, NoteId, ReceivedNote, Recipient, WalletTransparentOutput},
|
||||||
DecryptedOutput, PoolType, ShieldedProtocol, TransferType,
|
DecryptedOutput, PoolType, ShieldedProtocol, TransferType,
|
||||||
};
|
};
|
||||||
use zcash_keys::address::Address;
|
use zcash_keys::address::Receiver;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
block::BlockHash,
|
block::BlockHash,
|
||||||
consensus::{self, BlockHeight},
|
consensus::{self, BlockHeight},
|
||||||
|
@ -1063,11 +1063,22 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
for output in d_tx.sapling_outputs() {
|
for output in d_tx.sapling_outputs() {
|
||||||
match output.transfer_type() {
|
match output.transfer_type() {
|
||||||
TransferType::Outgoing => {
|
TransferType::Outgoing => {
|
||||||
//TODO: Recover the UA, if possible.
|
let recipient = {
|
||||||
let recipient = Recipient::Sapling(output.note().recipient());
|
let receiver = Receiver::Sapling(output.note().recipient());
|
||||||
|
let wallet_address = wallet::select_receiving_address(
|
||||||
|
&wdb.params,
|
||||||
|
wdb.conn.0,
|
||||||
|
*output.account(),
|
||||||
|
&receiver
|
||||||
|
)?.unwrap_or_else(||
|
||||||
|
receiver.to_zcash_address(wdb.params.network_type())
|
||||||
|
);
|
||||||
|
|
||||||
|
Recipient::External(wallet_address, PoolType::Shielded(ShieldedProtocol::Sapling))
|
||||||
|
};
|
||||||
|
|
||||||
wallet::put_sent_output(
|
wallet::put_sent_output(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
|
||||||
*output.account(),
|
*output.account(),
|
||||||
tx_ref,
|
tx_ref,
|
||||||
output.index(),
|
output.index(),
|
||||||
|
@ -1087,7 +1098,6 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
|
|
||||||
wallet::put_sent_output(
|
wallet::put_sent_output(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
|
||||||
*output.account(),
|
*output.account(),
|
||||||
tx_ref,
|
tx_ref,
|
||||||
output.index(),
|
output.index(),
|
||||||
|
@ -1102,14 +1112,22 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
if let Some(account_id) = funding_account {
|
if let Some(account_id) = funding_account {
|
||||||
let recipient = Recipient::InternalAccount {
|
let recipient = Recipient::InternalAccount {
|
||||||
receiving_account: *output.account(),
|
receiving_account: *output.account(),
|
||||||
// TODO: recover the actual UA, if possible
|
external_address: {
|
||||||
external_address: Some(Address::Sapling(output.note().recipient())),
|
let receiver = Receiver::Sapling(output.note().recipient());
|
||||||
|
Some(wallet::select_receiving_address(
|
||||||
|
&wdb.params,
|
||||||
|
wdb.conn.0,
|
||||||
|
*output.account(),
|
||||||
|
&receiver
|
||||||
|
)?.unwrap_or_else(||
|
||||||
|
receiver.to_zcash_address(wdb.params.network_type())
|
||||||
|
))
|
||||||
|
},
|
||||||
note: Note::Sapling(output.note().clone()),
|
note: Note::Sapling(output.note().clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
wallet::put_sent_output(
|
wallet::put_sent_output(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
|
||||||
account_id,
|
account_id,
|
||||||
tx_ref,
|
tx_ref,
|
||||||
output.index(),
|
output.index(),
|
||||||
|
@ -1126,20 +1144,22 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
for output in d_tx.orchard_outputs() {
|
for output in d_tx.orchard_outputs() {
|
||||||
match output.transfer_type() {
|
match output.transfer_type() {
|
||||||
TransferType::Outgoing => {
|
TransferType::Outgoing => {
|
||||||
// TODO: Recover the actual UA, if possible.
|
let recipient = {
|
||||||
let recipient = Recipient::Unified(
|
let receiver = Receiver::Orchard(output.note().recipient());
|
||||||
UnifiedAddress::from_receivers(
|
let wallet_address = wallet::select_receiving_address(
|
||||||
Some(output.note().recipient()),
|
&wdb.params,
|
||||||
None,
|
wdb.conn.0,
|
||||||
None,
|
*output.account(),
|
||||||
)
|
&receiver
|
||||||
.expect("UA has an Orchard receiver by construction."),
|
)?.unwrap_or_else(||
|
||||||
PoolType::Shielded(ShieldedProtocol::Orchard),
|
receiver.to_zcash_address(wdb.params.network_type())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Recipient::External(wallet_address, PoolType::Shielded(ShieldedProtocol::Orchard))
|
||||||
|
};
|
||||||
|
|
||||||
wallet::put_sent_output(
|
wallet::put_sent_output(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
|
||||||
*output.account(),
|
*output.account(),
|
||||||
tx_ref,
|
tx_ref,
|
||||||
output.index(),
|
output.index(),
|
||||||
|
@ -1159,7 +1179,6 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
|
|
||||||
wallet::put_sent_output(
|
wallet::put_sent_output(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
|
||||||
*output.account(),
|
*output.account(),
|
||||||
tx_ref,
|
tx_ref,
|
||||||
output.index(),
|
output.index(),
|
||||||
|
@ -1175,19 +1194,22 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
// Even if the recipient address is external, record the send as internal.
|
// Even if the recipient address is external, record the send as internal.
|
||||||
let recipient = Recipient::InternalAccount {
|
let recipient = Recipient::InternalAccount {
|
||||||
receiving_account: *output.account(),
|
receiving_account: *output.account(),
|
||||||
// TODO: recover the actual UA, if possible
|
external_address: {
|
||||||
external_address: Some(Address::Unified(
|
let receiver = Receiver::Orchard(output.note().recipient());
|
||||||
UnifiedAddress::from_receivers(
|
Some(wallet::select_receiving_address(
|
||||||
Some(output.note().recipient()),
|
&wdb.params,
|
||||||
None,
|
wdb.conn.0,
|
||||||
None,
|
*output.account(),
|
||||||
).expect("UA has an Orchard receiver by construction."))),
|
&receiver
|
||||||
|
)?.unwrap_or_else(||
|
||||||
|
receiver.to_zcash_address(wdb.params.network_type())
|
||||||
|
))
|
||||||
|
},
|
||||||
note: Note::Orchard(*output.note()),
|
note: Note::Orchard(*output.note()),
|
||||||
};
|
};
|
||||||
|
|
||||||
wallet::put_sent_output(
|
wallet::put_sent_output(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
|
||||||
account_id,
|
account_id,
|
||||||
tx_ref,
|
tx_ref,
|
||||||
output.index(),
|
output.index(),
|
||||||
|
@ -1240,13 +1262,29 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
if let Some(address) = txout.recipient_address() {
|
if let Some(address) = txout.recipient_address() {
|
||||||
|
let receiver = Receiver::Transparent(address);
|
||||||
|
|
||||||
|
#[cfg(feature = "transparent-inputs")]
|
||||||
|
let recipient_addr = wallet::select_receiving_address(
|
||||||
|
&wdb.params,
|
||||||
|
wdb.conn.0,
|
||||||
|
account_id,
|
||||||
|
&receiver
|
||||||
|
)?.unwrap_or_else(||
|
||||||
|
receiver.to_zcash_address(wdb.params.network_type())
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "transparent-inputs"))]
|
||||||
|
let recipient_addr = receiver.to_zcash_address(wdb.params.network_type());
|
||||||
|
|
||||||
|
let recipient = Recipient::External(recipient_addr, PoolType::Transparent);
|
||||||
|
|
||||||
wallet::put_sent_output(
|
wallet::put_sent_output(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
|
||||||
account_id,
|
account_id,
|
||||||
tx_ref,
|
tx_ref,
|
||||||
output_index,
|
output_index,
|
||||||
&Recipient::Transparent(address),
|
&recipient,
|
||||||
txout.value,
|
txout.value,
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
@ -1305,13 +1343,7 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
}
|
}
|
||||||
|
|
||||||
for output in sent_tx.outputs() {
|
for output in sent_tx.outputs() {
|
||||||
wallet::insert_sent_output(
|
wallet::insert_sent_output(wdb.conn.0, tx_ref, *sent_tx.account_id(), output)?;
|
||||||
wdb.conn.0,
|
|
||||||
&wdb.params,
|
|
||||||
tx_ref,
|
|
||||||
*sent_tx.account_id(),
|
|
||||||
output,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match output.recipient() {
|
match output.recipient() {
|
||||||
Recipient::InternalAccount {
|
Recipient::InternalAccount {
|
||||||
|
@ -1880,7 +1912,6 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(current_addr.is_some());
|
assert!(current_addr.is_some());
|
||||||
|
|
||||||
// TODO: Add Orchard
|
|
||||||
let addr2 = st
|
let addr2 = st
|
||||||
.wallet_mut()
|
.wallet_mut()
|
||||||
.get_next_available_address(account.account_id(), DEFAULT_UA_REQUEST)
|
.get_next_available_address(account.account_id(), DEFAULT_UA_REQUEST)
|
||||||
|
|
|
@ -76,12 +76,9 @@ use std::io::{self, Cursor};
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use zcash_keys::keys::{
|
|
||||||
AddressGenerationError, UnifiedAddressRequest, UnifiedIncomingViewingKey, UnifiedSpendingKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use zcash_address::ZcashAddress;
|
||||||
use zcash_client_backend::{
|
use zcash_client_backend::{
|
||||||
address::{Address, UnifiedAddress},
|
|
||||||
data_api::{
|
data_api::{
|
||||||
scanning::{ScanPriority, ScanRange},
|
scanning::{ScanPriority, ScanRange},
|
||||||
AccountBalance, AccountBirthday, AccountSource, BlockMetadata, Ratio,
|
AccountBalance, AccountBirthday, AccountSource, BlockMetadata, Ratio,
|
||||||
|
@ -92,6 +89,13 @@ use zcash_client_backend::{
|
||||||
wallet::{Note, NoteId, Recipient, WalletTx},
|
wallet::{Note, NoteId, Recipient, WalletTx},
|
||||||
PoolType, ShieldedProtocol,
|
PoolType, ShieldedProtocol,
|
||||||
};
|
};
|
||||||
|
use zcash_keys::{
|
||||||
|
address::{Address, Receiver, UnifiedAddress},
|
||||||
|
keys::{
|
||||||
|
AddressGenerationError, UnifiedAddressRequest, UnifiedIncomingViewingKey,
|
||||||
|
UnifiedSpendingKey,
|
||||||
|
},
|
||||||
|
};
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
block::BlockHash,
|
block::BlockHash,
|
||||||
consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters},
|
consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters},
|
||||||
|
@ -101,8 +105,8 @@ use zcash_primitives::{
|
||||||
components::{amount::NonNegativeAmount, Amount},
|
components::{amount::NonNegativeAmount, Amount},
|
||||||
Transaction, TransactionData, TxId,
|
Transaction, TransactionData, TxId,
|
||||||
},
|
},
|
||||||
zip32::{self, DiversifierIndex, Scope},
|
|
||||||
};
|
};
|
||||||
|
use zip32::{self, DiversifierIndex, Scope};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SqliteClientError,
|
error::SqliteClientError,
|
||||||
|
@ -2366,6 +2370,49 @@ pub(crate) fn put_tx_meta(
|
||||||
.map_err(SqliteClientError::from)
|
.map_err(SqliteClientError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the most likely wallet address that corresponds to the protocol-level receiver of a
|
||||||
|
/// note or UTXO.
|
||||||
|
pub(crate) fn select_receiving_address<P: consensus::Parameters>(
|
||||||
|
params: &P,
|
||||||
|
conn: &rusqlite::Connection,
|
||||||
|
account: AccountId,
|
||||||
|
receiver: &Receiver,
|
||||||
|
) -> Result<Option<ZcashAddress>, SqliteClientError> {
|
||||||
|
match receiver {
|
||||||
|
#[cfg(feature = "transparent-inputs")]
|
||||||
|
Receiver::Transparent(taddr) => conn
|
||||||
|
.query_row(
|
||||||
|
"SELECT address
|
||||||
|
FROM addresses
|
||||||
|
WHERE cached_transparent_receiver_address = :taddr",
|
||||||
|
named_params! {
|
||||||
|
":taddr": Address::Transparent(*taddr).encode(params)
|
||||||
|
},
|
||||||
|
|row| row.get::<_, String>(0),
|
||||||
|
)
|
||||||
|
.optional()?
|
||||||
|
.map(|addr_str| addr_str.parse::<ZcashAddress>())
|
||||||
|
.transpose()
|
||||||
|
.map_err(SqliteClientError::from),
|
||||||
|
receiver => {
|
||||||
|
// at present,
|
||||||
|
let mut stmt =
|
||||||
|
conn.prepare_cached("SELECT address FROM addresses WHERE account_id = :account")?;
|
||||||
|
|
||||||
|
let mut result = stmt.query(named_params! { ":account": account.0 })?;
|
||||||
|
while let Some(row) = result.next()? {
|
||||||
|
let addr_str = row.get::<_, String>(0)?;
|
||||||
|
let decoded = addr_str.parse::<ZcashAddress>()?;
|
||||||
|
if receiver.corresponds(&decoded) {
|
||||||
|
return Ok(Some(decoded));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts full transaction data into the database.
|
/// Inserts full transaction data into the database.
|
||||||
pub(crate) fn put_tx_data(
|
pub(crate) fn put_tx_data(
|
||||||
conn: &rusqlite::Connection,
|
conn: &rusqlite::Connection,
|
||||||
|
@ -2515,24 +2562,17 @@ pub(crate) fn put_legacy_transparent_utxo<P: consensus::Parameters>(
|
||||||
|
|
||||||
// A utility function for creation of parameters for use in `insert_sent_output`
|
// A utility function for creation of parameters for use in `insert_sent_output`
|
||||||
// and `put_sent_output`
|
// and `put_sent_output`
|
||||||
fn recipient_params<P: consensus::Parameters>(
|
fn recipient_params(
|
||||||
params: &P,
|
|
||||||
to: &Recipient<AccountId, Note>,
|
to: &Recipient<AccountId, Note>,
|
||||||
) -> (Option<String>, Option<AccountId>, PoolType) {
|
) -> (Option<String>, Option<AccountId>, PoolType) {
|
||||||
match to {
|
match to {
|
||||||
Recipient::Transparent(addr) => (Some(addr.encode(params)), None, PoolType::Transparent),
|
Recipient::External(addr, pool) => (Some(addr.encode()), None, *pool),
|
||||||
Recipient::Sapling(addr) => (
|
|
||||||
Some(addr.encode(params)),
|
|
||||||
None,
|
|
||||||
PoolType::Shielded(ShieldedProtocol::Sapling),
|
|
||||||
),
|
|
||||||
Recipient::Unified(addr, pool) => (Some(addr.encode(params)), None, *pool),
|
|
||||||
Recipient::InternalAccount {
|
Recipient::InternalAccount {
|
||||||
receiving_account,
|
receiving_account,
|
||||||
external_address,
|
external_address,
|
||||||
note,
|
note,
|
||||||
} => (
|
} => (
|
||||||
external_address.as_ref().map(|a| a.encode(params)),
|
external_address.as_ref().map(|a| a.encode()),
|
||||||
Some(*receiving_account),
|
Some(*receiving_account),
|
||||||
PoolType::Shielded(note.protocol()),
|
PoolType::Shielded(note.protocol()),
|
||||||
),
|
),
|
||||||
|
@ -2540,9 +2580,8 @@ fn recipient_params<P: consensus::Parameters>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Records information about a transaction output that your wallet created.
|
/// Records information about a transaction output that your wallet created.
|
||||||
pub(crate) fn insert_sent_output<P: consensus::Parameters>(
|
pub(crate) fn insert_sent_output(
|
||||||
conn: &rusqlite::Connection,
|
conn: &rusqlite::Connection,
|
||||||
params: &P,
|
|
||||||
tx_ref: i64,
|
tx_ref: i64,
|
||||||
from_account: AccountId,
|
from_account: AccountId,
|
||||||
output: &SentTransactionOutput<AccountId>,
|
output: &SentTransactionOutput<AccountId>,
|
||||||
|
@ -2556,7 +2595,7 @@ pub(crate) fn insert_sent_output<P: consensus::Parameters>(
|
||||||
:to_address, :to_account_id, :value, :memo)",
|
:to_address, :to_account_id, :value, :memo)",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (to_address, to_account_id, pool_type) = recipient_params(params, output.recipient());
|
let (to_address, to_account_id, pool_type) = recipient_params(output.recipient());
|
||||||
let sql_args = named_params![
|
let sql_args = named_params![
|
||||||
":tx": &tx_ref,
|
":tx": &tx_ref,
|
||||||
":output_pool": &pool_code(pool_type),
|
":output_pool": &pool_code(pool_type),
|
||||||
|
@ -2585,9 +2624,8 @@ pub(crate) fn insert_sent_output<P: consensus::Parameters>(
|
||||||
/// - If `recipient` is an internal account, `output_index` is an index into the Sapling outputs of
|
/// - If `recipient` is an internal account, `output_index` is an index into the Sapling outputs of
|
||||||
/// the transaction.
|
/// the transaction.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn put_sent_output<P: consensus::Parameters>(
|
pub(crate) fn put_sent_output(
|
||||||
conn: &rusqlite::Connection,
|
conn: &rusqlite::Connection,
|
||||||
params: &P,
|
|
||||||
from_account: AccountId,
|
from_account: AccountId,
|
||||||
tx_ref: i64,
|
tx_ref: i64,
|
||||||
output_index: usize,
|
output_index: usize,
|
||||||
|
@ -2610,7 +2648,7 @@ pub(crate) fn put_sent_output<P: consensus::Parameters>(
|
||||||
memo = IFNULL(:memo, memo)",
|
memo = IFNULL(:memo, memo)",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (to_address, to_account_id, pool_type) = recipient_params(params, recipient);
|
let (to_address, to_account_id, pool_type) = recipient_params(recipient);
|
||||||
let sql_args = named_params![
|
let sql_args = named_params![
|
||||||
":tx": &tx_ref,
|
":tx": &tx_ref,
|
||||||
":output_pool": &pool_code(pool_type),
|
":output_pool": &pool_code(pool_type),
|
||||||
|
|
|
@ -134,11 +134,10 @@ fn sqlite_client_error_to_wallet_migration_error(e: SqliteClientError) -> Wallet
|
||||||
SqliteClientError::InvalidNote => {
|
SqliteClientError::InvalidNote => {
|
||||||
WalletMigrationError::CorruptedData("invalid note".into())
|
WalletMigrationError::CorruptedData("invalid note".into())
|
||||||
}
|
}
|
||||||
SqliteClientError::Bech32DecodeError(e) => {
|
SqliteClientError::DecodingError(e) => WalletMigrationError::CorruptedData(e.to_string()),
|
||||||
WalletMigrationError::CorruptedData(e.to_string())
|
|
||||||
}
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
SqliteClientError::HdwalletError(e) => WalletMigrationError::CorruptedData(e.to_string()),
|
SqliteClientError::HdwalletError(e) => WalletMigrationError::CorruptedData(e.to_string()),
|
||||||
|
#[cfg(feature = "transparent-inputs")]
|
||||||
SqliteClientError::TransparentAddress(e) => {
|
SqliteClientError::TransparentAddress(e) => {
|
||||||
WalletMigrationError::CorruptedData(e.to_string())
|
WalletMigrationError::CorruptedData(e.to_string())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@ and this library adheres to Rust's notion of
|
||||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- `zcash_keys::address::Address::try_from_zcash_address`
|
||||||
|
- `zcash_keys::address::Receiver`
|
||||||
|
|
||||||
## [0.2.0] - 2024-03-25
|
## [0.2.0] - 2024-03-25
|
||||||
|
|
||||||
|
|
|
@ -233,6 +233,54 @@ impl UnifiedAddress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An enumeration of protocol-level receiver types. While these correspond to unified address
|
||||||
|
/// receiver
|
||||||
|
pub enum Receiver {
|
||||||
|
#[cfg(feature = "orchard")]
|
||||||
|
Orchard(orchard::Address),
|
||||||
|
#[cfg(feature = "sapling")]
|
||||||
|
Sapling(PaymentAddress),
|
||||||
|
Transparent(TransparentAddress),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Receiver {
|
||||||
|
/// Converts this receiver to a [`ZcashAddress`] for the given network.
|
||||||
|
///
|
||||||
|
/// This conversion function selects the least-capable address format possible; this means that
|
||||||
|
/// Orchard receivers will be rendered as Unified addresses, Sapling receivers will be rendered
|
||||||
|
/// as bare Sapling addresses, and Transparent receivers will be rendered as taddrs.
|
||||||
|
pub fn to_zcash_address(&self, net: NetworkType) -> ZcashAddress {
|
||||||
|
match self {
|
||||||
|
Receiver::Orchard(addr) => {
|
||||||
|
let receiver = unified::Receiver::Orchard(addr.to_raw_address_bytes());
|
||||||
|
let ua = unified::Address::try_from_items(vec![receiver])
|
||||||
|
.expect("A unified address may contain a single Orchard receiver.");
|
||||||
|
ZcashAddress::from_unified(net, ua)
|
||||||
|
}
|
||||||
|
Receiver::Sapling(addr) => ZcashAddress::from_sapling(net, addr.to_bytes()),
|
||||||
|
Receiver::Transparent(TransparentAddress::PublicKeyHash(data)) => {
|
||||||
|
ZcashAddress::from_transparent_p2pkh(net, *data)
|
||||||
|
}
|
||||||
|
Receiver::Transparent(TransparentAddress::ScriptHash(data)) => {
|
||||||
|
ZcashAddress::from_transparent_p2sh(net, *data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn corresponds(&self, addr: &ZcashAddress) -> bool {
|
||||||
|
addr.matches_receiver(&match self {
|
||||||
|
Receiver::Orchard(addr) => unified::Receiver::Orchard(addr.to_raw_address_bytes()),
|
||||||
|
Receiver::Sapling(addr) => unified::Receiver::Sapling(addr.to_bytes()),
|
||||||
|
Receiver::Transparent(TransparentAddress::PublicKeyHash(data)) => {
|
||||||
|
unified::Receiver::P2pkh(*data)
|
||||||
|
}
|
||||||
|
Receiver::Transparent(TransparentAddress::ScriptHash(data)) => {
|
||||||
|
unified::Receiver::P2sh(*data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An address that funds can be sent to.
|
/// An address that funds can be sent to.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Address {
|
pub enum Address {
|
||||||
|
@ -291,8 +339,14 @@ impl TryFromRawAddress for Address {
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
pub fn decode<P: consensus::Parameters>(params: &P, s: &str) -> Option<Self> {
|
pub fn decode<P: consensus::Parameters>(params: &P, s: &str) -> Option<Self> {
|
||||||
let addr = ZcashAddress::try_from_encoded(s).ok()?;
|
Self::try_from_zcash_address(params, s.parse::<ZcashAddress>().ok()?).ok()
|
||||||
addr.convert_if_network(params.network_type()).ok()
|
}
|
||||||
|
|
||||||
|
pub fn try_from_zcash_address<P: consensus::Parameters>(
|
||||||
|
params: &P,
|
||||||
|
zaddr: ZcashAddress,
|
||||||
|
) -> Result<Self, ConversionError<&'static str>> {
|
||||||
|
zaddr.convert_if_network(params.network_type())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_zcash_address<P: consensus::Parameters>(&self, params: &P) -> ZcashAddress {
|
pub fn to_zcash_address<P: consensus::Parameters>(&self, params: &P) -> ZcashAddress {
|
||||||
|
|
Loading…
Reference in New Issue