Merge pull request #1298 from oxarbitrage/basic-put-block
Basic `put_block` function for the memory wallet
This commit is contained in:
commit
5d2cd6ff46
|
@ -3327,3 +3327,8 @@ dependencies = [
|
||||||
"memuse",
|
"memuse",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[patch.unused]]
|
||||||
|
name = "sapling-crypto"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "git+https://github.com/nuttycom/sapling-crypto?rev=827534329f3ff61f9212f7933ed1688fa04e987c#827534329f3ff61f9212f7933ed1688fa04e987c"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
use incrementalmerkletree::Address;
|
use incrementalmerkletree::{Address, Retention};
|
||||||
|
use sapling::NullifierDerivingKey;
|
||||||
use secrecy::{ExposeSecret, SecretVec};
|
use secrecy::{ExposeSecret, SecretVec};
|
||||||
use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree};
|
use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -10,7 +11,7 @@ use std::{
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
};
|
};
|
||||||
use zcash_keys::keys::{AddressGenerationError, DerivationError};
|
use zcash_keys::keys::{AddressGenerationError, DerivationError};
|
||||||
use zip32::{fingerprint::SeedFingerprint, DiversifierIndex};
|
use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope};
|
||||||
|
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
block::BlockHash,
|
block::BlockHash,
|
||||||
|
@ -21,16 +22,17 @@ use zcash_primitives::{
|
||||||
use zcash_protocol::{
|
use zcash_protocol::{
|
||||||
memo::{self, Memo, MemoBytes},
|
memo::{self, Memo, MemoBytes},
|
||||||
value::Zatoshis,
|
value::Zatoshis,
|
||||||
|
ShieldedProtocol::{Orchard, Sapling},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
address::UnifiedAddress,
|
address::UnifiedAddress,
|
||||||
keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey},
|
keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey},
|
||||||
wallet::{NoteId, WalletTransparentOutput, WalletTx},
|
wallet::{NoteId, WalletSpend, WalletTransparentOutput, WalletTx},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata,
|
chain::CommitmentTreeRoot, scanning::ScanRange, Account, AccountBirthday, BlockMetadata,
|
||||||
DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees,
|
DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees,
|
||||||
WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
|
WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
|
||||||
};
|
};
|
||||||
|
@ -120,6 +122,7 @@ impl MemoryWalletDb {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
AccountUnknown(u32),
|
AccountUnknown(u32),
|
||||||
|
ViewingKeyNotFound(u32),
|
||||||
MemoDecryption(memo::Error),
|
MemoDecryption(memo::Error),
|
||||||
KeyDerivation(DerivationError),
|
KeyDerivation(DerivationError),
|
||||||
AddressGeneration(AddressGenerationError),
|
AddressGeneration(AddressGenerationError),
|
||||||
|
@ -377,12 +380,148 @@ impl WalletWrite for MemoryWalletDb {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a sequence of blocks to the data store.
|
||||||
|
///
|
||||||
|
/// Assumes blocks will be here in order.
|
||||||
fn put_blocks(
|
fn put_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
_from_state: &super::chain::ChainState,
|
from_state: &super::chain::ChainState,
|
||||||
_blocks: Vec<ScannedBlock<Self::AccountId>>,
|
blocks: Vec<ScannedBlock<Self::AccountId>>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
todo!()
|
// TODO:
|
||||||
|
// - Make sure blocks are coming in order.
|
||||||
|
// - Make sure the first block in the sequence is tip + 1?
|
||||||
|
// - Add a check to make sure the blocks are not already in the data store.
|
||||||
|
for block in blocks.into_iter() {
|
||||||
|
let mut transactions = HashMap::new();
|
||||||
|
let mut memos = HashMap::new();
|
||||||
|
for transaction in block.transactions().iter() {
|
||||||
|
let txid = transaction.txid();
|
||||||
|
for account_id in self.get_account_ids()? {
|
||||||
|
let ufvk = self
|
||||||
|
.get_account(account_id)?
|
||||||
|
.ok_or(Error::AccountUnknown(account_id))?
|
||||||
|
.ufvk()
|
||||||
|
.ok_or(Error::ViewingKeyNotFound(account_id))?
|
||||||
|
.clone();
|
||||||
|
let dfvk = ufvk
|
||||||
|
.sapling()
|
||||||
|
.ok_or(Error::ViewingKeyNotFound(account_id))?;
|
||||||
|
let nk = dfvk.to_nk(Scope::External);
|
||||||
|
|
||||||
|
transaction.sapling_outputs().iter().map(|o| {
|
||||||
|
// Insert the Sapling nullifiers of the spent notes into the `sapling_spends` map.
|
||||||
|
let nullifier = o.note().nf(&nk, o.note_commitment_tree_position().into());
|
||||||
|
self.sapling_spends
|
||||||
|
.entry(nullifier)
|
||||||
|
.or_insert((txid, false));
|
||||||
|
|
||||||
|
// Insert the memo into the `memos` map.
|
||||||
|
let note_id = NoteId::new(
|
||||||
|
txid,
|
||||||
|
Sapling,
|
||||||
|
u16::try_from(o.index())
|
||||||
|
.expect("output indices are representable as u16"),
|
||||||
|
);
|
||||||
|
if let Ok(Some(memo)) = self.get_memo(note_id) {
|
||||||
|
memos.insert(note_id, memo.encode());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "orchard")]
|
||||||
|
transaction.orchard_outputs().iter().map(|o| {
|
||||||
|
// Insert the Orchard nullifiers of the spent notes into the `orchard_spends` map.
|
||||||
|
if let Some(nullifier) = o.nf() {
|
||||||
|
self.orchard_spends
|
||||||
|
.entry(*nullifier)
|
||||||
|
.or_insert((txid, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the memo into the `memos` map.
|
||||||
|
let note_id = NoteId::new(
|
||||||
|
txid,
|
||||||
|
Orchard,
|
||||||
|
u16::try_from(o.index())
|
||||||
|
.expect("output indices are representable as u16"),
|
||||||
|
);
|
||||||
|
if let Ok(Some(memo)) = self.get_memo(note_id) {
|
||||||
|
memos.insert(note_id, memo.encode());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add frontier to the sapling tree
|
||||||
|
self.sapling_tree.insert_frontier(
|
||||||
|
from_state.final_sapling_tree().clone(),
|
||||||
|
Retention::Checkpoint {
|
||||||
|
id: from_state.block_height(),
|
||||||
|
is_marked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "orchard")]
|
||||||
|
// Add frontier to the orchard tree
|
||||||
|
self.orchard_tree.insert_frontier(
|
||||||
|
from_state.final_orchard_tree().clone(),
|
||||||
|
Retention::Checkpoint {
|
||||||
|
id: from_state.block_height(),
|
||||||
|
is_marked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mark the Sapling nullifiers of the spent notes as spent in the `sapling_spends` map.
|
||||||
|
transaction.sapling_spends().iter().map(|s| {
|
||||||
|
let nullifier = s.nf();
|
||||||
|
if let Some((txid, spent)) = self.sapling_spends.get_mut(nullifier) {
|
||||||
|
*spent = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "orchard")]
|
||||||
|
// Mark the Orchard nullifiers of the spent notes as spent in the `orchard_spends` map.
|
||||||
|
transaction.orchard_spends().iter().map(|s| {
|
||||||
|
let nullifier = s.nf();
|
||||||
|
if let Some((txid, spent)) = self.orchard_spends.get_mut(nullifier) {
|
||||||
|
*spent = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.tx_idx.insert(txid, block.block_height);
|
||||||
|
transactions.insert(txid, transaction.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let memory_block = MemoryWalletBlock {
|
||||||
|
height: block.block_height,
|
||||||
|
hash: block.block_hash,
|
||||||
|
block_time: block.block_time,
|
||||||
|
transactions,
|
||||||
|
memos,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.blocks.insert(block.block_height, memory_block);
|
||||||
|
|
||||||
|
// Add the Sapling commitments to the sapling tree.
|
||||||
|
let block_commitments = block.into_commitments();
|
||||||
|
let start_position = from_state
|
||||||
|
.final_sapling_tree()
|
||||||
|
.value()
|
||||||
|
.map_or(0.into(), |t| t.position() + 1);
|
||||||
|
self.sapling_tree
|
||||||
|
.batch_insert(start_position, block_commitments.sapling.into_iter());
|
||||||
|
|
||||||
|
#[cfg(feature = "orchard")]
|
||||||
|
{
|
||||||
|
// Add the Orchard commitments to the orchard tree.
|
||||||
|
let start_position = from_state
|
||||||
|
.final_orchard_tree()
|
||||||
|
.value()
|
||||||
|
.map_or(0.into(), |t| t.position() + 1);
|
||||||
|
self.orchard_tree
|
||||||
|
.batch_insert(start_position, block_commitments.orchard.into_iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a transparent UTXO received by the wallet to the data store.
|
/// Adds a transparent UTXO received by the wallet to the data store.
|
||||||
|
|
|
@ -119,6 +119,7 @@ impl<AccountId, N> Recipient<AccountId, Option<N>> {
|
||||||
/// The shielded subset of a [`Transaction`]'s data that is relevant to a particular wallet.
|
/// The shielded subset of a [`Transaction`]'s data that is relevant to a particular wallet.
|
||||||
///
|
///
|
||||||
/// [`Transaction`]: zcash_primitives::transaction::Transaction
|
/// [`Transaction`]: zcash_primitives::transaction::Transaction
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct WalletTx<AccountId> {
|
pub struct WalletTx<AccountId> {
|
||||||
txid: TxId,
|
txid: TxId,
|
||||||
block_index: usize,
|
block_index: usize,
|
||||||
|
@ -248,6 +249,7 @@ impl transparent_fees::InputView for WalletTransparentOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A reference to a spent note belonging to the wallet within a transaction.
|
/// A reference to a spent note belonging to the wallet within a transaction.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct WalletSpend<Nf, AccountId> {
|
pub struct WalletSpend<Nf, AccountId> {
|
||||||
index: usize,
|
index: usize,
|
||||||
nf: Nf,
|
nf: Nf,
|
||||||
|
@ -287,6 +289,7 @@ pub type WalletSaplingSpend<AccountId> = WalletSpend<sapling::Nullifier, Account
|
||||||
pub type WalletOrchardSpend<AccountId> = WalletSpend<orchard::note::Nullifier, AccountId>;
|
pub type WalletOrchardSpend<AccountId> = WalletSpend<orchard::note::Nullifier, AccountId>;
|
||||||
|
|
||||||
/// An output that was successfully decrypted in the process of wallet scanning.
|
/// An output that was successfully decrypted in the process of wallet scanning.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct WalletOutput<Note, Nullifier, AccountId> {
|
pub struct WalletOutput<Note, Nullifier, AccountId> {
|
||||||
index: usize,
|
index: usize,
|
||||||
ephemeral_key: EphemeralKeyBytes,
|
ephemeral_key: EphemeralKeyBytes,
|
||||||
|
|
Loading…
Reference in New Issue