Merge pull request #869 from nuttycom/feature/pre_dag_sync-put_blocks
Replace `put_block` with `put_blocks`
This commit is contained in:
commit
c75ff850d7
|
@ -15,9 +15,9 @@ and this library adheres to Rust's notion of
|
||||||
- `NullifierQuery` for use with `WalletRead::get_sapling_nullifiers`
|
- `NullifierQuery` for use with `WalletRead::get_sapling_nullifiers`
|
||||||
- `ScannedBlock`
|
- `ScannedBlock`
|
||||||
- `ShieldedProtocol`
|
- `ShieldedProtocol`
|
||||||
- `WalletRead::{block_metadata, block_fully_scanned, suggest_scan_ranges}`
|
|
||||||
- `WalletWrite::put_block`
|
|
||||||
- `WalletCommitmentTrees`
|
- `WalletCommitmentTrees`
|
||||||
|
- `WalletRead::{block_metadata, block_fully_scanned, suggest_scan_ranges}`
|
||||||
|
- `WalletWrite::put_blocks`
|
||||||
- `chain::CommitmentTreeRoot`
|
- `chain::CommitmentTreeRoot`
|
||||||
- `testing::MockWalletDb::new`
|
- `testing::MockWalletDb::new`
|
||||||
- `wallet::input_sellection::Proposal::{min_target_height, min_anchor_height}`:
|
- `wallet::input_sellection::Proposal::{min_target_height, min_anchor_height}`:
|
||||||
|
@ -75,7 +75,7 @@ and this library adheres to Rust's notion of
|
||||||
- `WalletRead::{get_commitment_tree, get_witnesses}` have been removed
|
- `WalletRead::{get_commitment_tree, get_witnesses}` have been removed
|
||||||
without replacement. The utility of these methods is now subsumed
|
without replacement. The utility of these methods is now subsumed
|
||||||
by those available from the `WalletCommitmentTrees` trait.
|
by those available from the `WalletCommitmentTrees` trait.
|
||||||
- `WalletWrite::advance_by_block` (use `WalletWrite::put_block` instead).
|
- `WalletWrite::advance_by_block` (use `WalletWrite::put_blocks` instead).
|
||||||
- `PrunedBlock` has been replaced by `ScannedBlock`
|
- `PrunedBlock` has been replaced by `ScannedBlock`
|
||||||
- `testing::MockWalletDb`, which is available under the `test-dependencies`
|
- `testing::MockWalletDb`, which is available under the `test-dependencies`
|
||||||
feature flag, has been modified by the addition of a `sapling_tree` property.
|
feature flag, has been modified by the addition of a `sapling_tree` property.
|
||||||
|
|
|
@ -494,9 +494,11 @@ pub trait WalletWrite: WalletRead {
|
||||||
/// Updates the state of the wallet database by persisting the provided block information,
|
/// Updates the state of the wallet database by persisting the provided block information,
|
||||||
/// along with the note commitments that were detected when scanning the block for transactions
|
/// along with the note commitments that were detected when scanning the block for transactions
|
||||||
/// pertaining to this wallet.
|
/// pertaining to this wallet.
|
||||||
fn put_block(
|
///
|
||||||
|
/// `blocks` must be sequential, in order of increasing block height
|
||||||
|
fn put_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: ScannedBlock<sapling::Nullifier>,
|
block: Vec<ScannedBlock<sapling::Nullifier>>,
|
||||||
) -> Result<Vec<Self::NoteRef>, Self::Error>;
|
) -> Result<Vec<Self::NoteRef>, Self::Error>;
|
||||||
|
|
||||||
/// Caches a decrypted transaction in the persistent wallet store.
|
/// Caches a decrypted transaction in the persistent wallet store.
|
||||||
|
@ -771,9 +773,9 @@ pub mod testing {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn put_block(
|
fn put_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
_block: ScannedBlock<sapling::Nullifier>,
|
_blocks: Vec<ScannedBlock<sapling::Nullifier>>,
|
||||||
) -> Result<Vec<Self::NoteRef>, Self::Error> {
|
) -> Result<Vec<Self::NoteRef>, Self::Error> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,6 +237,7 @@ where
|
||||||
|
|
||||||
batch_runner.flush();
|
batch_runner.flush();
|
||||||
|
|
||||||
|
let mut scanned_blocks = vec![];
|
||||||
block_source.with_blocks::<_, DbT::Error>(
|
block_source.with_blocks::<_, DbT::Error>(
|
||||||
Some(from_height),
|
Some(from_height),
|
||||||
Some(limit),
|
Some(limit),
|
||||||
|
@ -265,11 +266,12 @@ where
|
||||||
}));
|
}));
|
||||||
|
|
||||||
prior_block_metadata = Some(*scanned_block.metadata());
|
prior_block_metadata = Some(*scanned_block.metadata());
|
||||||
data_db.put_block(scanned_block).map_err(Error::Wallet)?;
|
scanned_blocks.push(scanned_block);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
data_db.put_blocks(scanned_blocks).map_err(Error::Wallet)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,9 @@ pub enum SqliteClientError {
|
||||||
/// different hash. This indicates that a required rewind was not performed.
|
/// different hash. This indicates that a required rewind was not performed.
|
||||||
BlockConflict(BlockHeight),
|
BlockConflict(BlockHeight),
|
||||||
|
|
||||||
|
/// A range of blocks provided to the database as a unit was non-sequential
|
||||||
|
NonSequentialBlocks,
|
||||||
|
|
||||||
/// A requested rewind would violate invariants of the storage layer. The payload returned with
|
/// A requested rewind would violate invariants of the storage layer. The payload returned with
|
||||||
/// this error is (safe rewind height, requested height).
|
/// this error is (safe rewind height, requested height).
|
||||||
RequestedRewindInvalid(BlockHeight, BlockHeight),
|
RequestedRewindInvalid(BlockHeight, BlockHeight),
|
||||||
|
@ -118,6 +121,7 @@ impl fmt::Display for SqliteClientError {
|
||||||
SqliteClientError::Io(e) => write!(f, "{}", e),
|
SqliteClientError::Io(e) => write!(f, "{}", e),
|
||||||
SqliteClientError::InvalidMemo(e) => write!(f, "{}", e),
|
SqliteClientError::InvalidMemo(e) => write!(f, "{}", e),
|
||||||
SqliteClientError::BlockConflict(h) => write!(f, "A block hash conflict occurred at height {}; rewind required.", u32::from(*h)),
|
SqliteClientError::BlockConflict(h) => write!(f, "A block hash conflict occurred at height {}; rewind required.", u32::from(*h)),
|
||||||
|
SqliteClientError::NonSequentialBlocks => write!(f, "`put_blocks` requires that the provided block range be sequential"),
|
||||||
SqliteClientError::DiversifierIndexOutOfRange => write!(f, "The space of available diversifier indices is exhausted"),
|
SqliteClientError::DiversifierIndexOutOfRange => write!(f, "The space of available diversifier indices is exhausted"),
|
||||||
SqliteClientError::KeyDerivationError(acct_id) => write!(f, "Key derivation failed for account {:?}", acct_id),
|
SqliteClientError::KeyDerivationError(acct_id) => write!(f, "Key derivation failed for account {:?}", acct_id),
|
||||||
SqliteClientError::AccountIdDiscontinuity => write!(f, "Wallet account identifiers must be sequential."),
|
SqliteClientError::AccountIdDiscontinuity => write!(f, "Wallet account identifiers must be sequential."),
|
||||||
|
|
|
@ -394,53 +394,71 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(height = u32::from(block.height())))]
|
#[tracing::instrument(skip_all, fields(height = blocks.first().map(|b| u32::from(b.height()))))]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn put_block(
|
fn put_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: ScannedBlock<sapling::Nullifier>,
|
blocks: Vec<ScannedBlock<sapling::Nullifier>>,
|
||||||
) -> Result<Vec<Self::NoteRef>, Self::Error> {
|
) -> Result<Vec<Self::NoteRef>, Self::Error> {
|
||||||
self.transactionally(|wdb| {
|
self.transactionally(|wdb| {
|
||||||
// Insert the block into the database.
|
let start_position = blocks.first().map(|block| {
|
||||||
wallet::put_block(
|
Position::from(
|
||||||
wdb.conn.0,
|
u64::from(block.metadata().sapling_tree_size())
|
||||||
block.height(),
|
- u64::try_from(block.sapling_commitments().len()).unwrap(),
|
||||||
block.block_hash(),
|
)
|
||||||
block.block_time(),
|
});
|
||||||
block.metadata().sapling_tree_size(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut wallet_note_ids = vec![];
|
let mut wallet_note_ids = vec![];
|
||||||
for tx in block.transactions() {
|
let mut sapling_commitments = vec![];
|
||||||
let tx_row = wallet::put_tx_meta(wdb.conn.0, tx, block.height())?;
|
let mut end_height = None;
|
||||||
|
|
||||||
// Mark notes as spent and remove them from the scanning cache
|
for block in blocks.into_iter() {
|
||||||
for spend in &tx.sapling_spends {
|
if end_height.iter().any(|prev| block.height() != *prev + 1) {
|
||||||
wallet::sapling::mark_sapling_note_spent(wdb.conn.0, tx_row, spend.nf())?;
|
return Err(SqliteClientError::NonSequentialBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
for output in &tx.sapling_outputs {
|
// Insert the block into the database.
|
||||||
let received_note_id =
|
wallet::put_block(
|
||||||
wallet::sapling::put_received_note(wdb.conn.0, output, tx_row)?;
|
wdb.conn.0,
|
||||||
|
block.height(),
|
||||||
|
block.block_hash(),
|
||||||
|
block.block_time(),
|
||||||
|
block.metadata().sapling_tree_size(),
|
||||||
|
)?;
|
||||||
|
|
||||||
// Save witness for note.
|
for tx in block.transactions() {
|
||||||
wallet_note_ids.push(received_note_id);
|
let tx_row = wallet::put_tx_meta(wdb.conn.0, tx, block.height())?;
|
||||||
|
|
||||||
|
// Mark notes as spent and remove them from the scanning cache
|
||||||
|
for spend in &tx.sapling_spends {
|
||||||
|
wallet::sapling::mark_sapling_note_spent(wdb.conn.0, tx_row, spend.nf())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for output in &tx.sapling_outputs {
|
||||||
|
let received_note_id =
|
||||||
|
wallet::sapling::put_received_note(wdb.conn.0, output, tx_row)?;
|
||||||
|
|
||||||
|
// Save witness for note.
|
||||||
|
wallet_note_ids.push(received_note_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
end_height = Some(block.height());
|
||||||
|
sapling_commitments.extend(block.into_sapling_commitments().into_iter());
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_height = block.height();
|
// We will have a start position and an end height in all cases where `blocks` is
|
||||||
let sapling_tree_size = block.metadata().sapling_tree_size();
|
// non-empty.
|
||||||
let sapling_commitments_len = block.sapling_commitments().len();
|
if let Some((start_position, end_height)) = start_position.zip(end_height) {
|
||||||
let mut sapling_commitments = block.into_sapling_commitments().into_iter();
|
// Update the Sapling note commitment tree with all newly read note commitments
|
||||||
wdb.with_sapling_tree_mut::<_, _, SqliteClientError>(move |sapling_tree| {
|
let mut sapling_commitments = sapling_commitments.into_iter();
|
||||||
let start_position = Position::from(u64::from(sapling_tree_size))
|
wdb.with_sapling_tree_mut::<_, _, SqliteClientError>(move |sapling_tree| {
|
||||||
- u64::try_from(sapling_commitments_len).unwrap();
|
sapling_tree.batch_insert(start_position, &mut sapling_commitments)?;
|
||||||
sapling_tree.batch_insert(start_position, &mut sapling_commitments)?;
|
Ok(())
|
||||||
Ok(())
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// Update now-expired transactions that didn't get mined.
|
// Update now-expired transactions that didn't get mined.
|
||||||
wallet::update_expired_notes(wdb.conn.0, block_height)?;
|
wallet::update_expired_notes(wdb.conn.0, end_height)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(wallet_note_ids)
|
Ok(wallet_note_ids)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1100,7 +1100,7 @@ pub(crate) fn put_legacy_transparent_utxo<P: consensus::Parameters>(
|
||||||
/// as expired, up to the given block height.
|
/// as expired, up to the given block height.
|
||||||
pub(crate) fn update_expired_notes(
|
pub(crate) fn update_expired_notes(
|
||||||
conn: &rusqlite::Connection,
|
conn: &rusqlite::Connection,
|
||||||
height: BlockHeight,
|
expiry_height: BlockHeight,
|
||||||
) -> Result<(), SqliteClientError> {
|
) -> Result<(), SqliteClientError> {
|
||||||
let mut stmt_update_expired = conn.prepare_cached(
|
let mut stmt_update_expired = conn.prepare_cached(
|
||||||
"UPDATE sapling_received_notes SET spent = NULL WHERE EXISTS (
|
"UPDATE sapling_received_notes SET spent = NULL WHERE EXISTS (
|
||||||
|
@ -1108,7 +1108,7 @@ pub(crate) fn update_expired_notes(
|
||||||
WHERE id_tx = sapling_received_notes.spent AND block IS NULL AND expiry_height < ?
|
WHERE id_tx = sapling_received_notes.spent AND block IS NULL AND expiry_height < ?
|
||||||
)",
|
)",
|
||||||
)?;
|
)?;
|
||||||
stmt_update_expired.execute([u32::from(height)])?;
|
stmt_update_expired.execute([u32::from(expiry_height)])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue