use std::{
collections::HashMap,
io::{self, Read, Write},
};
use byteorder::{LittleEndian, ReadBytesExt};
use orchard::tree::MerkleHashOrchard;
use prost::Message;
use incrementalmerkletree::{Address, Hashable, Level, Position, witness::IncrementalWitness};
use shardtree::{
LocatedPrunableTree, ShardTree,
store::{Checkpoint, ShardStore as _, memory::MemoryShardStore},
};
use zcash_client_backend::{
proto::compact_formats::CompactBlock, serialization::shardtree::read_shard,
};
use zcash_encoding::{CompactSize, Optional, Vector};
use zcash_primitives::{
consensus::BlockHeight,
memo::{Memo, MemoBytes},
merkle_tree::{HashSer, read_commitment_tree, read_incremental_witness},
transaction::TxId,
};
use zingo_status::confirmation_status::ConfirmationStatus;
use super::{keys::legacy::WalletCapability, traits::ReadableWriteable};
#[derive(Clone, PartialEq)]
pub struct BlockData {
pub(crate) ecb: Vec<u8>,
pub height: u64,
}
impl BlockData {
pub(crate) fn new_with(height: u64, hash: &[u8]) -> Self {
let hash = hash.iter().copied().rev().collect::<Vec<_>>();
let cb = CompactBlock {
hash,
..Default::default()
};
let mut ecb = vec![];
cb.encode(&mut ecb).unwrap();
Self { ecb, height }
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let height = reader.read_i32::<LittleEndian>()? as u64;
let mut hash_bytes = [0; 32];
reader.read_exact(&mut hash_bytes)?;
hash_bytes.reverse();
let tree: sapling_crypto::CommitmentTree = read_commitment_tree(&mut reader)?;
let _tree = if tree.size() == 0 { None } else { Some(tree) };
let version = reader.read_u64::<LittleEndian>()?;
let ecb = if version <= 11 {
vec![]
} else {
Vector::read(&mut reader, |r| r.read_u8())?
};
if ecb.is_empty() {
Ok(BlockData::new_with(height, &hash_bytes))
} else {
Ok(BlockData { ecb, height })
}
}
}
pub struct TxMap {
pub transaction_records_by_id: TransactionRecordsById,
}
impl TxMap {
pub fn serialized_version() -> u64 {
23
}
pub fn read_old<R: Read>(
mut reader: R,
wallet_capability: &WalletCapability,
) -> io::Result<Self> {
let mut witness_trees = wallet_capability.get_trees_witness_trees();
let mut old_inc_witnesses = if witness_trees.is_some() {
Some((Vec::new(), Vec::new()))
} else {
None
};
let txs = Vector::read_collected_mut(&mut reader, |r| {
let mut txid_bytes = [0u8; 32];
r.read_exact(&mut txid_bytes)?;
Ok((
TxId::from_bytes(txid_bytes),
TransactionRecord::read(r, (wallet_capability, old_inc_witnesses.as_mut()))
.unwrap(),
))
})?;
let map = TransactionRecordsById::from_map(txs);
if let Some((mut old_sap_wits, mut old_orch_wits)) = old_inc_witnesses {
old_sap_wits.sort_by(|(_w1, height1), (_w2, height2)| height1.cmp(height2));
let sap_tree = &mut witness_trees.as_mut().unwrap().witness_tree_sapling;
for (sap_wit, height) in old_sap_wits {
sap_tree
.insert_witness_nodes(sap_wit, height - 1)
.expect("infallible");
sap_tree.checkpoint(height).expect("infallible");
}
old_orch_wits.sort_by(|(_w1, height1), (_w2, height2)| height1.cmp(height2));
let orch_tree = &mut witness_trees.as_mut().unwrap().witness_tree_orchard;
for (orch_wit, height) in old_orch_wits {
orch_tree
.insert_witness_nodes(orch_wit, height - 1)
.expect("infallible");
orch_tree.checkpoint(height).expect("infallible");
}
}
Ok(Self {
transaction_records_by_id: map,
})
}
#[allow(unused_assignments)]
pub fn read<R: Read>(mut reader: R, wallet_capability: &WalletCapability) -> io::Result<Self> {
let version = reader.read_u64::<LittleEndian>()?;
if version > Self::serialized_version() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Can't read wallettxns because of incorrect version",
));
}
let mut witness_trees = wallet_capability.get_trees_witness_trees();
let mut old_inc_witnesses = if witness_trees.is_some() {
Some((Vec::new(), Vec::new()))
} else {
None
};
let map: HashMap<_, _> = Vector::read_collected_mut(&mut reader, |r| {
let mut txid_bytes = [0u8; 32];
r.read_exact(&mut txid_bytes)?;
Ok((
TxId::from_bytes(txid_bytes),
TransactionRecord::read(r, (wallet_capability, old_inc_witnesses.as_mut()))?,
))
})?;
let _mempool: Vec<(TxId, TransactionRecord)> = if version <= 20 {
Vector::read_collected_mut(&mut reader, |r| {
let mut txid_bytes = [0u8; 32];
r.read_exact(&mut txid_bytes)?;
let transaction_metadata =
TransactionRecord::read(r, (wallet_capability, old_inc_witnesses.as_mut()))?;
Ok((TxId::from_bytes(txid_bytes), transaction_metadata))
})?
} else {
vec![]
};
if version >= 22 {
witness_trees = Optional::read(reader, |r| WitnessTrees::read(r))?;
} else if let Some((mut old_sap_wits, mut old_orch_wits)) = old_inc_witnesses {
old_sap_wits.sort_by(|(_w1, height1), (_w2, height2)| height1.cmp(height2));
let sap_tree = &mut witness_trees.as_mut().unwrap().witness_tree_sapling;
for (sap_wit, height) in old_sap_wits {
sap_tree
.insert_witness_nodes(sap_wit, height - 1)
.expect("infallible");
sap_tree.checkpoint(height).expect("infallible");
}
old_orch_wits.sort_by(|(_w1, height1), (_w2, height2)| height1.cmp(height2));
let orch_tree = &mut witness_trees.as_mut().unwrap().witness_tree_orchard;
for (orch_wit, height) in old_orch_wits {
orch_tree
.insert_witness_nodes(orch_wit, height - 1)
.expect("infallible");
orch_tree.checkpoint(height).expect("infallible");
}
};
Ok(Self {
transaction_records_by_id: TransactionRecordsById::from_map(map),
})
}
}
pub struct TransactionRecordsById(pub HashMap<TxId, TransactionRecord>);
impl TransactionRecordsById {
pub fn from_map(map: HashMap<TxId, TransactionRecord>) -> Self {
TransactionRecordsById(map)
}
}
#[allow(dead_code)]
pub struct TransactionRecord {
pub status: zingo_status::confirmation_status::ConfirmationStatus,
pub datetime: u64,
pub txid: TxId,
pub spent_sapling_nullifiers: Vec<sapling_crypto::Nullifier>,
pub spent_orchard_nullifiers: Vec<orchard::note::Nullifier>,
pub sapling_notes: Vec<SaplingNote>,
pub orchard_notes: Vec<OrchardNote>,
pub transparent_outputs: Vec<TransparentOutput>,
pub total_transparent_value_spent: u64,
pub total_sapling_value_spent: u64,
pub total_orchard_value_spent: u64,
pub outgoing_tx_data: Vec<OutgoingTxData>,
pub price: Option<f64>,
}
impl TransactionRecord {
#[allow(clippy::type_complexity)]
pub fn read<R: Read>(
mut reader: R,
(wallet_capability, mut trees): (
&WalletCapability,
Option<&mut (
Vec<(
IncrementalWitness<sapling_crypto::Node, COMMITMENT_TREE_LEVELS>,
BlockHeight,
)>,
Vec<(
IncrementalWitness<MerkleHashOrchard, COMMITMENT_TREE_LEVELS>,
BlockHeight,
)>,
)>,
),
) -> io::Result<Self> {
let version = reader.read_u64::<LittleEndian>()?;
let block = BlockHeight::from_u32(reader.read_i32::<LittleEndian>()? as u32);
let pending = if version <= 20 {
false
} else {
reader.read_u8()? == 1
};
let datetime = if version >= 4 {
reader.read_u64::<LittleEndian>()?
} else {
0
};
let mut transaction_id_bytes = [0u8; 32];
reader.read_exact(&mut transaction_id_bytes)?;
let transaction_id = TxId::from_bytes(transaction_id_bytes);
let sapling_notes = zcash_encoding::Vector::read_collected_mut(&mut reader, |r| {
SaplingNote::read(r, (wallet_capability, trees.as_mut().map(|t| &mut t.0)))
})?;
let orchard_notes = if version > 22 {
zcash_encoding::Vector::read_collected_mut(&mut reader, |r| {
OrchardNote::read(r, (wallet_capability, trees.as_mut().map(|t| &mut t.1)))
})?
} else {
vec![]
};
let utxos = zcash_encoding::Vector::read(&mut reader, |r| TransparentOutput::read(r))?;
let total_transparent_value_spent = reader.read_u64::<LittleEndian>()?;
let total_sapling_value_spent = reader.read_u64::<LittleEndian>()?;
let total_orchard_value_spent = if version >= 22 {
reader.read_u64::<LittleEndian>()?
} else {
0
};
let outgoing_metadata = match version {
..24 => zcash_encoding::Vector::read(&mut reader, |r| OutgoingTxData::read_old(r))?,
24.. => zcash_encoding::Vector::read(&mut reader, |r| OutgoingTxData::read(r))?,
};
let _full_tx_scanned = reader.read_u8()? > 0;
let zec_price = if version <= 4 {
None
} else {
zcash_encoding::Optional::read(&mut reader, |r| r.read_f64::<LittleEndian>())?
};
let spent_sapling_nullifiers = if version <= 5 {
vec![]
} else {
zcash_encoding::Vector::read(&mut reader, |r| {
let mut n = [0u8; 32];
r.read_exact(&mut n)?;
Ok(sapling_crypto::Nullifier(n))
})?
};
let spent_orchard_nullifiers = if version <= 21 {
vec![]
} else {
zcash_encoding::Vector::read(&mut reader, |r| {
let mut n = [0u8; 32];
r.read_exact(&mut n)?;
Ok(orchard::note::Nullifier::from_bytes(&n).unwrap())
})?
};
let status = zingo_status::confirmation_status::ConfirmationStatus::from_blockheight_and_pending_bool(block, pending);
Ok(Self {
status,
datetime,
txid: transaction_id,
sapling_notes,
orchard_notes,
transparent_outputs: utxos,
spent_sapling_nullifiers,
spent_orchard_nullifiers,
total_transparent_value_spent,
total_sapling_value_spent,
total_orchard_value_spent,
outgoing_tx_data: outgoing_metadata,
price: zec_price,
})
}
}
impl ReadableWriteable<(sapling_crypto::Diversifier, &WalletCapability)> for sapling_crypto::Note {
const VERSION: u8 = 1;
fn read<R: Read>(
mut reader: R,
(diversifier, wallet_capability): (sapling_crypto::Diversifier, &WalletCapability),
) -> io::Result<Self> {
let _version = Self::get_version(&mut reader)?;
let value = reader.read_u64::<LittleEndian>()?;
let rseed = read_sapling_rseed(&mut reader)?;
Ok(
sapling_crypto::zip32::DiversifiableFullViewingKey::try_from(
&wallet_capability.unified_key_store,
)
.expect("to get an fvk from the unified key store")
.fvk()
.vk
.to_payment_address(diversifier)
.unwrap()
.create_note(sapling_crypto::value::NoteValue::from_raw(value), rseed),
)
}
fn write<W: Write>(&self, mut _writer: W, _input: ()) -> io::Result<()> {
unimplemented!()
}
}
fn read_sapling_rseed<R: Read>(mut reader: R) -> io::Result<sapling_crypto::Rseed> {
let note_type = reader.read_u8()?;
let mut r_bytes: [u8; 32] = [0; 32];
reader.read_exact(&mut r_bytes)?;
let r = match note_type {
1 => sapling_crypto::Rseed::BeforeZip212(jubjub::Fr::from_bytes(&r_bytes).unwrap()),
2 => sapling_crypto::Rseed::AfterZip212(r_bytes),
_ => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Bad note type")),
};
Ok(r)
}
impl ReadableWriteable<(orchard::keys::Diversifier, &WalletCapability)> for orchard::note::Note {
const VERSION: u8 = 1;
fn read<R: Read>(
mut reader: R,
(diversifier, wallet_capability): (orchard::keys::Diversifier, &WalletCapability),
) -> io::Result<Self> {
let _version = Self::get_version(&mut reader)?;
let value = reader.read_u64::<LittleEndian>()?;
let mut nullifier_bytes = [0; 32];
reader.read_exact(&mut nullifier_bytes)?;
let rho_nullifier = Option::from(orchard::note::Rho::from_bytes(&nullifier_bytes))
.ok_or(io::Error::new(io::ErrorKind::InvalidInput, "Bad Nullifier"))?;
let mut random_seed_bytes = [0; 32];
reader.read_exact(&mut random_seed_bytes)?;
let random_seed = Option::from(orchard::note::RandomSeed::from_bytes(
random_seed_bytes,
&rho_nullifier,
))
.ok_or(io::Error::new(
io::ErrorKind::InvalidInput,
"Nullifier not for note",
))?;
let fvk = orchard::keys::FullViewingKey::try_from(&wallet_capability.unified_key_store)
.expect("to get an fvk from the unified key store");
Option::from(orchard::note::Note::from_parts(
fvk.address(diversifier, orchard::keys::Scope::External),
orchard::value::NoteValue::from_raw(value),
rho_nullifier,
random_seed,
))
.ok_or(io::Error::new(io::ErrorKind::InvalidInput, "Invalid note"))
}
fn write<W: Write>(&self, mut _writer: W, _input: ()) -> io::Result<()> {
unimplemented!()
}
}
#[derive(Clone, PartialEq)]
pub struct TransparentOutput {
pub address: String,
pub txid: TxId,
pub output_index: u64,
pub script: Vec<u8>,
pub value: u64,
spend: Option<(TxId, ConfirmationStatus)>,
pub is_coinbase: bool,
}
impl TransparentOutput {
pub fn read<R: std::io::Read>(mut reader: R) -> std::io::Result<Self> {
let version = reader.read_u64::<byteorder::LittleEndian>()?;
let address_len = reader.read_i32::<byteorder::LittleEndian>()?;
let mut address_bytes = vec![0; address_len as usize];
reader.read_exact(&mut address_bytes)?;
let address = String::from_utf8(address_bytes).unwrap();
assert_eq!(address.chars().take(1).collect::<Vec<char>>()[0], 't');
let mut transaction_id_bytes = [0; 32];
reader.read_exact(&mut transaction_id_bytes)?;
let transaction_id = TxId::from_bytes(transaction_id_bytes);
let output_index = reader.read_u64::<byteorder::LittleEndian>()?;
let value = reader.read_u64::<byteorder::LittleEndian>()?;
let _height = reader.read_i32::<byteorder::LittleEndian>()?;
let script = zcash_encoding::Vector::read(&mut reader, |r| {
let mut byte = [0; 1];
r.read_exact(&mut byte)?;
Ok(byte[0])
})?;
let spent = zcash_encoding::Optional::read(&mut reader, |r| {
let mut transaction_bytes = [0u8; 32];
r.read_exact(&mut transaction_bytes)?;
Ok(TxId::from_bytes(transaction_bytes))
})?;
let spent_at_height = if version <= 1 {
None
} else {
zcash_encoding::Optional::read(&mut reader, |r| {
r.read_i32::<byteorder::LittleEndian>()
})?
};
let _pending_spent = if version == 3 {
zcash_encoding::Optional::read(&mut reader, |r| {
let mut transaction_bytes = [0u8; 32];
r.read_exact(&mut transaction_bytes)?;
let height = r.read_u32::<byteorder::LittleEndian>()?;
Ok((TxId::from_bytes(transaction_bytes), height))
})?
} else {
None
};
let spent_tuple: Option<(TxId, u32)> = if let Some(txid) = spent {
if let Some(height) = spent_at_height {
Some((txid, height as u32))
} else {
Some((txid, 0))
}
} else {
None
};
let spend =
spent_tuple.map(|(txid, height)| (txid, ConfirmationStatus::Confirmed(height.into())));
let is_coinbase = if version >= 5 {
reader.read_u8()? != 0
} else {
false
};
Ok(TransparentOutput {
address,
txid: transaction_id,
output_index,
script,
value,
spend,
is_coinbase,
})
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub struct SaplingNote {
pub diversifier: sapling_crypto::Diversifier,
pub sapling_crypto_note: sapling_crypto::Note,
pub(crate) witnessed_position: Option<Position>,
pub(crate) output_index: Option<u32>,
pub nullifier: Option<sapling_crypto::Nullifier>,
spend: Option<(TxId, ConfirmationStatus)>,
pub memo: Option<Memo>,
pub is_change: bool,
pub have_spending_key: bool,
}
impl
ReadableWriteable<(
&WalletCapability,
Option<
&mut Vec<(
IncrementalWitness<sapling_crypto::Node, COMMITMENT_TREE_LEVELS>,
BlockHeight,
)>,
>,
)> for SaplingNote
{
const VERSION: u8 = 5;
fn read<R: Read>(
mut reader: R,
(wallet_capability, inc_wit_vec): (
&WalletCapability,
Option<
&mut Vec<(
IncrementalWitness<sapling_crypto::Node, COMMITMENT_TREE_LEVELS>,
BlockHeight,
)>,
>,
),
) -> io::Result<Self> {
let external_version = Self::get_version(&mut reader)?;
if external_version < 2 {
let mut discarded_bytes = vec![0u8; 169];
reader
.read_exact(&mut discarded_bytes)
.expect("To not used this data.");
}
let mut diversifier_bytes = [0u8; 11];
reader.read_exact(&mut diversifier_bytes)?;
let diversifier = sapling_crypto::Diversifier(diversifier_bytes);
let note = sapling_crypto::Note::read(&mut reader, (diversifier, wallet_capability))?;
let witnessed_position = match external_version {
5.. => Optional::read(&mut reader, <R>::read_u64::<LittleEndian>)?.map(Position::from),
4 => Some(Position::from(reader.read_u64::<LittleEndian>()?)),
..4 => {
let witnesses_vec = Vector::read(&mut reader, |r| read_incremental_witness(r))?;
let top_height = reader.read_u64::<LittleEndian>()?;
let witnesses =
WitnessCache::<sapling_crypto::Node>::new(witnesses_vec, top_height);
let pos = witnesses.last().map(|w| w.witnessed_position());
for (i, witness) in witnesses.witnesses.into_iter().rev().enumerate().rev() {
let height = BlockHeight::from(top_height as u32 - i as u32);
if let Some(&mut ref mut wits) = inc_wit_vec {
wits.push((witness, height));
}
}
pos
}
};
let read_nullifier = |r: &mut R| {
let mut nullifier = [0u8; 32];
r.read_exact(&mut nullifier)?;
Ok(sapling_crypto::Nullifier::from_slice(&nullifier).unwrap())
};
let nullifier = match external_version {
5.. => Optional::read(&mut reader, read_nullifier)?,
..5 => Some(read_nullifier(&mut reader)?),
};
let spend = Optional::read(&mut reader, |r| {
let mut transaction_id_bytes = [0u8; 32];
r.read_exact(&mut transaction_id_bytes)?;
let status = match external_version {
5.. => ReadableWriteable::read(r, ()),
..5 => {
let height = r.read_u32::<LittleEndian>()?;
Ok(ConfirmationStatus::Confirmed(BlockHeight::from_u32(height)))
}
}?;
Ok((TxId::from_bytes(transaction_id_bytes), status))
})?;
if external_version < 3 {
let _pending_spent = {
Optional::read(&mut reader, |r| {
let mut transaction_bytes = [0u8; 32];
r.read_exact(&mut transaction_bytes)?;
let height = r.read_u32::<LittleEndian>()?;
Ok((TxId::from_bytes(transaction_bytes), height))
})?
};
}
let memo = Optional::read(&mut reader, |r| {
let mut memo_bytes = [0u8; 512];
r.read_exact(&mut memo_bytes)?;
match MemoBytes::from_bytes(&memo_bytes) {
Ok(mb) => match Memo::try_from(mb.clone()) {
Ok(m) => Ok(m),
Err(_) => Ok(Memo::Future(mb)),
},
Err(e) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Couldn't create memo: {}", e),
)),
}
})?;
let is_change: bool = reader.read_u8()? > 0;
let have_spending_key = reader.read_u8()? > 0;
let output_index = if external_version >= 4 {
match reader.read_u32::<LittleEndian>()? {
u32::MAX => None,
otherwise => Some(otherwise),
}
} else {
None
};
Ok(Self {
diversifier,
sapling_crypto_note: note,
witnessed_position,
nullifier,
spend,
memo,
is_change,
have_spending_key,
output_index,
})
}
fn write<W: Write>(&self, mut _writer: W, _input: ()) -> io::Result<()> {
unimplemented!()
}
}
#[derive(Clone, PartialEq)]
pub struct OrchardNote {
pub diversifier: orchard::keys::Diversifier,
pub orchard_crypto_note: orchard::note::Note,
pub witnessed_position: Option<Position>,
pub(crate) output_index: Option<u32>,
pub(crate) nullifier: Option<orchard::note::Nullifier>,
spend: Option<(TxId, ConfirmationStatus)>,
pub memo: Option<Memo>,
pub is_change: bool,
pub have_spending_key: bool,
}
impl
ReadableWriteable<(
&WalletCapability,
Option<
&mut Vec<(
IncrementalWitness<MerkleHashOrchard, COMMITMENT_TREE_LEVELS>,
BlockHeight,
)>,
>,
)> for OrchardNote
{
const VERSION: u8 = 5;
fn read<R: Read>(
mut reader: R,
(wallet_capability, inc_wit_vec): (
&WalletCapability,
Option<
&mut Vec<(
IncrementalWitness<MerkleHashOrchard, COMMITMENT_TREE_LEVELS>,
BlockHeight,
)>,
>,
),
) -> io::Result<Self> {
let external_version = Self::get_version(&mut reader)?;
if external_version < 2 {
let mut discarded_bytes = vec![0u8; 96];
reader
.read_exact(&mut discarded_bytes)
.expect("To not used this data.");
}
let mut diversifier_bytes = [0u8; 11];
reader.read_exact(&mut diversifier_bytes)?;
let diversifier = orchard::keys::Diversifier::from_bytes(diversifier_bytes);
let note = orchard::Note::read(&mut reader, (diversifier, wallet_capability))?;
let witnessed_position = match external_version {
5.. => Optional::read(&mut reader, <R>::read_u64::<LittleEndian>)?.map(Position::from),
4 => Some(Position::from(reader.read_u64::<LittleEndian>()?)),
..4 => {
let witnesses_vec = Vector::read(&mut reader, |r| read_incremental_witness(r))?;
let top_height = reader.read_u64::<LittleEndian>()?;
let witnesses = WitnessCache::<MerkleHashOrchard>::new(witnesses_vec, top_height);
let pos = witnesses.last().map(|w| w.witnessed_position());
for (i, witness) in witnesses.witnesses.into_iter().rev().enumerate().rev() {
let height = BlockHeight::from(top_height as u32 - i as u32);
if let Some(&mut ref mut wits) = inc_wit_vec {
wits.push((witness, height));
}
}
pos
}
};
let read_nullifier = |r: &mut R| {
let mut nullifier = [0u8; 32];
r.read_exact(&mut nullifier)?;
Ok(orchard::note::Nullifier::from_bytes(&nullifier).unwrap())
};
let nullifier = match external_version {
5.. => Optional::read(&mut reader, read_nullifier)?,
..5 => Some(read_nullifier(&mut reader)?),
};
let spend = Optional::read(&mut reader, |r| {
let mut transaction_id_bytes = [0u8; 32];
r.read_exact(&mut transaction_id_bytes)?;
let status = match external_version {
5.. => ReadableWriteable::read(r, ()),
..5 => {
let height = r.read_u32::<LittleEndian>()?;
Ok(ConfirmationStatus::Confirmed(BlockHeight::from_u32(height)))
}
}?;
Ok((TxId::from_bytes(transaction_id_bytes), status))
})?;
if external_version < 3 {
let _pending_spent = {
Optional::read(&mut reader, |r| {
let mut transaction_bytes = [0u8; 32];
r.read_exact(&mut transaction_bytes)?;
let height = r.read_u32::<LittleEndian>()?;
Ok((TxId::from_bytes(transaction_bytes), height))
})?
};
}
let memo = Optional::read(&mut reader, |r| {
let mut memo_bytes = [0u8; 512];
r.read_exact(&mut memo_bytes)?;
match MemoBytes::from_bytes(&memo_bytes) {
Ok(mb) => match Memo::try_from(mb.clone()) {
Ok(m) => Ok(m),
Err(_) => Ok(Memo::Future(mb)),
},
Err(e) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Couldn't create memo: {}", e),
)),
}
})?;
let is_change: bool = reader.read_u8()? > 0;
let have_spending_key = reader.read_u8()? > 0;
let output_index = if external_version >= 4 {
match reader.read_u32::<LittleEndian>()? {
u32::MAX => None,
otherwise => Some(otherwise),
}
} else {
None
};
Ok(Self {
diversifier,
orchard_crypto_note: note,
witnessed_position,
nullifier,
spend,
memo,
is_change,
have_spending_key,
output_index,
})
}
fn write<W: Write>(&self, mut _writer: W, _input: ()) -> io::Result<()> {
unimplemented!()
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub struct OutgoingTxData {
pub recipient_address: String,
pub value: u64,
pub memo: Memo,
pub recipient_ua: Option<String>,
pub output_index: Option<u64>,
}
impl OutgoingTxData {
pub fn read_old<R: Read>(mut reader: R) -> io::Result<Self> {
let address_len = reader.read_u64::<LittleEndian>()?;
let mut address_bytes = vec![0; address_len as usize];
reader.read_exact(&mut address_bytes)?;
let address = String::from_utf8(address_bytes).unwrap();
let value = reader.read_u64::<LittleEndian>()?;
let mut memo_bytes = [0u8; 512];
reader.read_exact(&mut memo_bytes)?;
let memo = match MemoBytes::from_bytes(&memo_bytes) {
Ok(mb) => match Memo::try_from(mb.clone()) {
Ok(m) => Ok(m),
Err(_) => Ok(Memo::Future(mb)),
},
Err(e) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Couldn't create memo: {}", e),
)),
}?;
Ok(OutgoingTxData {
recipient_address: address,
value,
memo,
recipient_ua: None,
output_index: None,
})
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let _external_version = CompactSize::read(&mut reader)?;
let address_len = reader.read_u64::<LittleEndian>()?;
let mut address_bytes = vec![0; address_len as usize];
reader.read_exact(&mut address_bytes)?;
let address = String::from_utf8(address_bytes).unwrap();
let value = reader.read_u64::<LittleEndian>()?;
let mut memo_bytes = [0u8; 512];
reader.read_exact(&mut memo_bytes)?;
let memo = match MemoBytes::from_bytes(&memo_bytes) {
Ok(mb) => match Memo::try_from(mb.clone()) {
Ok(m) => Ok(m),
Err(_) => Ok(Memo::Future(mb)),
},
Err(e) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Couldn't create memo: {}", e),
)),
}?;
let output_index = Optional::read(&mut reader, CompactSize::read)?;
Ok(OutgoingTxData {
recipient_address: address,
value,
memo,
recipient_ua: None,
output_index,
})
}
}
pub const COMMITMENT_TREE_LEVELS: u8 = 32;
pub const MAX_SHARD_LEVEL: u8 = 16;
pub const MAX_REORG: usize = 100;
#[derive(Debug)]
pub struct WitnessTrees {
pub witness_tree_sapling: ShardTree<SapStore, COMMITMENT_TREE_LEVELS, MAX_SHARD_LEVEL>,
pub witness_tree_orchard: ShardTree<OrchStore, COMMITMENT_TREE_LEVELS, MAX_SHARD_LEVEL>,
}
impl WitnessTrees {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let _serialized_version = reader.read_u8()?;
let witness_tree_sapling = read_shardtree(&mut reader)?;
let witness_tree_orchard = read_shardtree(reader)?;
Ok(Self {
witness_tree_sapling,
witness_tree_orchard,
})
}
}
impl Default for WitnessTrees {
fn default() -> WitnessTrees {
Self {
witness_tree_sapling: shardtree::ShardTree::new(MemoryShardStore::empty(), MAX_REORG),
witness_tree_orchard: shardtree::ShardTree::new(MemoryShardStore::empty(), MAX_REORG),
}
}
}
fn read_shardtree<
H: Hashable + Clone + HashSer + Eq,
C: Ord + std::fmt::Debug + Copy + From<u32>,
R: Read,
>(
mut reader: R,
) -> io::Result<shardtree::ShardTree<MemoryShardStore<H, C>, COMMITMENT_TREE_LEVELS, MAX_SHARD_LEVEL>>
{
let shards = Vector::read(&mut reader, |r| {
let level = Level::from(r.read_u8()?);
let index = r.read_u64::<LittleEndian>()?;
let root_addr = Address::from_parts(level, index);
let shard = read_shard(r)?;
LocatedPrunableTree::from_parts(root_addr, shard).map_err(|addr| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"parent node in root has level 0 relative to root address: {:?}",
addr
),
)
})
})?;
let mut store = MemoryShardStore::empty();
for shard in shards {
store.put_shard(shard).expect("Infallible");
}
let checkpoints = Vector::read(&mut reader, |r| {
let checkpoint_id = C::from(r.read_u32::<LittleEndian>()?);
let tree_state = match r.read_u8()? {
0 => shardtree::store::TreeState::Empty,
1 => shardtree::store::TreeState::AtPosition(Position::from(
r.read_u64::<LittleEndian>()?,
)),
otherwise => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("error reading TreeState: expected boolean value, found {otherwise}"),
));
}
};
let marks_removed = Vector::read(r, |r| r.read_u64::<LittleEndian>().map(Position::from))?;
Ok((
checkpoint_id,
Checkpoint::from_parts(tree_state, marks_removed.into_iter().collect()),
))
})?;
for (checkpoint_id, checkpoint) in checkpoints {
store
.add_checkpoint(checkpoint_id, checkpoint)
.expect("Infallible");
}
store.put_cap(read_shard(reader)?).expect("Infallible");
Ok(shardtree::ShardTree::new(store, MAX_REORG))
}
pub(crate) type SapStore = MemoryShardStore<sapling_crypto::Node, BlockHeight>;
pub(crate) type OrchStore = MemoryShardStore<MerkleHashOrchard, BlockHeight>;
#[allow(dead_code)]
#[derive(Clone)]
pub struct WitnessCache<Node: Hashable> {
pub(crate) witnesses: Vec<IncrementalWitness<Node, 32>>,
pub top_height: u64,
}
impl<Node: Hashable> WitnessCache<Node> {
pub fn new(witnesses: Vec<IncrementalWitness<Node, 32>>, top_height: u64) -> Self {
Self {
witnesses,
top_height,
}
}
pub fn last(&self) -> Option<&IncrementalWitness<Node, 32>> {
self.witnesses.last()
}
}
impl ReadableWriteable for ConfirmationStatus {
const VERSION: u8 = 0;
fn read<R: Read>(mut reader: R, _input: ()) -> io::Result<Self> {
let _external_version = Self::get_version(&mut reader);
let status = reader.read_u8()?;
let height = BlockHeight::from_u32(reader.read_u32::<LittleEndian>()?);
match status {
0 => Ok(Self::Calculated(height)),
1 => Ok(Self::Transmitted(height)),
2 => Ok(Self::Mempool(height)),
3 => Ok(Self::Confirmed(height)),
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
"Bad confirmation status",
)),
}
}
fn write<W: Write>(&self, mut _writer: W, _input: ()) -> io::Result<()> {
unimplemented!()
}
}
#[allow(clippy::enum_variant_names)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoDownloadOption {
NoMemos,
WalletMemos,
AllMemos,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub struct WalletOptions {
pub(crate) download_memos: MemoDownloadOption,
pub transaction_size_filter: Option<u32>,
}
pub const MAX_TRANSACTION_SIZE_DEFAULT: u32 = 500;
impl Default for WalletOptions {
fn default() -> Self {
WalletOptions {
download_memos: MemoDownloadOption::WalletMemos,
transaction_size_filter: Some(MAX_TRANSACTION_SIZE_DEFAULT),
}
}
}
impl WalletOptions {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let external_version = reader.read_u64::<LittleEndian>()?;
let download_memos = match reader.read_u8()? {
0 => MemoDownloadOption::NoMemos,
1 => MemoDownloadOption::WalletMemos,
2 => MemoDownloadOption::AllMemos,
v => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Bad download option {}", v),
));
}
};
let transaction_size_filter = if external_version > 1 {
Optional::read(reader, |mut r| r.read_u32::<LittleEndian>())?
} else {
Some(500)
};
Ok(Self {
download_memos,
transaction_size_filter,
})
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct WalletZecPriceInfo {
pub zec_price: Option<(u64, f64)>,
pub currency: String,
pub last_historical_prices_fetched_at: Option<u64>,
pub historical_prices_retry_count: u64,
}
impl Default for WalletZecPriceInfo {
fn default() -> Self {
Self {
zec_price: None,
currency: "USD".to_string(), last_historical_prices_fetched_at: None,
historical_prices_retry_count: 0,
}
}
}
impl WalletZecPriceInfo {
pub fn serialized_version() -> u64 {
20
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let version = reader.read_u64::<LittleEndian>()?;
if version > Self::serialized_version() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Can't read ZecPriceInfo because of incorrect version",
));
}
let zec_price = None;
let currency = "USD".to_string();
let last_historical_prices_fetched_at =
Optional::read(&mut reader, |r| r.read_u64::<LittleEndian>())?;
let historical_prices_retry_count = reader.read_u64::<LittleEndian>()?;
Ok(Self {
zec_price,
currency,
last_historical_prices_fetched_at,
historical_prices_retry_count,
})
}
}