zcash_client_sqlite: Add shard & checkpoint insertion.
This commit is contained in:
parent
9f2bb94a5e
commit
ade882d01c
|
@ -549,7 +549,7 @@ pub(crate) fn fully_scanned_height(
|
||||||
|row| {
|
|row| {
|
||||||
let max_height: u32 = row.get(0)?;
|
let max_height: u32 = row.get(0)?;
|
||||||
let sapling_tree_size: Option<u64> = row.get(1)?;
|
let sapling_tree_size: Option<u64> = row.get(1)?;
|
||||||
let sapling_tree: Vec<u8> = row.get(0)?;
|
let sapling_tree: Vec<u8> = row.get(2)?;
|
||||||
Ok((
|
Ok((
|
||||||
BlockHeight::from(max_height),
|
BlockHeight::from(max_height),
|
||||||
sapling_tree_size,
|
sapling_tree_size,
|
||||||
|
|
|
@ -875,7 +875,7 @@ mod tests {
|
||||||
|
|
||||||
// add a sapling sent note
|
// add a sapling sent note
|
||||||
wdb.conn.execute(
|
wdb.conn.execute(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '')",
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, x'00')",
|
||||||
[],
|
[],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -1039,7 +1039,7 @@ mod tests {
|
||||||
RecipientAddress::Transparent(*ufvk.default_address().0.transparent().unwrap())
|
RecipientAddress::Transparent(*ufvk.default_address().0.transparent().unwrap())
|
||||||
.encode(&tests::network());
|
.encode(&tests::network());
|
||||||
wdb.conn.execute(
|
wdb.conn.execute(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '')",
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, x'00')",
|
||||||
[],
|
[],
|
||||||
)?;
|
)?;
|
||||||
wdb.conn.execute(
|
wdb.conn.execute(
|
||||||
|
|
|
@ -327,7 +327,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
db_data.conn.execute_batch(
|
db_data.conn.execute_batch(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '');
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, x'00');
|
||||||
INSERT INTO transactions (block, id_tx, txid) VALUES (0, 0, '');
|
INSERT INTO transactions (block, id_tx, txid) VALUES (0, 0, '');
|
||||||
|
|
||||||
INSERT INTO sent_notes (tx, output_pool, output_index, from_account, address, value)
|
INSERT INTO sent_notes (tx, output_pool, output_index, from_account, address, value)
|
||||||
|
@ -460,7 +460,7 @@ mod tests {
|
||||||
db_data
|
db_data
|
||||||
.conn
|
.conn
|
||||||
.execute_batch(
|
.execute_batch(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '');",
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, x'00');",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db_data.conn.execute(
|
db_data.conn.execute(
|
||||||
|
|
|
@ -262,7 +262,7 @@ mod tests {
|
||||||
|
|
||||||
// Tx 0 contains two received notes of 2 and 5 zatoshis that are controlled by account 0.
|
// Tx 0 contains two received notes of 2 and 5 zatoshis that are controlled by account 0.
|
||||||
db_data.conn.execute_batch(
|
db_data.conn.execute_batch(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '');
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, x'00');
|
||||||
INSERT INTO transactions (block, id_tx, txid) VALUES (0, 0, 'tx0');
|
INSERT INTO transactions (block, id_tx, txid) VALUES (0, 0, 'tx0');
|
||||||
|
|
||||||
INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change)
|
INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change)
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl RusqliteMigration for Migration {
|
||||||
subtree_end_height INTEGER,
|
subtree_end_height INTEGER,
|
||||||
root_hash BLOB,
|
root_hash BLOB,
|
||||||
shard_data BLOB,
|
shard_data BLOB,
|
||||||
contains_marked INTEGER NOT NULL,
|
contains_marked INTEGER,
|
||||||
CONSTRAINT root_unique UNIQUE (root_hash)
|
CONSTRAINT root_unique UNIQUE (root_hash)
|
||||||
);
|
);
|
||||||
CREATE TABLE sapling_tree_cap (
|
CREATE TABLE sapling_tree_cap (
|
||||||
|
@ -101,19 +101,24 @@ impl RusqliteMigration for Migration {
|
||||||
let mut block_rows = stmt_blocks.query([])?;
|
let mut block_rows = stmt_blocks.query([])?;
|
||||||
while let Some(row) = block_rows.next()? {
|
while let Some(row) = block_rows.next()? {
|
||||||
let block_height: u32 = row.get(0)?;
|
let block_height: u32 = row.get(0)?;
|
||||||
let row_data: Vec<u8> = row.get(1)?;
|
let sapling_tree_data: Vec<u8> = row.get(1)?;
|
||||||
|
if sapling_tree_data == vec![0x00] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let block_end_tree = read_commitment_tree::<
|
let block_end_tree = read_commitment_tree::<
|
||||||
sapling::Node,
|
sapling::Node,
|
||||||
_,
|
_,
|
||||||
{ sapling::NOTE_COMMITMENT_TREE_DEPTH },
|
{ sapling::NOTE_COMMITMENT_TREE_DEPTH },
|
||||||
>(&row_data[..])
|
>(&sapling_tree_data[..])
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
rusqlite::Error::FromSqlConversionFailure(
|
rusqlite::Error::FromSqlConversionFailure(
|
||||||
row_data.len(),
|
sapling_tree_data.len(),
|
||||||
rusqlite::types::Type::Blob,
|
rusqlite::types::Type::Blob,
|
||||||
Box::new(e),
|
Box::new(e),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
stmt_update_block_sapling_tree_size
|
stmt_update_block_sapling_tree_size
|
||||||
.execute(params![block_end_tree.size(), block_height])?;
|
.execute(params![block_end_tree.size(), block_height])?;
|
||||||
|
|
||||||
|
|
|
@ -253,7 +253,7 @@ mod tests {
|
||||||
|
|
||||||
// - Tx 0 contains two received notes of 2 and 5 zatoshis that are controlled by account 0.
|
// - Tx 0 contains two received notes of 2 and 5 zatoshis that are controlled by account 0.
|
||||||
db_data.conn.execute_batch(
|
db_data.conn.execute_batch(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '');
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, x'00');
|
||||||
INSERT INTO transactions (block, id_tx, txid) VALUES (0, 0, 'tx0');
|
INSERT INTO transactions (block, id_tx, txid) VALUES (0, 0, 'tx0');
|
||||||
|
|
||||||
INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change)
|
INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change)
|
||||||
|
@ -265,7 +265,7 @@ mod tests {
|
||||||
// of 2 zatoshis. This is representative of a historic transaction where no `sent_notes`
|
// of 2 zatoshis. This is representative of a historic transaction where no `sent_notes`
|
||||||
// entry was created for the change value.
|
// entry was created for the change value.
|
||||||
db_data.conn.execute_batch(
|
db_data.conn.execute_batch(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (1, 1, 1, '');
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (1, 1, 1, x'00');
|
||||||
INSERT INTO transactions (block, id_tx, txid) VALUES (1, 1, 'tx1');
|
INSERT INTO transactions (block, id_tx, txid) VALUES (1, 1, 'tx1');
|
||||||
UPDATE received_notes SET spent = 1 WHERE tx = 0;
|
UPDATE received_notes SET spent = 1 WHERE tx = 0;
|
||||||
INSERT INTO sent_notes (tx, output_pool, output_index, from_account, to_account, to_address, value)
|
INSERT INTO sent_notes (tx, output_pool, output_index, from_account, to_account, to_address, value)
|
||||||
|
@ -279,7 +279,7 @@ mod tests {
|
||||||
// other half to the sending account as change. Also there's a random transparent utxo,
|
// other half to the sending account as change. Also there's a random transparent utxo,
|
||||||
// received, who knows where it came from but it's for account 0.
|
// received, who knows where it came from but it's for account 0.
|
||||||
db_data.conn.execute_batch(
|
db_data.conn.execute_batch(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (2, 2, 2, '');
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (2, 2, 2, x'00');
|
||||||
INSERT INTO transactions (block, id_tx, txid) VALUES (2, 2, 'tx2');
|
INSERT INTO transactions (block, id_tx, txid) VALUES (2, 2, 'tx2');
|
||||||
UPDATE received_notes SET spent = 2 WHERE tx = 1;
|
UPDATE received_notes SET spent = 2 WHERE tx = 1;
|
||||||
INSERT INTO utxos (received_by_account, address, prevout_txid, prevout_idx, script, value_zat, height)
|
INSERT INTO utxos (received_by_account, address, prevout_txid, prevout_idx, script, value_zat, height)
|
||||||
|
@ -297,7 +297,7 @@ mod tests {
|
||||||
// - Tx 3 just receives transparent funds and does nothing else. For this to work, the
|
// - Tx 3 just receives transparent funds and does nothing else. For this to work, the
|
||||||
// transaction must be retrieved by the wallet.
|
// transaction must be retrieved by the wallet.
|
||||||
db_data.conn.execute_batch(
|
db_data.conn.execute_batch(
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (3, 3, 3, '');
|
"INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (3, 3, 3, x'00');
|
||||||
INSERT INTO transactions (block, id_tx, txid) VALUES (3, 3, 'tx3');
|
INSERT INTO transactions (block, id_tx, txid) VALUES (3, 3, 'tx3');
|
||||||
|
|
||||||
INSERT INTO utxos (received_by_account, address, prevout_txid, prevout_idx, script, value_zat, height)
|
INSERT INTO utxos (received_by_account, address, prevout_txid, prevout_idx, script, value_zat, height)
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use either::Either;
|
use either::Either;
|
||||||
|
|
||||||
use incrementalmerkletree::Address;
|
use incrementalmerkletree::Address;
|
||||||
use rusqlite::{self, named_params, OptionalExtension};
|
use rusqlite::{self, named_params, OptionalExtension};
|
||||||
use shardtree::{Checkpoint, LocatedPrunableTree, PrunableTree, ShardStore};
|
use shardtree::{Checkpoint, LocatedPrunableTree, PrunableTree, ShardStore};
|
||||||
|
|
||||||
use std::io::{self, Cursor};
|
use std::io::{self, Cursor};
|
||||||
|
|
||||||
use zcash_primitives::{consensus::BlockHeight, sapling};
|
use zcash_primitives::{consensus::BlockHeight, merkle_tree::HashSer, sapling};
|
||||||
|
|
||||||
use crate::serialization::read_shard;
|
use crate::serialization::{read_shard, write_shard_v1};
|
||||||
|
|
||||||
pub struct WalletDbSaplingShardStore<'conn, 'a> {
|
pub struct WalletDbSaplingShardStore<'conn, 'a> {
|
||||||
pub(crate) conn: &'a rusqlite::Transaction<'conn>,
|
pub(crate) conn: &'a rusqlite::Transaction<'conn>,
|
||||||
|
@ -37,8 +39,8 @@ impl<'conn, 'a: 'conn> ShardStore for WalletDbSaplingShardStore<'conn, 'a> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_shard(&mut self, _subtree: LocatedPrunableTree<Self::H>) -> Result<(), Self::Error> {
|
fn put_shard(&mut self, subtree: LocatedPrunableTree<Self::H>) -> Result<(), Self::Error> {
|
||||||
todo!()
|
put_shard(self.conn, subtree)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_shard_roots(&self) -> Result<Vec<Address>, Self::Error> {
|
fn get_shard_roots(&self) -> Result<Vec<Address>, Self::Error> {
|
||||||
|
@ -68,14 +70,14 @@ impl<'conn, 'a: 'conn> ShardStore for WalletDbSaplingShardStore<'conn, 'a> {
|
||||||
|
|
||||||
fn add_checkpoint(
|
fn add_checkpoint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_checkpoint_id: Self::CheckpointId,
|
checkpoint_id: Self::CheckpointId,
|
||||||
_checkpoint: Checkpoint,
|
checkpoint: Checkpoint,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
todo!()
|
add_checkpoint(self.conn, checkpoint_id, checkpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checkpoint_count(&self) -> Result<usize, Self::Error> {
|
fn checkpoint_count(&self) -> Result<usize, Self::Error> {
|
||||||
todo!()
|
checkpoint_count(self.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_checkpoint_at_depth(
|
fn get_checkpoint_at_depth(
|
||||||
|
@ -125,10 +127,12 @@ impl<'conn, 'a: 'conn> ShardStore for WalletDbSaplingShardStore<'conn, 'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Error = Either<io::Error, rusqlite::Error>;
|
||||||
|
|
||||||
pub(crate) fn get_shard(
|
pub(crate) fn get_shard(
|
||||||
conn: &rusqlite::Connection,
|
conn: &rusqlite::Connection,
|
||||||
shard_root: Address,
|
shard_root: Address,
|
||||||
) -> Result<Option<LocatedPrunableTree<sapling::Node>>, Either<io::Error, rusqlite::Error>> {
|
) -> Result<Option<LocatedPrunableTree<sapling::Node>>, Error> {
|
||||||
conn.query_row(
|
conn.query_row(
|
||||||
"SELECT shard_data
|
"SELECT shard_data
|
||||||
FROM sapling_tree_shards
|
FROM sapling_tree_shards
|
||||||
|
@ -144,3 +148,69 @@ pub(crate) fn get_shard(
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn put_shard(
|
||||||
|
conn: &rusqlite::Connection,
|
||||||
|
subtree: LocatedPrunableTree<sapling::Node>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let subtree_root_hash = subtree
|
||||||
|
.root()
|
||||||
|
.annotation()
|
||||||
|
.and_then(|ann| {
|
||||||
|
ann.as_ref().map(|rc| {
|
||||||
|
let mut root_hash = vec![];
|
||||||
|
rc.write(&mut root_hash)?;
|
||||||
|
Ok(root_hash)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
.map_err(Either::Left)?;
|
||||||
|
|
||||||
|
let mut subtree_data = vec![];
|
||||||
|
write_shard_v1(&mut subtree_data, subtree.root()).map_err(Either::Left)?;
|
||||||
|
|
||||||
|
conn.prepare_cached(
|
||||||
|
"INSERT INTO sapling_tree_shards (shard_index, root_hash, shard_data)
|
||||||
|
VALUES (:shard_index, :root_hash, :shard_data)
|
||||||
|
ON CONFLICT (shard_index) DO UPDATE
|
||||||
|
SET root_hash = :root_hash,
|
||||||
|
shard_data = :shard_data",
|
||||||
|
)
|
||||||
|
.and_then(|mut stmt_put_shard| {
|
||||||
|
stmt_put_shard.execute(named_params![
|
||||||
|
":shard_index": subtree.root_addr().index(),
|
||||||
|
":root_hash": subtree_root_hash,
|
||||||
|
":shard_data": subtree_data
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.map_err(Either::Right)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_checkpoint(
|
||||||
|
conn: &rusqlite::Transaction<'_>,
|
||||||
|
checkpoint_id: BlockHeight,
|
||||||
|
checkpoint: Checkpoint,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
conn.prepare_cached(
|
||||||
|
"INSERT INTO sapling_tree_checkpoints (checkpoint_id, position)
|
||||||
|
VALUES (:checkpoint_id, :position)",
|
||||||
|
)
|
||||||
|
.and_then(|mut stmt_insert_checkpoint| {
|
||||||
|
stmt_insert_checkpoint.execute(named_params![
|
||||||
|
":checkpoint_id": u32::from(checkpoint_id),
|
||||||
|
":position": checkpoint.position().map(u64::from)
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.map_err(Either::Right)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn checkpoint_count(conn: &rusqlite::Connection) -> Result<usize, Error> {
|
||||||
|
conn.query_row("SELECT COUNT(*) FROM sapling_tree_checkpoints", [], |row| {
|
||||||
|
row.get::<_, usize>(0)
|
||||||
|
})
|
||||||
|
.map_err(Either::Right)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue